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 <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra 2026-06-17 16:59:41 +00:00 committed by Xiang Xiao
parent 0e662be70a
commit 34dabfc4e7
3 changed files with 265 additions and 2 deletions

View file

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

View file

@ -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;