chiark / gitweb /
util: make sure to fstatat() at most once in rm_rf_children()
[elogind.git] / src / shared / util.c
index ae0ce320ad40b5bb0ac4cab909096a27bc7d315f..7d98dc6e4fb34ff85d8ed1e12a7a38c1448d930e 100644 (file)
@@ -2949,12 +2949,20 @@ char* gethostname_malloc(void) {
 
         assert_se(uname(&u) >= 0);
 
-        if (u.nodename[0])
+        if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
                 return strdup(u.nodename);
 
         return strdup(u.sysname);
 }
 
+bool hostname_is_set(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        return !isempty(u.nodename) && !streq(u.nodename, "(none)");
+}
+
 char* getlogname_malloc(void) {
         uid_t uid;
         long bufsize;
@@ -3131,14 +3139,14 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         return 0;
 }
 
-static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
         DIR *d;
         int ret = 0;
 
         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) {
@@ -3149,7 +3157,8 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
 
         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);
@@ -3164,51 +3173,41 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                 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 (honour_sticky)
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-
                         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 {
-                        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;
+                        keep_around = false;
                 }
 
                 if (is_dir) {
                         int subdir_fd;
 
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        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) {
                                 if (ret == 0 && errno != ENOENT)
                                         ret = -errno;
                                 continue;
                         }
 
-                        r = rm_rf_children(subdir_fd, only_dirs, honour_sticky);
+                        r = rm_rf_children(subdir_fd, only_dirs, honour_sticky, root_dev);
                         if (r < 0 && ret == 0)
                                 ret = r;
 
@@ -3251,7 +3250,7 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
                 return 0;
         }
 
-        r = rm_rf_children(fd, only_dirs, honour_sticky);
+        r = rm_rf_children(fd, only_dirs, honour_sticky, NULL);
 
         if (delete_root) {
 
@@ -5631,3 +5630,12 @@ bool is_valid_documentation_url(const char *url) {
 
         return false;
 }
+
+bool in_initrd(void) {
+        static int saved = -1;
+
+        if (saved < 0)
+                saved = access("/etc/initrd-release", F_OK) >= 0;
+
+        return saved;
+}