This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit c7cba680a10a735317bb2cddb6a12914a55e8d58
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Oct 19 00:03:36 2026 +0200

    fs/vfs: Return EINVAL when rename() moves a directory below itself.
    
    rename() must fail with EINVAL when the new path is below the old
    directory.  mountptrename() checked this only when the new path
    existed.  When it did not exist, the file system got the request, and
    tmpfs moved the directory into itself.  pseudorename() did not check it
    at all and moved a pseudo directory into itself.  In both cases
    rename() returned 0, and the directory could no longer be found.
    
    mountptrename() now does this check for every directory, before it
    looks for the new path.  pseudorename() does it before it changes the
    inode tree.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 fs/vfs/fs_rename.c | 40 ++++++++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 14 deletions(-)

diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c
index 21c594fd4f8..05a3b689b7d 100644
--- a/fs/vfs/fs_rename.c
+++ b/fs/vfs/fs_rename.c
@@ -76,6 +76,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
 #ifdef CONFIG_FS_NOTIFY
   bool isdir = INODE_IS_PSEUDODIR(oldinode);
 #endif
+  size_t oldlen;
   int ret;
 
   /* Hold the inode tree lock across resolve / permission checks / mutate
@@ -102,6 +103,15 @@ static int pseudorename(FAR const char *oldpath, FAR 
struct inode *oldinode,
       goto errout_with_newsearch;
     }
 
+  /* It is an error to rename an inode to a path below itself */
+
+  oldlen = strlen(oldpath);
+  if (strncmp(newpath, oldpath, oldlen) == 0 && newpath[oldlen] == '/')
+    {
+      ret = -EINVAL;
+      goto errout_with_newsearch;
+    }
+
   /* According to POSIX, any new inode at this path should be removed
    * first, provided that it is not a directory.
    */
@@ -381,6 +391,22 @@ static int mountptrename(FAR const char *oldpath, FAR 
struct inode *oldinode,
 
       oldisdir = S_ISDIR(oldbuf.st_mode);
 
+      /* It is an error to rename a directory into one of its own
+       * subdirectories (new is below old), whether new exists or not.
+       */
+
+      if (oldisdir)
+        {
+          size_t oldlen = strlen(oldrelpath);
+
+          if (strncmp(newrelpath, oldrelpath, oldlen) == 0 &&
+              newrelpath[oldlen] == '/')
+            {
+              ret = -EINVAL;
+              goto errout_with_newinode;
+            }
+        }
+
 #ifdef CONFIG_FS_LINKS
       /* Use lstat if available to avoid dereferencing symlinks */
 
@@ -421,8 +447,6 @@ static int mountptrename(FAR const char *oldpath, FAR 
struct inode *oldinode,
 
           if (newisdir)
             {
-              size_t oldlen;
-
               /* It is an error to rename a file to a directory */
 
               if (!oldisdir)
@@ -430,18 +454,6 @@ static int mountptrename(FAR const char *oldpath, FAR 
struct inode *oldinode,
                   ret = -EISDIR;
                   goto errout_with_newinode;
                 }
-
-              /* It is an error to rename a directory into one of its
-               * own subdirectories (new is below old).
-               */
-
-              oldlen = strlen(oldrelpath);
-              if (strncmp(newrelpath, oldrelpath, oldlen) == 0 &&
-                  newrelpath[oldlen] == '/')
-                {
-                  ret = -EINVAL;
-                  goto errout_with_newinode;
-                }
             }
           else
             {

Reply via email to