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


The following commit(s) were added to refs/heads/master by this push:
     new 34dabfc4e74 fs/littlefs: Enforce open permissions and set create 
ownership
34dabfc4e74 is described below

commit 34dabfc4e7433b25f779698e6c511723453089d8
Author: Abhishek Mishra <[email protected]>
AuthorDate: Wed Jun 17 16:59:41 2026 +0000

    fs/littlefs: Enforce open permissions and set create ownership
    
    Check open access against file mode and owner via fs_checkmode(), verify
    parent directory permissions on create, and assign creator uid/gid to
    newly created files.
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 Documentation/components/filesystem/littlefs.rst |  30 ++-
 fs/inode/inode.h                                 |   6 +
 fs/littlefs/lfs_vfs.c                            | 231 ++++++++++++++++++++++-
 3 files changed, 265 insertions(+), 2 deletions(-)

diff --git a/Documentation/components/filesystem/littlefs.rst 
b/Documentation/components/filesystem/littlefs.rst
index 8e07e074818..35c042cc8f7 100644
--- a/Documentation/components/filesystem/littlefs.rst
+++ b/Documentation/components/filesystem/littlefs.rst
@@ -18,7 +18,7 @@ well as file operations (``fopen()``, ``fclose()``, etc).
    <https://github.com/apache/nuttx/issues/15840>`_.
 
 
-..note:: 
+.. note::
 
   If your littlefs setup is experiencing crashes when you boot, try
   troubleshooting by tweaking the ``BLOCK_SIZE_FACTOR`` options in Kconfig. A
@@ -29,3 +29,31 @@ well as file operations (``fopen()``, ``fclose()``, etc).
    The littlefs support on NuttX only works with mtd drivers, for storage
    devices such as flash chips, SD cards and eMMC. Performance on SD cards and
    eMMC devices is worse than flash.
+
+User identity and permissions
+=============================
+
+When both ``CONFIG_FS_PERMISSION`` and ``CONFIG_FS_LITTLEFS_ATTR_UPDATE`` are
+enabled, littlefs stores owner, group, and mode in per-file custom attributes.
+This requires :ref:`user-identity` (``CONFIG_SCHED_USER_IDENTITY``).
+
+**Metadata.** ``chmod``/``chown`` (via ``chstat``) update the stored mode,
+UID, and GID. ``stat`` and ``ls -l`` read them back.
+
+**Creation.** New files and directories are created with the effective UID and
+GID of the creating task.
+
+**Open checks.** ``open()`` enforces POSIX permission bits using the caller's
+effective identity:
+
+* Existing files: each path component must grant search (``X_OK``) permission;
+  the final component must grant the access requested by ``oflags``.
+* ``O_CREAT`` on a non-existent file: the parent directory must grant write and
+  search permission.
+
+**Directory reads.** ``opendir()`` requires read and search permission on the
+target directory.
+
+Files created before permission support was enabled, or without stored
+attributes, default to mode ``0777`` until ``chmod``/``chown`` sets explicit
+metadata.
diff --git a/fs/inode/inode.h b/fs/inode/inode.h
index 7cacd0697f3..b8280d91bb5 100644
--- a/fs/inode/inode.h
+++ b/fs/inode/inode.h
@@ -461,6 +461,12 @@ int fs_open_amode(int oflags);
 int fs_checkopenperm(uid_t owner, gid_t group, mode_t mode, int oflags);
 #endif
 
+#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/littlefs/lfs_vfs.c b/fs/littlefs/lfs_vfs.c
index 1a4208e3364..39825440257 100644
--- a/fs/littlefs/lfs_vfs.c
+++ b/fs/littlefs/lfs_vfs.c
@@ -30,6 +30,8 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include <nuttx/crc16.h>
 #include <nuttx/fs/fs.h>
@@ -38,7 +40,6 @@
 #include <nuttx/mtd/mtd.h>
 #include <nuttx/mutex.h>
 
-#include <sys/stat.h>
 #include <sys/statfs.h>
 
 #include "inode/inode.h"
@@ -337,6 +338,193 @@ static FAR const char *littlefs_convert_path(FAR const 
char *path)
   return path;
 }
 
+#if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_LITTLEFS_ATTR_UPDATE)
+
+/****************************************************************************
+ * Name: littlefs_statbuf_locked
+ ****************************************************************************/
+
+static int littlefs_statbuf_locked(FAR struct littlefs_mountpt_s *fs,
+                                   FAR const char *relpath,
+                                   FAR struct stat *buf)
+{
+  struct lfs_info info;
+  struct littlefs_attr_s attr;
+  int ret;
+
+  memset(buf, 0, sizeof(*buf));
+
+  ret = lfs_stat(&fs->lfs, relpath, &info);
+  if (ret < 0)
+    {
+      return littlefs_convert_result(ret);
+    }
+
+  ret = littlefs_convert_result(lfs_getattr(&fs->lfs, relpath, 0,
+                                            &attr, sizeof(attr)));
+  if (ret < 0)
+    {
+      if (ret != -ENODATA)
+        {
+          return ret;
+        }
+
+      memset(&attr, 0, sizeof(attr));
+      attr.at_mode = S_IRWXG | S_IRWXU | S_IRWXO;
+    }
+
+  buf->st_mode         = attr.at_mode;
+  buf->st_uid          = attr.at_uid;
+  buf->st_gid          = attr.at_gid;
+  buf->st_atim.tv_sec  = attr.at_atim / 1000000000ull;
+  buf->st_atim.tv_nsec = attr.at_atim % 1000000000ull;
+  buf->st_mtim.tv_sec  = attr.at_mtim / 1000000000ull;
+  buf->st_mtim.tv_nsec = attr.at_mtim % 1000000000ull;
+  buf->st_ctim.tv_sec  = attr.at_ctim / 1000000000ull;
+  buf->st_ctim.tv_nsec = attr.at_ctim % 1000000000ull;
+  buf->st_blksize      = fs->cfg.block_size;
+
+  if (info.type == LFS_TYPE_REG)
+    {
+      buf->st_mode |= S_IFREG;
+      buf->st_size = info.size;
+    }
+  else
+    {
+      buf->st_mode |= S_IFDIR;
+      buf->st_size = 0;
+    }
+
+  buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: littlefs_check_pathperm_locked
+ ****************************************************************************/
+
+static int littlefs_check_pathperm_locked(FAR struct littlefs_mountpt_s *fs,
+                                          FAR const char *relpath,
+                                          int final_amode)
+{
+  struct stat st;
+  char subpath[PATH_MAX];
+  size_t begin = 0;
+  size_t end;
+  int ret;
+
+  while (relpath[begin] == '/')
+    {
+      begin++;
+    }
+
+  if (relpath[begin] == '\0')
+    {
+      return OK;
+    }
+
+  for (end = begin; ; end++)
+    {
+      if (relpath[end] != '\0' && relpath[end] != '/')
+        {
+          continue;
+        }
+
+      if (relpath[end] == '\0')
+        {
+          break;
+        }
+
+      if (end - begin >= PATH_MAX)
+        {
+          return -ENAMETOOLONG;
+        }
+
+      memcpy(subpath, relpath + begin, end - begin);
+      subpath[end - begin] = '\0';
+
+      ret = littlefs_statbuf_locked(fs, subpath, &st);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      ret = fs_checkmode(st.st_uid, st.st_gid, st.st_mode, X_OK);
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+
+  /* Check permission on the final path component */
+
+  ret = littlefs_statbuf_locked(fs, &relpath[begin], &st);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return fs_checkmode(st.st_uid, st.st_gid, st.st_mode, final_amode);
+}
+
+/****************************************************************************
+ * Name: littlefs_check_openperm_locked
+ ****************************************************************************/
+
+static int littlefs_check_openperm_locked(FAR struct littlefs_mountpt_s *fs,
+                                          FAR const char *relpath,
+                                          int oflags)
+{
+  return littlefs_check_pathperm_locked(fs, relpath,
+                                        fs_open_amode(oflags));
+}
+
+/****************************************************************************
+ * Name: littlefs_check_parentperm_locked
+ ****************************************************************************/
+
+static int littlefs_check_parentperm_locked(
+                FAR struct littlefs_mountpt_s *fs,
+                FAR const char *relpath, int amode)
+{
+  FAR const char *slash;
+  char parent[PATH_MAX + 1];
+  struct stat st;
+  size_t pathlen;
+  int ret;
+
+  slash = strrchr(relpath, '/');
+  if (slash == NULL || slash == relpath)
+    {
+      ret = littlefs_statbuf_locked(fs, "/", &st);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      return fs_checkmode(st.st_uid, st.st_gid, st.st_mode, amode);
+    }
+
+  pathlen = slash - relpath;
+  if (pathlen > PATH_MAX)
+    {
+      return -ENAMETOOLONG;
+    }
+
+  memcpy(parent, relpath, pathlen);
+  parent[pathlen] = '\0';
+
+  ret = littlefs_statbuf_locked(fs, parent, &st);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return fs_checkmode(st.st_uid, st.st_gid, st.st_mode, amode);
+}
+
+#endif /* CONFIG_FS_PERMISSION && CONFIG_FS_LITTLEFS_ATTR_UPDATE */
+
 /****************************************************************************
  * Name: littlefs_open
  ****************************************************************************/
@@ -347,6 +535,9 @@ static int littlefs_open(FAR struct file *filep, FAR const 
char *relpath,
   FAR struct littlefs_mountpt_s *fs;
   FAR struct littlefs_file_s *priv;
   FAR struct inode *inode;
+#if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_LITTLEFS_ATTR_UPDATE)
+  struct lfs_info info;
+#endif
   int ret;
 
   /* Get the mountpoint inode reference from the file structure and the
@@ -377,6 +568,27 @@ static int littlefs_open(FAR struct file *filep, FAR const 
char *relpath,
   /* Try to open the file */
 
   relpath = littlefs_convert_path(relpath);
+
+#if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_LITTLEFS_ATTR_UPDATE)
+  ret = lfs_stat(&fs->lfs, relpath, &info);
+  if (ret == 0)
+    {
+      ret = littlefs_check_openperm_locked(fs, relpath, oflags);
+      if (ret < 0)
+        {
+          goto errout;
+        }
+    }
+  else if (ret == LFS_ERR_NOENT && (oflags & O_CREAT) != 0)
+    {
+      ret = littlefs_check_parentperm_locked(fs, relpath, W_OK | X_OK);
+      if (ret < 0)
+        {
+          goto errout;
+        }
+    }
+#endif
+
   oflags = littlefs_convert_oflags(oflags);
   if (fs->readonly)
     {
@@ -405,6 +617,10 @@ static int littlefs_open(FAR struct file *filep, FAR const 
char *relpath,
       clock_gettime(CLOCK_REALTIME, &time);
       memset(&attr, 0, sizeof(attr));
       attr.at_mode = mode;
+#ifdef CONFIG_FS_PERMISSION
+      attr.at_uid = geteuid();
+      attr.at_gid = getegid();
+#endif
       attr.at_ctim = 1000000000ull * time.tv_sec + time.tv_nsec;
       attr.at_atim = attr.at_ctim;
       attr.at_mtim = attr.at_ctim;
@@ -1019,6 +1235,15 @@ static int littlefs_opendir(FAR struct inode *mountpt,
   /* Call the LFS's opendir function */
 
   relpath = littlefs_convert_path(relpath);
+
+#if defined(CONFIG_FS_PERMISSION) && defined(CONFIG_FS_LITTLEFS_ATTR_UPDATE)
+  ret = littlefs_check_pathperm_locked(fs, relpath, R_OK | X_OK);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+#endif
+
   ret = littlefs_convert_result(lfs_dir_open(&fs->lfs, &ldir->dir, relpath));
   if (ret < 0)
     {
@@ -1740,6 +1965,10 @@ static int littlefs_mkdir(FAR struct inode *mountpt, FAR 
const char *relpath,
       clock_gettime(CLOCK_REALTIME, &time);
       memset(&attr, 0, sizeof(attr));
       attr.at_mode = mode;
+#ifdef CONFIG_FS_PERMISSION
+      attr.at_uid = geteuid();
+      attr.at_gid = getegid();
+#endif
       attr.at_ctim = 1000000000ull * time.tv_sec + time.tv_nsec;
       attr.at_atim = attr.at_ctim;
       attr.at_mtim = attr.at_ctim;

Reply via email to