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 {
