diff options
Diffstat (limited to 'src/tmpfiles/tmpfiles.c')
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 266 |
1 files changed, 151 insertions, 115 deletions
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 42f757c4b7..954f4aa985 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -20,43 +18,59 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <fcntl.h> +#include <dirent.h> #include <errno.h> -#include <string.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <getopt.h> +#include <glob.h> #include <limits.h> -#include <dirent.h> +#include <linux/fs.h> +#include <stdbool.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> -#include <stddef.h> -#include <getopt.h> -#include <stdbool.h> -#include <time.h> -#include <glob.h> -#include <fnmatch.h> +#include <string.h> #include <sys/stat.h> #include <sys/xattr.h> -#include <linux/fs.h> +#include <time.h> +#include <unistd.h> +#include "acl-util.h" +#include "alloc-util.h" +#include "btrfs-util.h" +#include "capability-util.h" +#include "chattr-util.h" +#include "conf-files.h" +#include "copy.h" +#include "def.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "formats-util.h" +#include "fs-util.h" +#include "glob-util.h" +#include "io-util.h" +#include "label.h" #include "log.h" -#include "util.h" #include "macro.h" #include "missing.h" #include "mkdir.h" +#include "mount-util.h" +#include "parse-util.h" #include "path-util.h" -#include "strv.h" -#include "label.h" -#include "set.h" -#include "conf-files.h" -#include "capability.h" -#include "specifier.h" -#include "build.h" -#include "copy.h" #include "rm-rf.h" #include "selinux-util.h" -#include "btrfs-util.h" -#include "acl-util.h" -#include "formats-util.h" +#include "set.h" +#include "specifier.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "umask-util.h" +#include "user-util.h" +#include "util.h" /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates * them in the file system. This is intended to be used to create @@ -70,6 +84,8 @@ typedef enum ItemType { CREATE_DIRECTORY = 'd', TRUNCATE_DIRECTORY = 'D', CREATE_SUBVOLUME = 'v', + CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', + CREATE_SUBVOLUME_NEW_QUOTA = 'Q', CREATE_FIFO = 'p', CREATE_SYMLINK = 'L', CREATE_CHAR_DEVICE = 'c', @@ -78,6 +94,7 @@ typedef enum ItemType { /* These ones take globs */ WRITE_FILE = 'w', + EMPTY_DIRECTORY = 'e', SET_XATTR = 't', RECURSIVE_SET_XATTR = 'T', SET_ACL = 'a', @@ -141,7 +158,7 @@ static char **arg_include_prefixes = NULL; static char **arg_exclude_prefixes = NULL; static char *arg_root = NULL; -static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles"); +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d"); #define MAX_DEPTH 256 @@ -163,6 +180,7 @@ static bool needs_glob(ItemType t) { IGNORE_DIRECTORY_PATH, REMOVE_PATH, RECURSIVE_REMOVE_PATH, + EMPTY_DIRECTORY, ADJUST_MODE, RELABEL_PATH, RECURSIVE_RELABEL_PATH, @@ -179,8 +197,11 @@ static bool takes_ownership(ItemType t) { CREATE_FILE, TRUNCATE_FILE, CREATE_DIRECTORY, + EMPTY_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, + CREATE_SUBVOLUME_INHERIT_QUOTA, + CREATE_SUBVOLUME_NEW_QUOTA, CREATE_FIFO, CREATE_SYMLINK, CREATE_CHAR_DEVICE, @@ -595,7 +616,7 @@ static int path_set_perms(Item *i, const char *path) { * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via * O_PATH. */ - fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME); + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); @@ -616,7 +637,7 @@ static int path_set_perms(Item *i, const char *path) { if (!(st.st_mode & 0111)) m &= ~0111; if (!(st.st_mode & 0222)) - m &= ~0222; + m &= ~0222; if (!(st.st_mode & 0444)) m &= ~0444; if (!S_ISDIR(st.st_mode)) @@ -641,7 +662,7 @@ static int path_set_perms(Item *i, const char *path) { if (chown(fn, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID) < 0) - return log_error_errno(errno, "chown(%s) failed: %m", path); + return log_error_errno(errno, "chown(%s) failed: %m", path); } } @@ -662,7 +683,7 @@ static int parse_xattrs_from_arg(Item *i) { for (;;) { _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL; - r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE); + r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); if (r < 0) log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p); if (r <= 0) @@ -786,7 +807,7 @@ static int path_set_acls(Item *item, const char *path) { assert(item); assert(path); - fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME); + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); @@ -845,7 +866,7 @@ static int parse_attribute_from_arg(Item *item) { { 'a', FS_APPEND_FL }, /* writes to file may only append */ { 'c', FS_COMPR_FL }, /* Compress file */ { 'd', FS_NODUMP_FL }, /* do not dump file */ - { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/ + { 'e', FS_EXTENT_FL }, /* Extents */ { 'i', FS_IMMUTABLE_FL }, /* Immutable file */ { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */ { 's', FS_SECRM_FL }, /* Secure deletion */ @@ -899,10 +920,7 @@ static int parse_attribute_from_arg(Item *item) { v = attributes[i].value; - if (mode == MODE_ADD || mode == MODE_SET) - value |= v; - else - value &= ~v; + SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); mask |= v; } @@ -955,9 +973,10 @@ static int path_set_attribute(Item *item, const char *path) { r = chattr_fd(fd, f, item->attribute_mask); if (r < 0) - return log_error_errno(r, - "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", - path, item->attribute_value, item->attribute_mask); + log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, + r, + "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", + path, item->attribute_value, item->attribute_mask); return 0; } @@ -1054,7 +1073,7 @@ static int item_do_children(Item *i, const char *path, action_t action) { errno = 0; de = readdir(d); if (!de) { - if (errno != 0 && r == 0) + if (errno > 0 && r == 0) r = -errno; break; @@ -1132,6 +1151,7 @@ static int create_item(Item *i) { _cleanup_free_ char *resolved = NULL; struct stat st; int r = 0; + int q = 0; CreationMode creation; assert(i); @@ -1198,16 +1218,35 @@ static int create_item(Item *i) { case CREATE_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_SUBVOLUME: - + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: RUN_WITH_UMASK(0000) mkdir_parents_label(i->path, 0755); - if (i->type == CREATE_SUBVOLUME) - RUN_WITH_UMASK((~i->mode) & 0777) { - r = btrfs_subvol_make(i->path); - log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path); + if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { + + if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) + + /* Don't create a subvolume unless the + * root directory is one, too. We do + * this under the assumption that if + * the root directory is just a plain + * directory (i.e. very light-weight), + * we shouldn't try to split it up + * into subvolumes (i.e. more + * heavy-weight). Thus, chroot() + * environments and suchlike will get + * a full brtfs subvolume set up below + * their tree only if they + * specifically set up a btrfs + * subvolume for the root dir too. */ + + r = -ENOTTY; + else { + RUN_WITH_UMASK((~i->mode) & 0777) + r = btrfs_subvol_make(i->path); } - else + } else r = 0; if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) @@ -1236,14 +1275,34 @@ static int create_item(Item *i) { log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { + r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); + if (r == -ENOTTY) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); + else if (r == -EROFS) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); + else if (r == -ENOPROTOOPT) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); + else if (r < 0) + q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); + else if (r > 0) + log_debug("Adjusted quota for subvolume \"%s\".", i->path); + else if (r == 0) + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); + } + + /* fall through */ + + case EMPTY_DIRECTORY: r = path_set_perms(i, i->path); + if (q < 0) + return q; if (r < 0) return r; break; case CREATE_FIFO: - RUN_WITH_UMASK(0000) { mac_selinux_create_file_prepare(i->path, S_IFIFO); r = mkfifo(i->path, i->mode); @@ -1480,45 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) { } static int remove_item(Item *i) { - int r = 0; - assert(i); log_debug("Running remove action for entry %c %s", (char) i->type, i->path); switch (i->type) { - case CREATE_FILE: - case TRUNCATE_FILE: - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_FIFO: - case CREATE_SYMLINK: - case CREATE_CHAR_DEVICE: - case CREATE_BLOCK_DEVICE: - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case ADJUST_MODE: - case RELABEL_PATH: - case RECURSIVE_RELABEL_PATH: - case WRITE_FILE: - case COPY_FILES: - case SET_XATTR: - case RECURSIVE_SET_XATTR: - case SET_ACL: - case RECURSIVE_SET_ACL: - case SET_ATTRIBUTE: - case RECURSIVE_SET_ATTRIBUTE: - break; - case REMOVE_PATH: case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - r = glob_item(i, remove_item_instance, false); - break; - } + return glob_item(i, remove_item_instance, false); - return r; + default: + return 0; + } } static int clean_item_instance(Item *i, const char* instance) { @@ -1541,13 +1575,12 @@ static int clean_item_instance(Item *i, const char* instance) { d = opendir_nomod(instance); if (!d) { - if (errno == ENOENT || errno == ENOTDIR) { + if (IN_SET(errno, ENOENT, ENOTDIR)) { log_debug_errno(errno, "Directory \"%s\": %m", instance); return 0; } - log_error_errno(errno, "Failed to open directory %s: %m", instance); - return -errno; + return log_error_errno(errno, "Failed to open directory %s: %m", instance); } if (fstat(dirfd(d), &s) < 0) @@ -1561,8 +1594,7 @@ static int clean_item_instance(Item *i, const char* instance) { if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); - mountpoint = s.st_dev != ps.st_dev || - (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino); + mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino; log_debug("Cleanup threshold for %s \"%s\" is %s", mountpoint ? "mount point" : "directory", @@ -1574,8 +1606,6 @@ static int clean_item_instance(Item *i, const char* instance) { } static int clean_item(Item *i) { - int r = 0; - assert(i); log_debug("Running clean action for entry %c %s", (char) i->type, i->path); @@ -1583,19 +1613,19 @@ static int clean_item(Item *i) { switch (i->type) { case CREATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case IGNORE_PATH: case COPY_FILES: clean_item_instance(i, i->path); - break; + return 0; case IGNORE_DIRECTORY_PATH: - r = glob_item(i, clean_item_instance, false); - break; + return glob_item(i, clean_item_instance, false); default: - break; + return 0; } - - return r; } static int process_item_array(ItemArray *array); @@ -1759,9 +1789,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { assert(line >= 1); assert(buffer); - r = unquote_many_words( + r = extract_many_words( &buffer, - 0, + NULL, + EXTRACT_QUOTES, &action, &path, &mode, @@ -1818,6 +1849,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case CREATE_DIRECTORY: case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: @@ -1982,8 +2016,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i.mode = m; i.mode_set = true; } else - i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY) - ? 0755 : 0644; + i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; if (!isempty(age) && !streq(age, "-")) { const char *a = age; @@ -2074,7 +2107,7 @@ static int parse_argv(int argc, char *argv[]) { {} }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -2088,9 +2121,7 @@ static int parse_argv(int argc, char *argv[]) { return 0; case ARG_VERSION: - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - return 0; + return version(); case ARG_CREATE: arg_create = true; @@ -2119,12 +2150,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ROOT: - free(arg_root); - arg_root = path_make_absolute_cwd(optarg); - if (!arg_root) - return log_oom(); - - path_kill_slashes(arg_root); + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; break; case '?': @@ -2143,25 +2171,33 @@ static int parse_argv(int argc, char *argv[]) { } static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; + _cleanup_fclose_ FILE *_f = NULL; + FILE *f; char line[LINE_MAX]; Iterator iterator; unsigned v = 0; Item *i; - int r; + int r = 0; assert(fn); - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) { - log_debug_errno(r, "Failed to open \"%s\": %m", fn); - return 0; - } + if (streq(fn, "-")) { + log_debug("Reading config from stdin."); + fn = "<stdin>"; + f = stdin; + } else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; + } - return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); + return log_error_errno(r, "Failed to open '%s': %m", fn); + } + log_debug("Reading config file \"%s\".", fn); + f = _f; } - log_debug("Reading config file \"%s\".", fn); FOREACH_LINE(line, f, break) { char *l; @@ -2187,7 +2223,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { continue; ORDERED_HASHMAP_FOREACH(j, items, iter) { - if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME) + if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) continue; if (path_equal(j->path, i->path)) { @@ -2230,7 +2266,7 @@ int main(int argc, char *argv[]) { umask(0022); - mac_selinux_init(NULL); + mac_selinux_init(); items = ordered_hashmap_new(&string_hash_ops); globs = ordered_hashmap_new(&string_hash_ops); |