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

Reply via email to