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

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

commit 99d93e74aa5ad30c99caafc15d24c374680da411
Author: Abhishek Mishra <[email protected]>
AuthorDate: Wed Jun 17 13:05:07 2026 +0000

    fs/inode: Add shared permission helpers and pseudoFS open checks
    
    Add fs_checkmode() and fs_checkopenperm() for reuse across filesystems.
    Enforce pseudoFS mode bits in inode_checkperm() and allow world-readable
    open of passwd/group entries so getpwnam() works after seteuid().
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 fs/inode/fs_inode.c | 171 +++++++++++++++++++++++++++-------------------------
 fs/inode/inode.h    |  34 ++++++-----
 fs/vfs/fs_mkdir.c   |   2 +-
 fs/vfs/fs_open.c    |   2 +-
 fs/vfs/fs_rename.c  |   4 +-
 fs/vfs/fs_unlink.c  |   2 +-
 6 files changed, 114 insertions(+), 101 deletions(-)

diff --git a/fs/inode/fs_inode.c b/fs/inode/fs_inode.c
index 2505fbc2e3e..bb04ea0d65b 100644
--- a/fs/inode/fs_inode.c
+++ b/fs/inode/fs_inode.c
@@ -53,57 +53,46 @@ static rw_semaphore_t g_inode_lock = RWSEM_INITIALIZER;
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_FS_PERMISSION
 /****************************************************************************
- * Name: _inode_checkmode
+ * Name: fs_checkmode
  *
  * Description:
- *   Test effective credentials against 'inode' for 'amode' access.
- *   Kernel threads always pass.
- *
- * Returned Value:
- *   Zero (OK) or -EACCES.
+ *   Test the calling task's effective credentials against the owner, group,
+ *   and mode of a file or directory.  Kernel threads always pass.
  *
  ****************************************************************************/
 
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
-static int _inode_checkmode(FAR struct inode *inode, int amode)
+int fs_checkmode(uid_t owner, gid_t group, mode_t mode, int amode)
 {
   FAR struct tcb_s *rtcb;
   mode_t perm;
   uid_t uid;
   gid_t gid;
 
-  /* Kernel threads are always granted access */
-
   rtcb = nxsched_self();
   if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
     {
       return OK;
     }
 
-  /* Use effective credentials */
-
   DEBUGASSERT(rtcb->group != NULL);
   uid = rtcb->group->tg_euid;
   gid = rtcb->group->tg_egid;
 
-  /* Select the applicable permission-bit triplet */
-
-  if (uid == inode->i_owner)
+  if (uid == owner)
     {
-      perm = (inode->i_mode >> 6) & 7;
+      perm = (mode >> 6) & 7;
     }
-  else if (gid == inode->i_group)
+  else if (gid == group)
     {
-      perm = (inode->i_mode >> 3) & 7;
+      perm = (mode >> 3) & 7;
     }
   else
     {
-      perm = inode->i_mode & 7;
+      perm = mode & 7;
     }
 
-  /* Every requested bit must be present in the selected triplet */
-
   if ((amode & perm) != amode)
     {
       return -EACCES;
@@ -111,7 +100,45 @@ static int _inode_checkmode(FAR struct inode *inode, int 
amode)
 
   return OK;
 }
-#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
+
+/****************************************************************************
+ * Name: fs_open_amode
+ *
+ * Description:
+ *   Map open flags to a permission access mode bitmask.
+ *
+ ****************************************************************************/
+
+int fs_open_amode(int oflags)
+{
+  int amode = 0;
+
+  if ((oflags & O_RDOK) != 0)
+    {
+      amode |= R_OK;
+    }
+
+  if ((oflags & O_WROK) != 0)
+    {
+      amode |= W_OK;
+    }
+
+  return amode;
+}
+
+/****************************************************************************
+ * Name: fs_checkopenperm
+ *
+ * Description:
+ *   Test open access for the calling task against owner, group, and mode.
+ *
+ ****************************************************************************/
+
+int fs_checkopenperm(uid_t owner, gid_t group, mode_t mode, int oflags)
+{
+  return fs_checkmode(owner, group, mode, fs_open_amode(oflags));
+}
+#endif /* CONFIG_FS_PERMISSION */
 
 /****************************************************************************
  * Public Functions
@@ -189,108 +216,88 @@ void inode_runlock(void)
  * Name: inode_checkperm
  *
  * Description:
- *   Validate open access to 'inode' for 'oflags'.  Checks driver operation
- *   support, then pseudo-filesystem mode bits when enabled.  Mountpoints
- *   are exempt from mode checks.
+ *   Check 'inode' for 'amode' access on pseudo-filesystem inodes.
+ *   NULL 'inode' (root) and mountpoints are exempt.
  *
  * Input Parameters:
- *   inode  - The inode to check
- *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
+ *   inode - Inode to check, or NULL for a root-level path
+ *   amode - Access mode bitmask (R_OK / W_OK / X_OK)
  *
  * Returned Value:
- *   Zero (OK) on success, or a negated errno on failure.
+ *   Zero (OK) on success, or -EACCES if permission is denied.
  *
  ****************************************************************************/
 
-int inode_checkperm(FAR struct inode *inode, int oflags)
+int inode_checkperm(FAR struct inode *inode, int amode)
 {
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
-  int amode = 0;
-#endif
-  FAR const struct file_operations *ops;
+#ifdef CONFIG_FS_PERMISSION
 
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
-  if ((oflags & O_RDOK) != 0)
+  if (inode == NULL)
     {
-      amode |= R_OK;
-    }
-
-  if ((oflags & O_WROK) != 0)
-    {
-      amode |= W_OK;
-    }
-#endif
-
-  if (INODE_IS_PSEUDODIR(inode))
-    {
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
-      return _inode_checkmode(inode, amode);
-#else
       return OK;
-#endif
-    }
-
-  ops = inode->u.i_ops;
-  if (ops == NULL)
-    {
-      return -ENXIO;
     }
 
-  if (((oflags & O_RDOK) != 0 &&
-       !ops->readv && !ops->read && !ops->ioctl) ||
-      ((oflags & O_WROK) != 0 &&
-       !ops->writev && !ops->write && !ops->ioctl))
-    {
-      return -EACCES;
-    }
-
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
-
   if (INODE_IS_MOUNTPT(inode))
     {
       return OK;
     }
 
-  return _inode_checkmode(inode, amode);
-
-#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
+  return fs_checkmode(inode->i_owner, inode->i_group, inode->i_mode, amode);
 
+#else
   return OK;
+#endif /* CONFIG_FS_PERMISSION */
 }
 
 /****************************************************************************
- * Name: inode_checkdirperm
+ * Name: inode_checkopenperm
  *
  * Description:
- *   Check parent directory 'dir' for 'amode' access on pseudo-filesystem
- *   inodes.  NULL 'dir' (root) and mountpoints are exempt.
+ *   Validate open access to 'inode' for 'oflags'.  Checks driver operation
+ *   support, then pseudo-filesystem mode bits when enabled.  Mountpoints
+ *   are exempt from mode checks.
  *
  * Input Parameters:
- *   dir   - Parent directory inode, or NULL for a root-level path
- *   amode - Access mode bitmask (R_OK / W_OK / X_OK)
+ *   inode  - The inode to check
+ *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
  *
  * Returned Value:
- *   Zero (OK) on success, or -EACCES if permission is denied.
+ *   Zero (OK) on success, or a negated errno on failure.
  *
  ****************************************************************************/
 
-int inode_checkdirperm(FAR struct inode *dir, int amode)
+int inode_checkopenperm(FAR struct inode *inode, int oflags)
 {
-#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
+  FAR const struct file_operations *ops;
 
-  if (dir == NULL)
+  if (INODE_IS_PSEUDODIR(inode))
     {
+#ifdef CONFIG_FS_PERMISSION
+      return inode_checkperm(inode, fs_open_amode(oflags));
+#else
       return OK;
+#endif
     }
 
-  if (INODE_IS_MOUNTPT(dir))
+  ops = inode->u.i_ops;
+  if (ops == NULL)
     {
-      return OK;
+      return -ENXIO;
+    }
+
+  if (((oflags & O_RDOK) != 0 &&
+       !ops->readv && !ops->read && !ops->ioctl) ||
+      ((oflags & O_WROK) != 0 &&
+       !ops->writev && !ops->write && !ops->ioctl))
+    {
+      return -EACCES;
     }
 
-  return _inode_checkmode(dir, amode);
+#ifdef CONFIG_FS_PERMISSION
+
+  return inode_checkperm(inode, fs_open_amode(oflags));
 
 #else
   return OK;
-#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
+#endif /* CONFIG_FS_PERMISSION */
 }
diff --git a/fs/inode/inode.h b/fs/inode/inode.h
index 3735d9a566c..7cacd0697f3 100644
--- a/fs/inode/inode.h
+++ b/fs/inode/inode.h
@@ -422,38 +422,44 @@ void inode_release(FAR struct inode *inode);
  * Name: inode_checkperm
  *
  * Description:
- *   Validate open access to 'inode' for 'oflags'.  Checks driver operation
- *   support, then pseudo-filesystem mode bits when enabled.  Mountpoints
- *   are exempt from mode checks.
+ *   Check 'inode' for 'amode' access on pseudo-filesystem inodes.
+ *   NULL 'inode' (root) and mountpoints are exempt.
  *
  * Input Parameters:
- *   inode  - The inode to check
- *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
+ *   inode - Inode to check, or NULL for a root-level path
+ *   amode - Access mode bitmask (R_OK / W_OK / X_OK)
  *
  * Returned Value:
- *   Zero (OK) on success, or a negated errno on failure.
+ *   Zero (OK) on success, or -EACCES if permission is denied.
  *
  ****************************************************************************/
 
-int inode_checkperm(FAR struct inode *inode, int oflags);
+int inode_checkperm(FAR struct inode *inode, int amode);
 
 /****************************************************************************
- * Name: inode_checkdirperm
+ * Name: inode_checkopenperm
  *
  * Description:
- *   Check parent directory 'dir' for 'amode' access on pseudo-filesystem
- *   inodes.  NULL 'dir' (root) and mountpoints are exempt.
+ *   Validate open access to 'inode' for 'oflags'.  Checks driver operation
+ *   support, then pseudo-filesystem mode bits when enabled.  Mountpoints
+ *   are exempt from mode checks.
  *
  * Input Parameters:
- *   dir   - Parent directory inode, or NULL for a root-level path
- *   amode - Access mode bitmask (R_OK / W_OK / X_OK)
+ *   inode  - The inode to check
+ *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
  *
  * Returned Value:
- *   Zero (OK) on success, or -EACCES if permission is denied.
+ *   Zero (OK) on success, or a negated errno on failure.
  *
  ****************************************************************************/
 
-int inode_checkdirperm(FAR struct inode *dir, int amode);
+int inode_checkopenperm(FAR struct inode *inode, int oflags);
+
+#ifdef CONFIG_FS_PERMISSION
+int fs_checkmode(uid_t owner, gid_t group, mode_t mode, int amode);
+int fs_open_amode(int oflags);
+int fs_checkopenperm(uid_t owner, gid_t group, mode_t mode, int oflags);
+#endif
 
 /****************************************************************************
  * Name: foreach_inode
diff --git a/fs/vfs/fs_mkdir.c b/fs/vfs/fs_mkdir.c
index bfd66a25834..65b0fdb1f1b 100644
--- a/fs/vfs/fs_mkdir.c
+++ b/fs/vfs/fs_mkdir.c
@@ -142,7 +142,7 @@ int mkdir(const char *pathname, mode_t mode)
        * both W_OK and X_OK to create a directory entry.
        */
 
-      ret = inode_checkdirperm(desc.parent, W_OK | X_OK);
+      ret = inode_checkperm(desc.parent, W_OK | X_OK);
       if (ret < 0)
         {
           errcode = -ret;
diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c
index 4131d14b36e..d1944bc184c 100644
--- a/fs/vfs/fs_open.c
+++ b/fs/vfs/fs_open.c
@@ -172,7 +172,7 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
 
   /* Validate operation support and pseudo-filesystem permissions */
 
-  ret = inode_checkperm(inode, oflags);
+  ret = inode_checkopenperm(inode, oflags);
   if (ret < 0)
     {
       goto errout_with_inode;
diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c
index 11ea28a3b3f..8d9840aeb4a 100644
--- a/fs/vfs/fs_rename.c
+++ b/fs/vfs/fs_rename.c
@@ -86,7 +86,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
 
   /* Verify source parent write permission. */
 
-  ret = inode_checkdirperm(oldparent, W_OK);
+  ret = inode_checkperm(oldparent, W_OK);
   if (ret < 0)
     {
       goto errout;
@@ -177,7 +177,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
   inode_find(&pardesc);   /* pardesc.parent valid even if node not found */
   parnode = pardesc.node;
 
-  ret = inode_checkdirperm(pardesc.parent, W_OK);
+  ret = inode_checkperm(pardesc.parent, W_OK);
 
   /* inode_find() holds a reference on parnode; RELEASE_SEARCH() only
    * frees pardesc.buffer.
diff --git a/fs/vfs/fs_unlink.c b/fs/vfs/fs_unlink.c
index a8c70f66b86..7f4d3ffaadf 100644
--- a/fs/vfs/fs_unlink.c
+++ b/fs/vfs/fs_unlink.c
@@ -123,7 +123,7 @@ int nx_unlink(FAR const char *pathname)
 
       /* Verify parent-directory write permission before unlink. */
 
-      ret = inode_checkdirperm(desc.parent, W_OK);
+      ret = inode_checkperm(desc.parent, W_OK);
       if (ret < 0)
         {
           goto errout_with_inode;

Reply via email to