This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 0e662be70ae fs/tmpfs: Add user identity and permission enforcement
0e662be70ae is described below
commit 0e662be70ae1ae49c13ca2b969b795cf7dceac00
Author: Abhishek Mishra <[email protected]>
AuthorDate: Wed Jun 17 16:47:08 2026 +0000
fs/tmpfs: Add user identity and permission enforcement
Store uid/gid/mode on tmpfs objects, support chstat, enforce open and
path permissions via fs_checkmode()/fs_checkopenperm(), and inherit
creator identity on file creation.
Signed-off-by: Abhishek Mishra <[email protected]>
---
fs/tmpfs/fs_tmpfs.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++-----
fs/tmpfs/fs_tmpfs.h | 17 +++
2 files changed, 326 insertions(+), 30 deletions(-)
diff --git a/fs/tmpfs/fs_tmpfs.c b/fs/tmpfs/fs_tmpfs.c
index 99b01d04f26..80e2e04e5b6 100644
--- a/fs/tmpfs/fs_tmpfs.c
+++ b/fs/tmpfs/fs_tmpfs.c
@@ -106,13 +106,15 @@ static int tmpfs_remove_dirent(FAR struct
tmpfs_directory_s *tdo,
static int tmpfs_add_dirent(FAR struct tmpfs_directory_s *tdo,
FAR struct tmpfs_object_s *to, FAR const char *name);
static FAR struct tmpfs_file_s *
-tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent);
+tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent, mode_t mode);
static int tmpfs_create_file(FAR struct tmpfs_s *fs,
- FAR const char *relpath, FAR struct tmpfs_file_s **tfo);
+ FAR const char *relpath, mode_t mode,
+ FAR struct tmpfs_file_s **tfo);
static FAR struct tmpfs_directory_s *
-tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent);
+tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent, mode_t mode);
static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
- FAR const char *relpath, FAR struct tmpfs_directory_s **tdo);
+ FAR const char *relpath, mode_t mode,
+ FAR struct tmpfs_directory_s **tdo);
static int tmpfs_find_object(FAR struct tmpfs_s *fs,
FAR const char *relpath, size_t len,
FAR struct tmpfs_object_s **object,
@@ -146,6 +148,8 @@ static int tmpfs_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int tmpfs_sync(FAR struct file *filep);
static int tmpfs_dup(FAR const struct file *oldp, FAR struct file *newp);
static int tmpfs_fstat(FAR const struct file *filep, FAR struct stat *buf);
+static int tmpfs_fchstat(FAR const struct file *filep,
+ FAR const struct stat *buf, int flags);
static int tmpfs_truncate(FAR struct file *filep, off_t length);
static int tmpfs_mmap(FAR struct file *filep,
FAR struct mm_map_entry_s *map);
@@ -174,6 +178,8 @@ static void tmpfs_stat_common(FAR struct tmpfs_object_s *to,
FAR struct stat *buf);
static int tmpfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf);
+static int tmpfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR const struct stat *buf, int flags);
/****************************************************************************
* Public Data
@@ -196,7 +202,7 @@ const struct mountpt_operations g_tmpfs_operations =
tmpfs_sync, /* sync */
tmpfs_dup, /* dup */
tmpfs_fstat, /* fstat */
- NULL, /* fchstat */
+ tmpfs_fchstat, /* fchstat */
tmpfs_opendir, /* opendir */
tmpfs_closedir, /* closedir */
@@ -212,7 +218,7 @@ const struct mountpt_operations g_tmpfs_operations =
tmpfs_rmdir, /* rmdir */
tmpfs_rename, /* rename */
tmpfs_stat, /* stat */
- NULL /* chstat */
+ tmpfs_chstat /* chstat */
};
/****************************************************************************
@@ -555,12 +561,162 @@ static int tmpfs_add_dirent(FAR struct tmpfs_directory_s
*tdo,
return OK;
}
+#ifdef CONFIG_FS_PERMISSION
+
+/****************************************************************************
+ * Name: tmpfs_init_object
+ ****************************************************************************/
+
+static void tmpfs_init_object(FAR struct tmpfs_object_s *to, mode_t mode)
+{
+ FAR struct tcb_s *rtcb;
+
+ to->to_mode = mode & 07777;
+ rtcb = nxsched_self();
+ if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL ||
+ rtcb->group == NULL)
+ {
+ to->to_uid = 0;
+ to->to_gid = 0;
+ }
+ else
+ {
+ to->to_uid = rtcb->group->tg_euid;
+ to->to_gid = rtcb->group->tg_egid;
+ }
+}
+
+/****************************************************************************
+ * Name: tmpfs_check_pathperm
+ ****************************************************************************/
+
+static int tmpfs_check_pathperm(FAR struct tmpfs_s *fs,
+ FAR const char *relpath, size_t relpathlen,
+ int final_amode)
+{
+ FAR struct tmpfs_directory_s *tdo;
+ FAR struct tmpfs_object_s *to;
+ size_t begin = 0;
+ size_t end;
+ int ret;
+
+ while (begin < relpathlen && relpath[begin] == '/')
+ {
+ begin++;
+ }
+
+ if (begin >= relpathlen)
+ {
+ to = fs->tfs_root.tde_object;
+ return fs_checkmode(to->to_uid, to->to_gid,
+ to->to_mode | S_IFDIR, final_amode);
+ }
+
+ for (end = begin; end <= relpathlen; end++)
+ {
+ if (end < relpathlen && relpath[end] != '/')
+ {
+ continue;
+ }
+
+ ret = tmpfs_find_directory(fs, relpath, end, &tdo, NULL);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ to = (FAR struct tmpfs_object_s *)tdo;
+ ret = fs_checkmode(to->to_uid, to->to_gid,
+ to->to_mode | S_IFDIR,
+ end >= relpathlen ? final_amode : X_OK);
+ tdo->tdo_refs--;
+ tmpfs_unlock_directory(tdo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: tmpfs_check_parentperm
+ ****************************************************************************/
+
+static int tmpfs_check_parentperm(FAR struct tmpfs_s *fs,
+ FAR const char *relpath, int amode)
+{
+ FAR const char *slash;
+ FAR struct tmpfs_object_s *to;
+ size_t parentlen;
+
+ slash = strrchr(relpath, '/');
+ if (slash == NULL || slash == relpath)
+ {
+ to = fs->tfs_root.tde_object;
+ return fs_checkmode(to->to_uid, to->to_gid,
+ to->to_mode | S_IFDIR, amode);
+ }
+
+ parentlen = slash - relpath;
+ return tmpfs_check_pathperm(fs, relpath, parentlen, amode);
+}
+
+/****************************************************************************
+ * Name: tmpfs_chstat_object
+ ****************************************************************************/
+
+static int tmpfs_chstat_object(FAR struct tmpfs_object_s *to,
+ FAR const struct stat *buf, int flags)
+{
+ FAR struct tcb_s *rtcb;
+ uid_t euid;
+
+ rtcb = nxsched_self();
+ if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL &&
+ rtcb->group != NULL)
+ {
+ euid = rtcb->group->tg_euid;
+
+ if ((flags & (CH_STAT_UID | CH_STAT_GID)) != 0 && euid != 0)
+ {
+ return -EPERM;
+ }
+
+ if ((flags & CH_STAT_MODE) != 0 &&
+ euid != 0 && euid != to->to_uid)
+ {
+ return -EPERM;
+ }
+ }
+
+ if ((flags & CH_STAT_MODE) != 0)
+ {
+ to->to_mode = buf->st_mode & 07777;
+ }
+
+ if ((flags & CH_STAT_UID) != 0)
+ {
+ to->to_uid = buf->st_uid;
+ }
+
+ if ((flags & CH_STAT_GID) != 0)
+ {
+ to->to_gid = buf->st_gid;
+ }
+
+ return OK;
+}
+
+#endif /* CONFIG_FS_PERMISSION */
+
/****************************************************************************
* Name: tmpfs_alloc_file
****************************************************************************/
static FAR struct tmpfs_file_s *
-tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent)
+tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent, mode_t mode)
{
FAR struct tmpfs_file_s *tfo;
@@ -584,6 +740,12 @@ tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent)
tfo->tfo_size = 0;
tfo->tfo_data = NULL;
+#ifdef CONFIG_FS_PERMISSION
+ tmpfs_init_object((FAR struct tmpfs_object_s *)tfo, mode);
+#else
+ UNUSED(mode);
+#endif
+
nxrmutex_init(&tfo->tfo_lock);
tmpfs_lock_file(tfo);
@@ -595,7 +757,7 @@ tmpfs_alloc_file(FAR struct tmpfs_directory_s *parent)
****************************************************************************/
static int tmpfs_create_file(FAR struct tmpfs_s *fs,
- FAR const char *relpath,
+ FAR const char *relpath, mode_t mode,
FAR struct tmpfs_file_s **tfo)
{
FAR struct tmpfs_directory_s *parent;
@@ -670,7 +832,7 @@ static int tmpfs_create_file(FAR struct tmpfs_s *fs,
* one reference count.
*/
- newtfo = tmpfs_alloc_file(parent);
+ newtfo = tmpfs_alloc_file(parent, mode);
if (newtfo == NULL)
{
ret = -ENOMEM;
@@ -712,7 +874,7 @@ errout_with_parent:
****************************************************************************/
static FAR struct tmpfs_directory_s *
-tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent)
+tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent, mode_t mode)
{
FAR struct tmpfs_directory_s *tdo;
@@ -733,6 +895,12 @@ tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent)
tdo->tdo_nentries = 0;
tdo->tdo_entry = NULL;
+#ifdef CONFIG_FS_PERMISSION
+ tmpfs_init_object((FAR struct tmpfs_object_s *)tdo, mode);
+#else
+ UNUSED(mode);
+#endif
+
nxrmutex_init(&tdo->tdo_lock);
return tdo;
@@ -743,7 +911,7 @@ tmpfs_alloc_directory(FAR struct tmpfs_directory_s *parent)
****************************************************************************/
static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
- FAR const char *relpath,
+ FAR const char *relpath, mode_t mode,
FAR struct tmpfs_directory_s **tdo)
{
FAR struct tmpfs_directory_s *parent;
@@ -817,7 +985,7 @@ static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
* the new directory and the object is not locked.
*/
- newtdo = tmpfs_alloc_directory(parent);
+ newtdo = tmpfs_alloc_directory(parent, mode);
if (newtdo == NULL)
{
ret = -ENOMEM;
@@ -1369,6 +1537,9 @@ static int tmpfs_open(FAR struct file *filep, FAR const
char *relpath,
FAR struct inode *inode;
FAR struct tmpfs_s *fs;
FAR struct tmpfs_file_s *tfo;
+#ifdef CONFIG_FS_PERMISSION
+ FAR struct tmpfs_object_s *to;
+#endif
off_t offset;
int ret;
@@ -1392,10 +1563,6 @@ static int tmpfs_open(FAR struct file *filep, FAR const
char *relpath,
return ret;
}
- /* Skip over any leading directory separators (shouldn't be any) */
-
- for (; *relpath == '/'; relpath++);
-
/* Find the file object associated with this relative path.
* If successful, this action will lock both the parent directory and
* the file object, adding one to the reference count of both.
@@ -1420,9 +1587,23 @@ static int tmpfs_open(FAR struct file *filep, FAR const
char *relpath,
goto errout_with_filelock;
}
- /* Check if the caller has sufficient privileges to open the file.
- * REVISIT: No file protection implemented
- */
+ /* Check if the caller has sufficient privileges to open the file. */
+
+#ifdef CONFIG_FS_PERMISSION
+ to = (FAR struct tmpfs_object_s *)tfo;
+ ret = tmpfs_check_parentperm(fs, relpath, X_OK);
+ if (ret < 0)
+ {
+ goto errout_with_filelock;
+ }
+
+ ret = fs_checkopenperm(to->to_uid, to->to_gid,
+ to->to_mode | S_IFREG, oflags);
+ if (ret < 0)
+ {
+ goto errout_with_filelock;
+ }
+#endif
/* If O_TRUNC is specified and the file is opened for writing,
* then truncate the file. This operation requires that the file is
@@ -1467,7 +1648,15 @@ static int tmpfs_open(FAR struct file *filep, FAR const
char *relpath,
* on the new file object.
*/
- ret = tmpfs_create_file(fs, relpath, &tfo);
+#ifdef CONFIG_FS_PERMISSION
+ ret = tmpfs_check_parentperm(fs, relpath, W_OK | X_OK);
+ if (ret < 0)
+ {
+ goto errout_with_fslock;
+ }
+#endif
+
+ ret = tmpfs_create_file(fs, relpath, mode, &tfo);
if (ret < 0)
{
goto errout_with_fslock;
@@ -1936,6 +2125,36 @@ static int tmpfs_fstat(FAR const struct file *filep, FAR
struct stat *buf)
return OK;
}
+/****************************************************************************
+ * Name: tmpfs_fchstat
+ ****************************************************************************/
+
+static int tmpfs_fchstat(FAR const struct file *filep,
+ FAR const struct stat *buf, int flags)
+{
+ DEBUGASSERT(filep->f_priv != NULL && buf != NULL);
+
+#ifdef CONFIG_FS_PERMISSION
+ FAR struct tmpfs_file_s *tfo;
+ int ret;
+
+ tfo = filep->f_priv;
+
+ ret = tmpfs_lock_file(tfo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = tmpfs_chstat_object((FAR struct tmpfs_object_s *)tfo, buf, flags);
+ tmpfs_unlock_file(tfo);
+ return ret;
+#else
+ UNUSED(flags);
+ return -ENOSYS;
+#endif
+}
+
/****************************************************************************
* Name: tmpfs_truncate
****************************************************************************/
@@ -2031,10 +2250,6 @@ static int tmpfs_opendir(FAR struct inode *mountpt, FAR
const char *relpath,
return ret;
}
- /* Skip over any leading directory separators (shouldn't be any) */
-
- for (; *relpath == '/'; relpath++);
-
/* Find the directory object associated with this relative path.
* If successful, this action will lock both the parent directory and
* the file object, adding one to the reference count of both.
@@ -2045,10 +2260,21 @@ static int tmpfs_opendir(FAR struct inode *mountpt, FAR
const char *relpath,
ret = tmpfs_find_directory(fs, relpath, strlen(relpath), &tdo, NULL);
if (ret >= 0)
{
- tdir->tf_tdo = tdo;
- tdir->tf_index = tdo->tdo_nentries;
- *dir = &tdir->tf_base;
- tmpfs_unlock_directory(tdo);
+#ifdef CONFIG_FS_PERMISSION
+ ret = tmpfs_check_pathperm(fs, relpath, strlen(relpath), R_OK | X_OK);
+ if (ret < 0)
+ {
+ tdo->tdo_refs--;
+ tmpfs_unlock_directory(tdo);
+ }
+ else
+#endif
+ {
+ tdir->tf_tdo = tdo;
+ tdir->tf_index = tdo->tdo_nentries;
+ *dir = &tdir->tf_base;
+ tmpfs_unlock_directory(tdo);
+ }
}
/* Release the lock on the file system and return the result */
@@ -2212,7 +2438,7 @@ static int tmpfs_bind(FAR struct inode *blkdriver, FAR
const void *data,
* the file system structure.
*/
- tdo = tmpfs_alloc_directory(NULL);
+ tdo = tmpfs_alloc_directory(NULL, 0777);
if (tdo == NULL)
{
fs_heap_free(fs);
@@ -2481,7 +2707,7 @@ static int tmpfs_mkdir(FAR struct inode *mountpt, FAR
const char *relpath,
/* Create the directory. */
- ret = tmpfs_create_directory(fs, relpath, NULL);
+ ret = tmpfs_create_directory(fs, relpath, mode, NULL);
tmpfs_unlock(fs);
return ret;
}
@@ -2768,9 +2994,15 @@ static void tmpfs_stat_common(FAR struct tmpfs_object_s
*to,
FAR struct tmpfs_file_s *tfo =
(FAR struct tmpfs_file_s *)to;
+#ifdef CONFIG_FS_PERMISSION
+ buf->st_mode = (to->to_mode & 07777) | S_IFREG;
+ buf->st_uid = to->to_uid;
+ buf->st_gid = to->to_gid;
+#else
/* -rwxrwxrwx */
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFREG;
+#endif
/* Get the size of the object */
@@ -2781,9 +3013,15 @@ static void tmpfs_stat_common(FAR struct tmpfs_object_s
*to,
FAR struct tmpfs_directory_s *tdo =
(FAR struct tmpfs_directory_s *)to;
+#ifdef CONFIG_FS_PERMISSION
+ buf->st_mode = (to->to_mode & 07777) | S_IFDIR;
+ buf->st_uid = to->to_uid;
+ buf->st_gid = to->to_gid;
+#else
/* drwxrwxrwx */
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFDIR;
+#endif
/* Get the size of the object */
@@ -2853,6 +3091,47 @@ errout_with_fslock:
return ret;
}
+/****************************************************************************
+ * Name: tmpfs_chstat
+ ****************************************************************************/
+
+static int tmpfs_chstat(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR const struct stat *buf, int flags)
+{
+ DEBUGASSERT(mountpt != NULL && relpath != NULL && buf != NULL);
+
+#ifdef CONFIG_FS_PERMISSION
+ FAR struct tmpfs_s *fs;
+ FAR struct tmpfs_object_s *to;
+ int ret;
+
+ fs = mountpt->i_private;
+ DEBUGASSERT(fs != NULL);
+
+ ret = tmpfs_lock(fs);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = tmpfs_find_object(fs, relpath, strlen(relpath), &to, NULL);
+ if (ret < 0)
+ {
+ goto errout_with_fslock;
+ }
+
+ ret = tmpfs_chstat_object(to, buf, flags);
+ tmpfs_release_lockedobject(to);
+
+errout_with_fslock:
+ tmpfs_unlock(fs);
+ return ret;
+#else
+ UNUSED(flags);
+ return -ENOSYS;
+#endif
+}
+
/****************************************************************************
* Public Functions
****************************************************************************/
diff --git a/fs/tmpfs/fs_tmpfs.h b/fs/tmpfs/fs_tmpfs.h
index 56a726adc99..b04b62628ca 100644
--- a/fs/tmpfs/fs_tmpfs.h
+++ b/fs/tmpfs/fs_tmpfs.h
@@ -30,6 +30,8 @@
#include <nuttx/config.h>
#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mutex.h>
@@ -82,6 +84,11 @@ struct tmpfs_object_s
uint8_t to_type; /* See enum tmpfs_objtype_e */
uint8_t to_refs; /* Reference count */
FAR struct tmpfs_directory_s *to_parent;
+#ifdef CONFIG_FS_PERMISSION
+ mode_t to_mode; /* File mode (permission bits) */
+ uid_t to_uid; /* Owner user ID */
+ gid_t to_gid; /* Owner group ID */
+#endif
};
/* The form of a directory memory object */
@@ -96,6 +103,11 @@ struct tmpfs_directory_s
uint8_t tdo_type; /* See enum tmpfs_objtype_e */
uint8_t tdo_refs; /* Reference count */
FAR struct tmpfs_directory_s *tdo_parent;
+#ifdef CONFIG_FS_PERMISSION
+ mode_t tdo_mode; /* Directory mode (permission bits) */
+ uid_t tdo_uid; /* Owner user ID */
+ gid_t tdo_gid; /* Owner group ID */
+#endif
/* Remaining fields are unique to a directory object */
@@ -123,6 +135,11 @@ struct tmpfs_file_s
uint8_t tfo_type; /* See enum tmpfs_objtype_e */
uint8_t tfo_refs; /* Reference count */
FAR struct tmpfs_directory_s *tfo_parent;
+#ifdef CONFIG_FS_PERMISSION
+ mode_t tfo_mode; /* File mode (permission bits) */
+ uid_t tfo_uid; /* Owner user ID */
+ gid_t tfo_gid; /* Owner group ID */
+#endif
/* Remaining fields are unique to a directory object */