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;
