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 0e64dd76a0e9c3f9b53cd3506b170897dc2154a3 Author: zhengyu16 <[email protected]> AuthorDate: Fri Dec 5 14:37:41 2025 +0800 fs/vfs: add lstat interface to mountpt_operations Add an lstat method to mountpt_operations so that mounted file systems can report link metadata without dereferencing symbolic links. In mountptrename() and stat_recursive(), prefer lstat() over stat() when it is available so that rename() and the non-following stat path operate on the link itself rather than its target, matching POSIX semantics. Signed-off-by: zhengyu16 <[email protected]> --- fs/vfs/fs_rename.c | 42 ++++++++++++++++++++++++++++++++++-------- fs/vfs/fs_stat.c | 12 ++++++++++-- include/nuttx/fs/fs.h | 3 +++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c index 38ed865cabd..c461124157c 100644 --- a/fs/vfs/fs_rename.c +++ b/fs/vfs/fs_rename.c @@ -361,12 +361,28 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, * then the rename should fail with the error ENOTEMPTY. */ +#ifdef CONFIG_FS_LINKS + if (oldinode->u.i_mops->lstat != NULL || oldinode->u.i_mops->stat != NULL) +#else if (oldinode->u.i_mops->stat != NULL) +#endif { struct stat oldbuf; struct stat newbuf; - ret = oldinode->u.i_mops->stat(oldinode, oldrelpath, &oldbuf); +#ifdef CONFIG_FS_LINKS + /* Use lstat if available to avoid dereferencing symlinks */ + + if (oldinode->u.i_mops->lstat) + { + ret = oldinode->u.i_mops->lstat(oldinode, oldrelpath, &oldbuf); + } + else +#endif + { + ret = oldinode->u.i_mops->stat(oldinode, oldrelpath, &oldbuf); + } + if (ret < 0) { goto errout_with_newinode; @@ -374,7 +390,19 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, oldisdir = S_ISDIR(oldbuf.st_mode); - ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &newbuf); +#ifdef CONFIG_FS_LINKS + /* Use lstat if available to avoid dereferencing symlinks */ + + if (oldinode->u.i_mops->lstat) + { + ret = oldinode->u.i_mops->lstat(oldinode, newrelpath, &newbuf); + } + else +#endif + { + ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &newbuf); + } + if (ret >= 0) { newisdir = S_ISDIR(newbuf.st_mode); @@ -425,9 +453,9 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode, * method should check that. */ - oldinode->u.i_mops->unlink(oldinode, newrelpath); + oldinode->u.i_mops->unlink(oldinode, newrelpath); #ifdef CONFIG_FS_NOTIFY - notify_unlink(newrelpath); + notify_unlink(newrelpath); #endif } } @@ -522,15 +550,13 @@ int rename(FAR const char *oldpath, FAR const char *newpath) } else #endif /* CONFIG_DISABLE_MOUNTPOINT */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS { +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS ret = pseudorename(oldpath, oldinode, olddesc.parent, newpath); - } #else - { ret = -ENXIO; - } #endif + } inode_release(oldinode); diff --git a/fs/vfs/fs_stat.c b/fs/vfs/fs_stat.c index 60259cca12e..9eefb401e5e 100644 --- a/fs/vfs/fs_stat.c +++ b/fs/vfs/fs_stat.c @@ -123,10 +123,17 @@ static int stat_recursive(FAR const char *path, * supports the stat() method */ +# ifdef CONFIG_FS_LINKS + /* use lstat() if available to avoid following symlinks */ + + if (!resolve && inode->u.i_mops && inode->u.i_mops->lstat) + { + ret = inode->u.i_mops->lstat(inode, desc.relpath, buf); + } + else +# endif if (inode->u.i_mops && inode->u.i_mops->stat) { - /* Perform the stat() operation */ - ret = inode->u.i_mops->stat(inode, desc.relpath, buf); } else @@ -428,6 +435,7 @@ int inode_stat(FAR struct inode *inode, FAR struct stat *buf, int resolve) (inode->u.i_bops->geometry != NULL)) { struct geometry geo; + if (inode->u.i_bops->geometry(inode, &geo) >= 0 && geo.geo_available) { diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index a12cc3e2976..36c039888ec 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -412,6 +412,9 @@ struct mountpt_operations CODE ssize_t (*readlink)(FAR struct inode *mountpt, FAR const char *relpath, FAR char *buf, size_t bufsize); + CODE int (*lstat)(FAR struct inode *mountpt, + FAR const char *relpath, + FAR struct stat *buf); #endif }; #endif /* CONFIG_DISABLE_MOUNTPOINT */
