[PATCH] Implement tmpfile white listing.

William Douglas william.douglas at linux.intel.com
Tue Apr 19 15:12:19 PDT 2011


x, in addition to its old behavior, now will protect (recursively)
files from removal. D has been modified to also "protect" directories
so that r/R won't touch them.

src/util.h: Modify rm_rf signature to take a function pointer used
to assess if files are protected or not.

src/util.c: Update item deletion to check if they are on the
protected list and not delete if they are. Also change deleting
a folder failure behavior to not report failure if the folder wasn't
empty.

src/tmpfiles.c: Add new hashmap for filenames that are protected and
add paths (post glob expansion) for items that are of type IGNORE_PATH
or TRUNCATE_DIRECTORY to that hashmap.
---
 man/tmpfiles.d.xml |   15 ++++-------
 src/tmpfiles.c     |   65 +++++++++++++++++++++++++++++++++++++++++++++------
 src/util.c         |   45 +++++++++++++++++++++++------------
 src/util.h         |    2 +-
 4 files changed, 93 insertions(+), 34 deletions(-)

diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index abc74ef..aadfb7c 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -115,15 +115,12 @@ d    /run/user 0755 root root 10d</programlisting>
                                 <varlistentry>
                                         <term><varname>x</varname></term>
                                         <listitem><para>Ignore a path
-                                        during cleaning. Use this type
-                                        to exclude paths from clean-up
-                                        as controlled with the Age
-                                        parameter. Note that lines of
-                                        this type do not influence the
-                                        effect of r or R lines. Lines
-                                        of this type accept
-                                        shell-style globs in place of
-                                        of normal path
+                                        during cleaning or removal.
+                                        Use this type to exclude paths
+                                        from clean-up as controlled with
+                                        the Age parameter. Lines of this
+                                        type accept shell-style globs in
+                                        place of normal path
                                         names.</para></listitem>
                                 </varlistentry>
 
diff --git a/src/tmpfiles.c b/src/tmpfiles.c
index 1574a19..dcbdfac 100644
--- a/src/tmpfiles.c
+++ b/src/tmpfiles.c
@@ -78,7 +78,7 @@ typedef struct Item {
         bool age_set:1;
 } Item;
 
-static Hashmap *items = NULL, *globs = NULL;
+static Hashmap *items = NULL, *globs = NULL, *kept = NULL;
 static Set *unix_sockets = NULL;
 
 static bool arg_create = false;
@@ -89,6 +89,10 @@ static const char *arg_prefix = NULL;
 
 #define MAX_DEPTH 256
 
+static bool protected(const char *key) {
+        return hashmap_get(kept, key) != NULL;
+}
+
 static bool needs_glob(int t) {
         return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH;
 }
@@ -533,16 +537,18 @@ static int remove_item(Item *i, const char *instance) {
                 break;
 
         case REMOVE_PATH:
-                if (remove(instance) < 0 && errno != ENOENT) {
-                        log_error("remove(%s): %m", instance);
-                        return -errno;
+                if (!protected(instance)) {
+                        if (remove(instance) < 0 && errno != ENOENT) {
+                                log_error("remove(%s): %m", instance);
+                                return -errno;
+                        }
                 }
 
                 break;
 
         case TRUNCATE_DIRECTORY:
         case RECURSIVE_REMOVE_PATH:
-                if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 &&
+                if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, protected)) < 0 &&
                     r != -ENOENT) {
                         log_error("rm_rf(%s): %s", instance, strerror(-r));
                         return r;
@@ -654,9 +660,10 @@ static bool item_equal(Item *a, Item *b) {
 
 static int parse_line(const char *fname, unsigned line, const char *buffer) {
         Item *i, *existing;
-        char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
+        char *mode = NULL, *user = NULL, *group = NULL, *age = NULL, **keep = NULL;
         Hashmap *h;
-        int r;
+        int r, k;
+        glob_t glb;
 
         assert(fname);
         assert(line >= 1);
@@ -772,6 +779,42 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 i->age_set = true;
         }
 
+        if (i->type == IGNORE_PATH || i->type == TRUNCATE_DIRECTORY) {
+                if ((k = glob(i->path, GLOB_BRACE, NULL, &glb)) != 0) {
+                        if (k != GLOB_NOMATCH) {
+                                if (errno != 0)
+                                        errno = EIO;
+
+                                log_error("glob(%s) failed: %m", i->path);
+                                r = -errno;
+                        }
+                        r = 0;
+                        goto finish;
+                }
+
+                STRV_FOREACH(keep, glb.gl_pathv) {
+                        char *ks = NULL;
+                        if ((existing = hashmap_get(kept, *keep))) {
+                                log_warning("Two or more conflicting lines for %s configured, ignoring.", *keep);
+                                continue;
+                        }
+
+                        if (!(ks = strdup(*keep))) {
+                                log_error("strdup(%s) failed: %m", *keep);
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        if ((r = hashmap_put(kept, ks, ks)) < 0) {
+                                log_warning("Failed to insert item %s: %s", *keep, strerror(-r));
+                                globfree(&glb);
+                                free(ks);
+                                goto finish;
+                        }
+                }
+                globfree(&glb);
+        }
+
         h = needs_glob(i->type) ? globs : items;
 
         if ((existing = hashmap_get(h, i->path))) {
@@ -932,6 +975,7 @@ int main(int argc, char *argv[]) {
         int r;
         Item *i;
         Iterator iterator;
+        char *kp;
 
         if ((r = parse_argv(argc, argv)) <= 0)
                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
@@ -944,8 +988,9 @@ int main(int argc, char *argv[]) {
 
         items = hashmap_new(string_hash_func, string_compare_func);
         globs = hashmap_new(string_hash_func, string_compare_func);
+        kept = hashmap_new(string_hash_func, string_compare_func);
 
-        if (!items || !globs) {
+        if (!items || !globs || !kept) {
                 log_error("Out of memory");
                 r = EXIT_FAILURE;
                 goto finish;
@@ -994,8 +1039,12 @@ finish:
         while ((i = hashmap_steal_first(globs)))
                 item_free(i);
 
+        while ((kp = hashmap_steal_first(kept)))
+                free(kp);
+
         hashmap_free(items);
         hashmap_free(globs);
+        hashmap_free(kept);
 
         set_free_free(unix_sockets);
 
diff --git a/src/util.c b/src/util.c
index 5029896..336be47 100644
--- a/src/util.c
+++ b/src/util.c
@@ -3077,9 +3077,10 @@ int get_ctty(char **r, dev_t *_devnr) {
         return 0;
 }
 
-static int rm_rf_children(int fd, bool only_dirs) {
+static int rm_rf_children(int fd, bool only_dirs, const char *path, bool (*protected)(const char *)) {
         DIR *d;
         int ret = 0;
+        char *new_path = NULL;
 
         assert(fd >= 0);
 
@@ -3122,39 +3123,49 @@ static int rm_rf_children(int fd, bool only_dirs) {
                 } else
                         is_dir = de->d_type == DT_DIR;
 
+                free(new_path);
+                if (asprintf(&new_path, "%s/%s", path, de->d_name) < 0)
+                        return -ENOMEM;
+
                 if (is_dir) {
                         int subdir_fd;
 
+                        if (protected && protected(new_path))
+                                continue;
+
                         if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) {
+                        if ((r = rm_rf_children(subdir_fd, only_dirs, new_path, protected)) < 0) {
                                 if (ret == 0)
                                         ret = r;
                         }
 
                         if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                                if (ret == 0 && errno != ENOENT)
+                                if (ret == 0 && errno != ENOENT && errno != EEXIST && errno != ENOTEMPTY)
                                         ret = -errno;
                         }
                 } else  if (!only_dirs) {
 
-                        if (unlinkat(fd, de->d_name, 0) < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
+                        if (!(protected && protected(new_path))) {
+                                if (unlinkat(fd, de->d_name, 0) < 0) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                }
                         }
                 }
         }
 
+        free(new_path);
         closedir(d);
 
         return ret;
 }
 
-int rm_rf(const char *path, bool only_dirs, bool delete_root) {
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool (*protected)(const char *)) {
         int fd;
         int r;
 
@@ -3165,20 +3176,22 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root) {
                 if (errno != ENOTDIR)
                         return -errno;
 
-                if (delete_root && !only_dirs)
-                        if (unlink(path) < 0)
-                                return -errno;
+                if (!(protected && protected(path)))
+                        if (delete_root && !only_dirs)
+                                if (unlink(path) < 0)
+                                        return -errno;
 
                 return 0;
         }
 
-        r = rm_rf_children(fd, only_dirs);
+        r = rm_rf_children(fd, only_dirs, path, protected);
 
-        if (delete_root)
-                if (rmdir(path) < 0) {
-                        if (r == 0)
-                                r = -errno;
-                }
+        if (!(protected && protected(path)))
+                if (delete_root)
+                        if (rmdir(path) < 0) {
+                                if (r == 0 && errno != EEXIST && errno != ENOTEMPTY)
+                                        r = -errno;
+                        }
 
         return r;
 }
diff --git a/src/util.h b/src/util.h
index 7fa488b..0d0f2c9 100644
--- a/src/util.h
+++ b/src/util.h
@@ -350,7 +350,7 @@ int get_ctty(char **r, dev_t *_devnr);
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 
-int rm_rf(const char *path, bool only_dirs, bool delete_root);
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool (*protected)(const char *));
 
 cpu_set_t* cpu_set_malloc(unsigned *ncpus);
 
-- 
1.7.2.3



More information about the systemd-devel mailing list