]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
chase: make struct stat always sync with the opened fd
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 9 Apr 2023 23:40:08 +0000 (08:40 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 9 Apr 2023 23:40:14 +0000 (08:40 +0900)
Previously, struct stat may not be correctly synced with the currently
opened fd, e.g. when a path contains symlink which points to an absolute
path.

This also rename variables for struct stat, to make them consistent with
the corresponding fd.

src/basic/chase.c

index 64f8cd366f452285bfda2a22947cf67053ff05d5..699ca6985f6b230132fe134a42f77fd9469e04b3 100644 (file)
@@ -77,7 +77,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
         _cleanup_close_ int fd = -EBADF, root_fd = -EBADF;
         unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
         bool exists = true, append_trail_slash = false;
-        struct stat previous_stat;
+        struct stat st; /* stat obtained from fd */
         const char *todo;
         int r;
 
@@ -218,7 +218,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
         if (fd < 0)
                 return -errno;
 
-        if (fstat(fd, &previous_stat) < 0)
+        if (fstat(fd, &st) < 0)
                 return -errno;
 
         if (flags & CHASE_TRAIL_SLASH)
@@ -227,7 +227,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
         for (todo = buffer;;) {
                 _cleanup_free_ char *first = NULL;
                 _cleanup_close_ int child = -EBADF;
-                struct stat st;
+                struct stat st_child;
                 const char *e;
 
                 r = path_find_first_component(&todo, /* accept_dot_dot= */ true, &e);
@@ -248,6 +248,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                 if (path_equal(first, "..")) {
                         _cleanup_free_ char *parent = NULL;
                         _cleanup_close_ int fd_parent = -EBADF;
+                        struct stat st_parent;
 
                         /* If we already are at the top, then going up will not change anything. This is
                          * in-line with how the kernel handles this. */
@@ -258,12 +259,12 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                         if (fd_parent < 0)
                                 return -errno;
 
-                        if (fstat(fd_parent, &st) < 0)
+                        if (fstat(fd_parent, &st_parent) < 0)
                                 return -errno;
 
                         /* If we opened the same directory, that means we're at the host root directory, so
                          * going up won't change anything. */
-                        if (st.st_dev == previous_stat.st_dev && st.st_ino == previous_stat.st_ino)
+                        if (st_parent.st_dev == st.st_dev && st_parent.st_ino == st.st_ino)
                                 continue;
 
                         r = path_extract_directory(done, &parent);
@@ -279,18 +280,16 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                         if (flags & CHASE_STEP)
                                 goto chased_one;
 
-                        if (flags & CHASE_SAFE) {
-                                if (unsafe_transition(&previous_stat, &st))
-                                        return log_unsafe_transition(fd, fd_parent, path, flags);
-
-                                previous_stat = st;
-                        }
+                        if (flags & CHASE_SAFE &&
+                            unsafe_transition(&st, &st_parent))
+                                return log_unsafe_transition(fd, fd_parent, path, flags);
 
                         if (FLAGS_SET(flags, CHASE_PARENT) && isempty(todo))
                                 break;
 
+                        /* update fd and stat */
+                        st = st_parent;
                         close_and_replace(fd, fd_parent);
-
                         continue;
                 }
 
@@ -322,19 +321,18 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                                 return r;
                 }
 
-                if (fstat(child, &st) < 0)
+                if (fstat(child, &st_child) < 0)
                         return -errno;
+
                 if ((flags & CHASE_SAFE) &&
-                    unsafe_transition(&previous_stat, &st))
+                    unsafe_transition(&st, &st_child))
                         return log_unsafe_transition(fd, child, path, flags);
 
-                previous_stat = st;
-
                 if ((flags & CHASE_NO_AUTOFS) &&
                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
                         return log_autofs_mount_point(child, path, flags);
 
-                if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
+                if (S_ISLNK(st_child.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         _cleanup_free_ char *destination = NULL;
 
                         if (flags & CHASE_PROHIBIT_SYMLINKS)
@@ -361,15 +359,12 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                                 if (fd < 0)
                                         return fd;
 
-                                if (flags & CHASE_SAFE) {
-                                        if (fstat(fd, &st) < 0)
-                                                return -errno;
-
-                                        if (unsafe_transition(&previous_stat, &st))
-                                                return log_unsafe_transition(child, fd, path, flags);
+                                if (fstat(fd, &st) < 0)
+                                        return -errno;
 
-                                        previous_stat = st;
-                                }
+                                if (flags & CHASE_SAFE &&
+                                    unsafe_transition(&st_child, &st))
+                                        return log_unsafe_transition(child, fd, path, flags);
 
                                 r = free_and_strdup(&done, need_absolute ? "/" : NULL);
                                 if (r < 0)
@@ -398,6 +393,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                         break;
 
                 /* And iterate again, but go one directory further down. */
+                st = st_child;
                 close_and_replace(fd, child);
         }