Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion Documentation/components/filesystem/littlefs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
6 changes: 6 additions & 0 deletions fs/inode/inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ int inode_checkperm(FAR struct inode *inode, int oflags);

int inode_checkdirperm(FAR struct inode *dir, int amode);

#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
*
Expand Down
231 changes: 230 additions & 1 deletion fs/littlefs/lfs_vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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"
Expand Down Expand Up @@ -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
****************************************************************************/
Expand All @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
Loading