chiark / gitweb /
util: make sure to fstatat() at most once in rm_rf_children()
authorLennart Poettering <lennart@poettering.net>
Tue, 22 May 2012 14:14:34 +0000 (16:14 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 22 May 2012 14:14:34 +0000 (16:14 +0200)
src/shared/util.c

index 4b841491aa2a73e4d9ea79bca62d31ce0bc44bae..7d98dc6e4fb34ff85d8ed1e12a7a38c1448d930e 100644 (file)
@@ -3146,7 +3146,7 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
         assert(fd >= 0);
 
         /* This returns the first error we run into, but nevertheless
         assert(fd >= 0);
 
         /* This returns the first error we run into, but nevertheless
-         * tries to go on */
+         * tries to go on. This closes the passed fd. */
 
         d = fdopendir(fd);
         if (!d) {
 
         d = fdopendir(fd);
         if (!d) {
@@ -3157,7 +3157,8 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
 
         for (;;) {
                 struct dirent buf, *de;
 
         for (;;) {
                 struct dirent buf, *de;
-                bool is_dir, keep_around = false;
+                bool is_dir, keep_around;
+                struct stat st;
                 int r;
 
                 r = readdir_r(d, &buf, &de);
                 int r;
 
                 r = readdir_r(d, &buf, &de);
@@ -3172,73 +3173,50 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
-                if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
-
+                if (de->d_type == DT_UNKNOWN ||
+                    honour_sticky ||
+                    (de->d_type == DT_DIR && root_dev)) {
                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
                         if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        if (honour_sticky)
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-
                         is_dir = S_ISDIR(st.st_mode);
                         is_dir = S_ISDIR(st.st_mode);
-
+                        keep_around =
+                                honour_sticky &&
+                                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                (st.st_mode & S_ISVTX);
                 } else {
                 } else {
-                        if (honour_sticky) {
-                                struct stat st;
-
-                                if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                        if (ret == 0 && errno != ENOENT)
-                                                ret = -errno;
-                                        continue;
-                                }
-
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-                        }
-
                         is_dir = de->d_type == DT_DIR;
                         is_dir = de->d_type == DT_DIR;
+                        keep_around = false;
                 }
 
                 if (is_dir) {
                         int subdir_fd;
                 }
 
                 if (is_dir) {
                         int subdir_fd;
-                        struct stat sb;
-                        if (root_dev) {
-                                if (fstatat(fd, de->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
-                                        if (ret == 0 && errno != ENOENT)
-                                                ret = -errno;
-                                        continue;
-                                }
-                        }
 
                         /* if root_dev is set, remove subdirectories only, if device is same as dir */
 
                         /* if root_dev is set, remove subdirectories only, if device is same as dir */
-                        if ((root_dev == NULL) || (sb.st_dev == root_dev->st_dev)) {
+                        if (root_dev && st.st_dev != root_dev->st_dev)
+                                continue;
 
 
-                                subdir_fd = openat(fd, de->d_name,
-                                                   O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
-                                if (subdir_fd < 0) {
+                        subdir_fd = openat(fd, de->d_name,
+                                           O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                        if (subdir_fd < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        r = rm_rf_children(subdir_fd, only_dirs, honour_sticky, root_dev);
+                        if (r < 0 && ret == 0)
+                                ret = r;
+
+                        if (!keep_around)
+                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
                                         if (ret == 0 && errno != ENOENT)
                                                 ret = -errno;
                                         if (ret == 0 && errno != ENOENT)
                                                 ret = -errno;
-                                        continue;
                                 }
 
                                 }
 
-                                r = rm_rf_children(subdir_fd, only_dirs, honour_sticky, root_dev);
-                                if (r < 0 && ret == 0)
-                                        ret = r;
-
-                                if (!keep_around)
-                                        if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                                                if (ret == 0 && errno != ENOENT)
-                                                        ret = -errno;
-                                        }
-                        }
-
                 } else if (!only_dirs && !keep_around) {
 
                         if (unlinkat(fd, de->d_name, 0) < 0) {
                 } else if (!only_dirs && !keep_around) {
 
                         if (unlinkat(fd, de->d_name, 0) < 0) {