X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Ftmpfiles%2Ftmpfiles.c;h=323781f9737ac547aac6ff5693f7626d94f6f6ff;hb=cb7ed9dfca647198bce95f503552710eae22da37;hp=d8fcb4015544a691198f4dac5727ad815f73d7ac;hpb=a48f3d156652cc241eb67fdf34041d1b7cdb71fb;p=elogind.git diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index d8fcb4015..323781f97 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -38,13 +38,17 @@ #include #include #include +#include #include "log.h" #include "util.h" #include "mkdir.h" +#include "path-util.h" #include "strv.h" #include "label.h" #include "set.h" +#include "conf-files.h" +#include "capability.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 @@ -87,6 +91,8 @@ typedef struct Item { bool gid_set:1; bool mode_set:1; bool age_set:1; + + bool keep_first_level:1; } Item; static Hashmap *items = NULL, *globs = NULL; @@ -98,6 +104,17 @@ static bool arg_remove = false; static const char *arg_prefix = NULL; +static const char * const conf_file_dirs[] = { + "/etc/tmpfiles.d", + "/run/tmpfiles.d", + "/usr/local/lib/tmpfiles.d", + "/usr/lib/tmpfiles.d", +#ifdef HAVE_SPLIT_USR + "/lib/tmpfiles.d", +#endif + NULL +}; + #define MAX_DEPTH 256 static bool needs_glob(ItemType t) { @@ -206,7 +223,8 @@ static int dir_cleanup( usec_t cutoff, dev_t rootdev, bool mountpoint, - int maxdepth) + int maxdepth, + bool keep_this_level) { struct dirent *dent; struct timespec times[2]; @@ -244,8 +262,7 @@ static int dir_cleanup( sub_path = NULL; if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) { - log_error("Out of memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -279,13 +296,23 @@ static int dir_cleanup( continue; } - q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1); + q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); closedir(sub_dir); if (q < 0) r = q; } + /* Note: if you are wondering why we don't + * support the sticky bit for excluding + * directories from cleaning like we do it for + * other file system objects: well, the sticky + * bit already has a meaning for directories, + * so we don't want to overload that. */ + + if (keep_this_level) + continue; + /* Ignore ctime, we change it when deleting */ age = MAX(timespec_load(&s.st_mtim), timespec_load(&s.st_atim)); @@ -327,6 +354,11 @@ static int dir_cleanup( if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) continue; + /* Keep files on this level around if this is + * requested */ + if (keep_this_level) + continue; + age = MAX3(timespec_load(&s.st_mtim), timespec_load(&s.st_atim), timespec_load(&s.st_ctim)); @@ -415,7 +447,7 @@ static int clean_item(Item *i) { mountpoint = s.st_dev != ps.st_dev || (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino); - r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH); + r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH, i->keep_first_level); finish: if (d) @@ -441,7 +473,7 @@ static int item_set_perms(Item *i, const char *path) { return -errno; } - return label_fix(path, false); + return label_fix(path, false, false); } static int recursive_relabel_children(Item *i, const char *path) { @@ -615,8 +647,13 @@ static int create_item(Item *i) { iovec[1].iov_len = 1; n = writev(fd, iovec, 2); - if (n < 0 || (size_t) n != l+1) { - log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short"); + + /* It's OK if we don't write the trailing + * newline, hence we check for l, instead of + * l+1 here. Files in /sys often refuse + * writing of the trailing newline. */ + if (n < 0 || (size_t) n < l) { + log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short write"); close_nointr_nofail(fd); return n < 0 ? n : -EIO; } @@ -645,7 +682,7 @@ static int create_item(Item *i) { case CREATE_DIRECTORY: u = umask(0); - mkdir_parents(i->path, 0755); + mkdir_parents_label(i->path, 0755); r = mkdir(i->path, i->mode); umask(u); @@ -729,10 +766,23 @@ static int create_item(Item *i) { case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: { + mode_t file_type; + + if (have_effective_cap(CAP_MKNOD) == 0) { + /* In a container we lack CAP_MKNOD. We + shouldnt attempt to create the device node in + that case to avoid noise, and we don't support + virtualized devices in containers anyway. */ + + log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); + return 0; + } + + file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); u = umask(0); - label_context_set(i->path, CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); - r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor); + label_context_set(i->path, file_type); + r = mknod(i->path, i->mode | file_type, i->major_minor); e = errno; label_context_clear(); umask(u); @@ -748,7 +798,7 @@ static int create_item(Item *i) { return -errno; } - if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) { + if ((st.st_mode & S_IFMT) != file_type) { log_error("%s is not a device node.", i->path); return -EEXIST; } @@ -809,7 +859,9 @@ static int remove_item_instance(Item *i, const char *instance) { case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); + /* FIXME: we probably should use dir_cleanup() here + * instead of rm_rf() so that 'x' is honoured. */ + r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); if (r < 0 && r != -ENOENT) { log_error("rm_rf(%s): %s", instance, strerror(-r)); return r; @@ -930,10 +982,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { assert(buffer); i = new0(Item, 1); - if (!i) { - log_error("Out of memory"); - return -ENOMEM; - } + if (!i) + return log_oom(); if (sscanf(buffer, "%c " @@ -959,10 +1009,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { n += strspn(buffer+n, WHITESPACE); if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) { i->argument = unquote(buffer+n, "\""); - if (!i->argument) { - log_error("Out of memory"); - return -ENOMEM; - } + if (!i->argument) + return log_oom(); } } @@ -1040,7 +1088,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { if (user && !streq(user, "-")) { const char *u = user; - r = get_user_creds(&u, &i->uid, NULL, NULL); + r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); if (r < 0) { log_error("[%s:%u] Unknown user '%s'.", fname, line, user); goto finish; @@ -1078,7 +1126,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i->type == TRUNCATE_DIRECTORY ? 0755 : 0644; if (age && !streq(age, "-")) { - if (parse_usec(age, &i->age) < 0) { + const char *a = age; + + if (*a == '~') { + i->keep_first_level = true; + a++; + } + + if (parse_usec(a, &i->age) < 0) { log_error("[%s:%u] Invalid age '%s'.", fname, line, age); r = -EBADMSG; goto finish; @@ -1246,6 +1301,30 @@ static int read_config_file(const char *fn, bool ignore_enoent) { return r; } +static char *resolve_fragment(const char *fragment, const char **search_paths) { + const char **p; + char *resolved_path; + + if (is_path(fragment)) + return strdup(fragment); + + STRV_FOREACH(p, search_paths) { + resolved_path = strjoin(*p, "/", fragment, NULL); + if (resolved_path == NULL) { + log_oom(); + return NULL; + } + + if (access(resolved_path, F_OK) == 0) + return resolved_path; + + free(resolved_path); + } + + errno = ENOENT; + return NULL; +} + int main(int argc, char *argv[]) { int r; Item *i; @@ -1267,7 +1346,7 @@ int main(int argc, char *argv[]) { globs = hashmap_new(string_hash_func, string_compare_func); if (!items || !globs) { - log_error("Out of memory"); + log_oom(); r = EXIT_FAILURE; goto finish; } @@ -1277,19 +1356,25 @@ int main(int argc, char *argv[]) { if (optind < argc) { int j; - for (j = optind; j < argc; j++) - if (read_config_file(argv[j], false) < 0) + for (j = optind; j < argc; j++) { + char *fragment; + + fragment = resolve_fragment(argv[j], (const char**) conf_file_dirs); + if (!fragment) { + log_error("Failed to find a %s file: %m", argv[j]); + r = EXIT_FAILURE; + goto finish; + } + if (read_config_file(fragment, false) < 0) r = EXIT_FAILURE; + free(fragment); + } } else { char **files, **f; - r = conf_files_list(&files, ".conf", - "/etc/tmpfiles.d", - "/run/tmpfiles.d", - "/usr/local/lib/tmpfiles.d", - "/usr/lib/tmpfiles.d", - NULL); + r = conf_files_list_strv(&files, ".conf", + (const char **) conf_file_dirs); if (r < 0) { log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r)); r = EXIT_FAILURE;