From b1bfa70b24d707355f97b0f0cb4429c7a6d22e83 Mon Sep 17 00:00:00 2001 From: zhengyu16 Date: Wed, 24 Sep 2025 16:06:57 +0800 Subject: [PATCH] fs/vfs/rename: rename a directory to an empty directory resolve rename{7}: On a call to rename(old, new), when the old argument points to the pathname of a directory, if the directory named by the new argument exists and is empty it shall be removed and old renamed to new. resolve rename{23}: EEXIST or ENOTEMPTY in errno and a return value of -1 on a call to rename(old, new) when the link named by new is a directory containing entries other than dot and dot-dot. The named files are not changed. Signed-off-by: zhengyu16 --- fs/vfs/fs_rename.c | 91 ++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 55 deletions(-) diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c index 183fb6e5ae6..38ed865cabd 100644 --- a/fs/vfs/fs_rename.c +++ b/fs/vfs/fs_rename.c @@ -294,10 +294,8 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, FAR struct inode *newinode; FAR const char *newrelpath; FAR char *subdir = NULL; -#ifdef CONFIG_FS_NOTIFY bool newisdir = false; bool oldisdir = false; -#endif int ret; DEBUGASSERT(oldinode->u.i_mops); @@ -314,7 +312,7 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, } /* Get an inode for the new relpath -- it should lie on the same - * mountpoint. Path search on oldinode was already enforced by rename(). + * mountpoint */ SETUP_SEARCH(&newdesc, newpath, true); @@ -356,73 +354,68 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, * If the directory entry at the newrelpath is a regular file, then that * file should be removed first. * - * If the directory entry at the target is a directory, then the source - * file should be moved "under" the directory, i.e., if newrelpath is a - * directory, then rename(b,a) should use move the oldrelpath should be - * moved as if rename(b,a/basename(b)) had been called. + * If the directory entry at the newrelpath is an empty directory, then it + * can be removed without issue. + * + * If the directory entry at the newrelpath is a non-empty directory, + * then the rename should fail with the error ENOTEMPTY. */ if (oldinode->u.i_mops->stat != NULL) { - struct stat buf; + struct stat oldbuf; + struct stat newbuf; - ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &buf); + ret = oldinode->u.i_mops->stat(oldinode, oldrelpath, &oldbuf); + if (ret < 0) + { + goto errout_with_newinode; + } + + oldisdir = S_ISDIR(oldbuf.st_mode); + + ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &newbuf); if (ret >= 0) { - /* Is the directory entry a directory? */ + newisdir = S_ISDIR(newbuf.st_mode); + + /* Is the new path a directory? */ -#ifdef CONFIG_FS_NOTIFY - newisdir = S_ISDIR(buf.st_mode); if (newisdir) -#else - if (S_ISDIR(buf.st_mode)) -#endif { - FAR char *subdirname; + /* It is an error to rename a file to a directory */ - /* Yes.. In this case, the target of the rename must be a - * subdirectory of newinode, not the newinode itself. For - * example: mv b a/ must move b to a/b. + if (!oldisdir) + { + ret = -EISDIR; + goto errout_with_newinode; + } + + /* Remove the newrelpath which already exists. + * rmdir will handle the error cases. */ - subdirname = basename((FAR char *)oldrelpath); - - /* Special case the root directory */ - - if (*newrelpath == '\0') + if (oldinode->u.i_mops->rmdir) { - newrelpath = subdirname; - } - else - { - ret = fs_heap_asprintf(&subdir, "%s/%s", newrelpath, - subdirname); + ret = oldinode->u.i_mops->rmdir(oldinode, newrelpath); if (ret < 0) { - subdir = NULL; - ret = -ENOMEM; goto errout_with_newinode; } - - newrelpath = subdir; } } else { - /* No.. newrelpath must refer to a regular file. Make sure - * that the file at the oldrelpath actually exists before - * performing any further actions with newrelpath - */ + /* No.. newrelpath must refer to a regular file. */ - ret = oldinode->u.i_mops->stat(oldinode, oldrelpath, &buf); - if (ret < 0) + if (oldisdir) { + /* It is an error to rename a directory to a file */ + + ret = -ENOTDIR; goto errout_with_newinode; } -#ifdef CONFIG_FS_NOTIFY - oldisdir = S_ISDIR(buf.st_mode); -#endif if (oldinode->u.i_mops->unlink) { /* Attempt to remove the file before doing the rename. @@ -439,18 +432,6 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, } } } -#ifdef CONFIG_FS_NOTIFY - else - { - ret = oldinode->u.i_mops->stat(oldinode, oldrelpath, &buf); - if (ret < 0) - { - goto errout_with_newinode; - } - - oldisdir = S_ISDIR(buf.st_mode); - } -#endif } /* Perform the rename operation using the relative paths at the common