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 */

Reply via email to