X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Ftmpfiles%2Ftmpfiles.c;h=f9830c431d2b905aa46fae305640930a5a93fd55;hp=89f6c6bd15696037e3004d2f2a6a886a93b45002;hb=e7eb49db071f9aab2a9bad0660962f2aa4d0c7d1;hpb=e73a03e059830a3df8fac811f923704311e93731 diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 89f6c6bd1..f9830c431 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -99,20 +99,22 @@ typedef struct Item { bool gid_set:1; bool mode_set:1; bool age_set:1; + bool mask_perms:1; bool keep_first_level:1; -} Item; -static Hashmap *items = NULL, *globs = NULL; -static Set *unix_sockets = NULL; + bool force:1; + + bool done:1; +} Item; static bool arg_create = false; static bool arg_clean = false; static bool arg_remove = false; static bool arg_boot = false; -static char **include_prefixes = NULL; -static char **exclude_prefixes = NULL; +static char **arg_include_prefixes = NULL; +static char **arg_exclude_prefixes = NULL; static char *arg_root = NULL; static const char conf_file_dirs[] = @@ -127,6 +129,9 @@ static const char conf_file_dirs[] = #define MAX_DEPTH 256 +static Hashmap *items = NULL, *globs = NULL; +static Set *unix_sockets = NULL; + static bool needs_glob(ItemType t) { return IN_SET(t, WRITE_FILE, @@ -160,7 +165,7 @@ static void load_unix_sockets(void) { /* We maintain a cache of the sockets we found in * /proc/net/unix to speed things up a little. */ - unix_sockets = set_new(string_hash_func, string_compare_func); + unix_sockets = set_new(&string_hash_ops); if (!unix_sockets) return; @@ -447,52 +452,65 @@ finish: return r; } -static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) { +static int item_set_perms(Item *i, const char *path) { + struct stat st; + bool st_valid; + assert(i); assert(path); + st_valid = stat(path, &st) == 0; + /* not using i->path directly because it may be a glob */ - if (i->mode_set) - if (chmod(path, i->mode) < 0) { - if (errno != ENOENT || !ignore_enoent) { + if (i->mode_set) { + mode_t m = i->mode; + + if (i->mask_perms && st_valid) { + if (!(st.st_mode & 0111)) + m &= ~0111; + if (!(st.st_mode & 0222)) + m &= ~0222; + if (!(st.st_mode & 0444)) + m &= ~0444; + if (!S_ISDIR(st.st_mode)) + m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ + } + + if (!st_valid || m != (st.st_mode & 07777)) { + if (chmod(path, m) < 0) { log_error("chmod(%s) failed: %m", path); return -errno; } } + } - if (i->uid_set || i->gid_set) + if ((!st_valid || (i->uid != st.st_uid || i->gid != st.st_gid)) && + (i->uid_set || i->gid_set)) if (chown(path, i->uid_set ? i->uid : (uid_t) -1, i->gid_set ? i->gid : (gid_t) -1) < 0) { - if (errno != ENOENT || !ignore_enoent) { - log_error("chown(%s) failed: %m", path); - return -errno; - } + log_error("chown(%s) failed: %m", path); + return -errno; } - return label_fix(path, ignore_enoent, false); -} - -static int item_set_perms(Item *i, const char *path) { - return item_set_perms_full(i, path, false); + return label_fix(path, false, false); } static int write_one_file(Item *i, const char *path) { - int flags; - int fd = -1; + _cleanup_close_ int fd = -1; + int flags, r = 0; struct stat st; - int r = 0; assert(i); assert(path); - flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND : - i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; + flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : + i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; - RUN_WITH_UMASK(0) { + RUN_WITH_UMASK(0000) { label_context_set(path, S_IFREG); - fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); + fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); label_context_clear(); } @@ -510,22 +528,19 @@ static int write_one_file(Item *i, const char *path) { size_t l; unescaped = cunescape(i->argument); - if (unescaped == NULL) { - safe_close(fd); + if (!unescaped) return log_oom(); - } l = strlen(unescaped); n = write(fd, unescaped, l); if (n < 0 || (size_t) n < l) { log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write"); - safe_close(fd); return n < 0 ? n : -EIO; } } - safe_close(fd); + fd = safe_close(fd); if (stat(path, &st) < 0) { log_error("stat(%s) failed: %m", path); @@ -656,10 +671,29 @@ static int create_item(Item *i) { break; case COPY_FILES: - r = copy_tree(i->argument, i->path); + r = copy_tree(i->argument, i->path, false); if (r < 0) { - log_error("Failed to copy files: %s", strerror(-r)); - return r; + struct stat a, b; + + if (r != -EEXIST) { + log_error("Failed to copy files to %s: %s", i->path, strerror(-r)); + return -r; + } + + if (stat(i->argument, &a) < 0) { + log_error("stat(%s) failed: %m", i->argument); + return -errno; + } + + if (stat(i->path, &b) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } + + if ((a.st_mode ^ b.st_mode) & S_IFMT) { + log_debug("Can't copy to %s, file exists already and is of different type", i->path); + return 0; + } } r = item_set_perms(i, i->path); @@ -680,22 +714,24 @@ static int create_item(Item *i) { RUN_WITH_UMASK(0000) { mkdir_parents_label(i->path, 0755); - r = mkdir(i->path, i->mode); + r = mkdir_label(i->path, i->mode); } - if (r < 0 && errno != EEXIST) { - log_error("Failed to create directory %s: %m", i->path); - return -errno; - } + if (r < 0) { + if (r != -EEXIST) { + log_error("Failed to create directory %s: %s", i->path, strerror(-r)); + return r; + } - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } - if (!S_ISDIR(st.st_mode)) { - log_error("%s is not a directory.", i->path); - return -EEXIST; + if (!S_ISDIR(st.st_mode)) { + log_debug("%s already exists and is not a directory.", i->path); + return 0; + } } r = item_set_perms(i, i->path); @@ -707,22 +743,41 @@ static int create_item(Item *i) { case CREATE_FIFO: RUN_WITH_UMASK(0000) { + label_context_set(i->path, S_IFIFO); r = mkfifo(i->path, i->mode); + label_context_clear(); } - if (r < 0 && errno != EEXIST) { - log_error("Failed to create fifo %s: %m", i->path); - return -errno; - } + if (r < 0) { + if (errno != EEXIST) { + log_error("Failed to create fifo %s: %m", i->path); + return -errno; + } - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } + + if (!S_ISFIFO(st.st_mode)) { + + if (i->force) { + + RUN_WITH_UMASK(0000) { + label_context_set(i->path, S_IFIFO); + r = mkfifo_atomic(i->path, i->mode); + label_context_clear(); + } - if (!S_ISFIFO(st.st_mode)) { - log_error("%s is not a fifo.", i->path); - return -EEXIST; + if (r < 0) { + log_error("Failed to create fifo %s: %s", i->path, strerror(-r)); + return r; + } + } else { + log_debug("%s is not a fifo.", i->path); + return 0; + } + } } r = item_set_perms(i, i->path); @@ -731,31 +786,40 @@ static int create_item(Item *i) { break; - case CREATE_SYMLINK: { - _cleanup_free_ char *x = NULL; + case CREATE_SYMLINK: label_context_set(i->path, S_IFLNK); r = symlink(i->argument, i->path); label_context_clear(); - if (r < 0 && errno != EEXIST) { - log_error("symlink(%s, %s) failed: %m", i->argument, i->path); - return -errno; - } - - r = readlink_malloc(i->path, &x); if (r < 0) { - log_error("readlink(%s) failed: %s", i->path, strerror(-r)); - return -errno; - } + _cleanup_free_ char *x = NULL; + + if (errno != EEXIST) { + log_error("symlink(%s, %s) failed: %m", i->argument, i->path); + return -errno; + } + + r = readlink_malloc(i->path, &x); + if (r < 0 || !streq(i->argument, x)) { - if (!streq(i->argument, x)) { - log_error("%s is not the right symlinks.", i->path); - return -EEXIST; + if (i->force) { + label_context_set(i->path, S_IFLNK); + r = symlink_atomic(i->argument, i->path); + label_context_clear(); + + if (r < 0) { + log_error("symlink(%s, %s) failed: %s", i->argument, i->path, strerror(-r)); + return r; + } + } else { + log_debug("%s is not a symlink or does not point to the correct path.", i->path); + return 0; + } + } } break; - } case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: { @@ -771,7 +835,7 @@ static int create_item(Item *i) { return 0; } - file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); + file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; RUN_WITH_UMASK(0000) { label_context_set(i->path, file_type); @@ -779,19 +843,42 @@ static int create_item(Item *i) { label_context_clear(); } - if (r < 0 && errno != EEXIST) { - log_error("Failed to create device node %s: %m", i->path); - return -errno; - } + if (r < 0) { + if (errno == EPERM) { + log_debug("We lack permissions, possibly because of cgroup configuration; " + "skipping creation of device node %s.", i->path); + return 0; + } - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (errno != EEXIST) { + log_error("Failed to create device node %s: %m", i->path); + return -errno; + } + + if (stat(i->path, &st) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } + + if ((st.st_mode & S_IFMT) != file_type) { + + if (i->force) { - if ((st.st_mode & S_IFMT) != file_type) { - log_error("%s is not a device node.", i->path); - return -EEXIST; + RUN_WITH_UMASK(0000) { + label_context_set(i->path, file_type); + r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); + label_context_clear(); + } + + if (r < 0) { + log_error("Failed to create device node %s: %s", i->path, strerror(-r)); + return r; + } + } else { + log_debug("%s is not a device node.", i->path); + return 0; + } + } } r = item_set_perms(i, i->path); @@ -977,9 +1064,23 @@ static int clean_item(Item *i) { static int process_item(Item *i) { int r, q, p; + char prefix[PATH_MAX]; assert(i); + if (i->done) + return 0; + + i->done = true; + + PATH_FOREACH_PREFIX(prefix, i->path) { + Item *j; + + j = hashmap_get(items, prefix); + if (j) + process_item(j); + } + r = arg_create ? create_item(i) : 0; q = arg_remove ? remove_item(i) : 0; p = arg_clean ? clean_item(i) : 0; @@ -994,7 +1095,9 @@ static int process_item(Item *i) { } static void item_free(Item *i) { - assert(i); + + if (!i) + return; free(i->path); free(i->argument); @@ -1048,19 +1151,17 @@ static bool item_equal(Item *a, Item *b) { static bool should_include_path(const char *path) { char **prefix; - STRV_FOREACH(prefix, exclude_prefixes) { + STRV_FOREACH(prefix, arg_exclude_prefixes) if (path_startswith(path, *prefix)) return false; - } - STRV_FOREACH(prefix, include_prefixes) { + STRV_FOREACH(prefix, arg_include_prefixes) if (path_startswith(path, *prefix)) return true; - } /* no matches, so we should include this path only if we * have no whitelist at all */ - return strv_length(include_prefixes) == 0; + return strv_length(arg_include_prefixes) == 0; } static int parse_line(const char *fname, unsigned line, const char *buffer) { @@ -1098,10 +1199,17 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -EIO; } - if (strlen(action) > 2 || (strlen(action) > 1 && action[1] != '!')) { - log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); + if (isempty(action)) { + log_error("[%s:%u] Command too short '%s'.", fname, line, action); + return -EINVAL; + } + + if (strlen(action) > 1 && !in_charset(action+1, "!+")) { + log_error("[%s:%u] Unknown modifiers in command '%s'", fname, line, action); return -EINVAL; - } else if (strlen(action) > 1 && !arg_boot) + } + + if (strchr(action+1, '!') && !arg_boot) return 0; type = action[0]; @@ -1110,6 +1218,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { if (!i) return log_oom(); + i->force = !!strchr(action+1, '+'); + r = specifier_printf(path, specifier_table, NULL, &i->path); if (r < 0) { log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); @@ -1125,7 +1235,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } } - switch(type) { + switch (type) { case CREATE_FILE: case TRUNCATE_FILE: @@ -1143,10 +1253,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case CREATE_SYMLINK: if (!i->argument) { - log_error("[%s:%u] Symlink file requires argument.", fname, line); - return -EBADMSG; + i->argument = strappend("/usr/share/factory", i->path); + if (!i->argument) + return log_oom(); } - break; case WRITE_FILE: @@ -1158,8 +1268,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case COPY_FILES: if (!i->argument) { - log_error("[%s:%u] Copy files requires argument.", fname, line); - return -EBADMSG; + i->argument = strappend("/usr/share/factory", i->path); + if (!i->argument) + return log_oom(); } if (!path_is_absolute(i->argument)) { @@ -1189,7 +1300,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } default: - log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); + log_error("[%s:%u] Unknown command type '%c'.", fname, line, type); return -EBADMSG; } @@ -1241,9 +1352,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } if (mode && !streq(mode, "-")) { + const char *mm = mode; unsigned m; - if (sscanf(mode, "%o", &m) != 1) { + if (*mm == '~') { + i->mask_perms = true; + mm++; + } + + if (sscanf(mm, "%o", &m) != 1) { log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); return -ENOENT; } @@ -1294,8 +1411,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return 0; } -static int help(void) { - +static void help(void) { printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" " -h --help Show this help\n" @@ -1308,8 +1424,6 @@ static int help(void) { " --exclude-prefix=PATH Ignore rules that apply to paths with the specified prefix\n" " --root=PATH Operate on an alternate filesystem root\n", program_invocation_short_name); - - return 0; } static int parse_argv(int argc, char *argv[]) { @@ -1343,12 +1457,13 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch (c) { case 'h': - return help(); + help(); + return 0; case ARG_VERSION: puts(PACKAGE_STRING); @@ -1372,19 +1487,21 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PREFIX: - if (strv_push(&include_prefixes, optarg) < 0) + if (strv_push(&arg_include_prefixes, optarg) < 0) return log_oom(); break; case ARG_EXCLUDE_PREFIX: - if (strv_push(&exclude_prefixes, optarg) < 0) + if (strv_push(&arg_exclude_prefixes, optarg) < 0) return log_oom(); 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); break; @@ -1394,7 +1511,6 @@ static int parse_argv(int argc, char *argv[]) { default: assert_not_reached("Unhandled option"); } - } if (!arg_clean && !arg_create && !arg_remove) { log_error("You need to specify at least one of --clean, --create or --remove."); @@ -1482,7 +1598,7 @@ int main(int argc, char *argv[]) { r = parse_argv(argc, argv); if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + goto finish; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); @@ -1492,8 +1608,8 @@ int main(int argc, char *argv[]) { label_init(NULL); - items = hashmap_new(string_hash_func, string_compare_func); - globs = hashmap_new(string_hash_func, string_compare_func); + items = hashmap_new(&string_hash_ops); + globs = hashmap_new(&string_hash_ops); if (!items || !globs) { r = log_oom(); @@ -1544,8 +1660,8 @@ finish: hashmap_free(items); hashmap_free(globs); - free(include_prefixes); - free(exclude_prefixes); + free(arg_include_prefixes); + free(arg_exclude_prefixes); free(arg_root); set_free_free(unix_sockets);