X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Ftmpfiles%2Ftmpfiles.c;h=dfe3daa7f72fc01abb6854c01d00702359b07526;hb=d139b24a808beb0dd39d1a80023c111241c0a009;hp=15913089ba44c576c6aec8f14da4b04c1b7a8bf7;hpb=dce818b390a857a11f7dd634684500675cf79833;p=elogind.git diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 15913089b..dfe3daa7f 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -42,9 +42,11 @@ #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" /* 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 +89,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 +102,14 @@ 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", + NULL +}; + #define MAX_DEPTH 256 static bool needs_glob(ItemType t) { @@ -206,7 +218,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]; @@ -279,13 +292,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 +350,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 +443,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) @@ -562,7 +590,7 @@ static int glob_item(Item *i, int (*action)(Item *, const char *)) { } static int create_item(Item *i) { - int r; + int r, e; mode_t u; struct stat st; @@ -584,8 +612,12 @@ static int create_item(Item *i) { i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; u = umask(0); + label_context_set(i->path, S_IFREG); fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); + e = errno; + label_context_clear(); umask(u); + errno = e; if (fd < 0) { if (i->type == WRITE_FILE && errno == ENOENT) @@ -611,8 +643,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; } @@ -641,7 +678,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); @@ -696,7 +733,12 @@ static int create_item(Item *i) { case CREATE_SYMLINK: { char *x; + label_context_set(i->path, S_IFLNK); r = symlink(i->argument, i->path); + e = errno; + label_context_clear(); + errno = e; + if (r < 0 && errno != EEXIST) { log_error("symlink(%s, %s) failed: %m", i->argument, i->path); return -errno; @@ -720,10 +762,15 @@ static int create_item(Item *i) { case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: { + mode_t file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); u = umask(0); - 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); + errno = e; if (r < 0 && errno != EEXIST) { log_error("Failed to create device node %s: %m", i->path); @@ -735,7 +782,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; } @@ -796,6 +843,8 @@ static int remove_item_instance(Item *i, const char *instance) { case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: + /* FIXME: we probably should use dir_cleanup() here + * instead of rm_rf() so that 'x' is honoured. */ r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); if (r < 0 && r != -ENOENT) { log_error("rm_rf(%s): %s", instance, strerror(-r)); @@ -1065,7 +1114,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; @@ -1233,6 +1289,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 = join(*p, "/", fragment, NULL); + if (resolved_path == NULL) { + log_error("Out of memory"); + 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; @@ -1248,7 +1328,7 @@ int main(int argc, char *argv[]) { umask(0022); - label_init(); + label_init(NULL); items = hashmap_new(string_hash_func, string_compare_func); globs = hashmap_new(string_hash_func, string_compare_func); @@ -1264,22 +1344,28 @@ 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) { - r = EXIT_FAILURE; log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r)); + r = EXIT_FAILURE; goto finish; }