Documentation: describe FS permission interface and mount-crossing

Document inode_checkperm / inode_checkpathperm, mountpoint traverse vs open
semantics, and the optional mountpt_operations.permission hook in
file_permission.rst.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra
2026-08-02 18:48:40 -03:00
committed by Alan C. Assis
parent 08afc5f2cf
commit 4002e6af5f
6 changed files with 165 additions and 3 deletions
@@ -57,3 +57,6 @@ target directory.
Files created before permission support was enabled, or without stored
attributes, default to mode ``0777`` until ``chmod``/``chown`` sets explicit
metadata.
Path components above the littlefs mountpoint are enforced by the VFS; see
:ref:`file-permission`.
@@ -211,3 +211,7 @@ will also fail. You cannot do this, for example:
See also NxFileSystem in
`Porting Guide <https://cwiki.apache.org/confluence/display/NUTTX/Porting+Guide>`_
When ``CONFIG_FS_PERMISSION`` is enabled, pseudoFS directory modes also gate
access into mounted volumes beneath them. See
:ref:`file-permission`.
@@ -11,3 +11,13 @@ At runtime, simply use ``mount -t tmpfs /tmp`` to have a ``/tmp`` folder backed
Be aware that TMPFS is backed by kernel memory thus don't expect to store big files on it and its size is limited by free kernel memory.
We can watch the size of TMPFS with ``df -h`` command, especially you can see the ``Size`` column of TMPFS changes when files are added or removed in the TMPFS folder. Changes in TMPFS size is always reflected by reverse changes of free kernel memory size.
Permissions
===========
When ``CONFIG_FS_PERMISSION`` is enabled, tmpfs stores per-object owner, group,
and mode and enforces them on path operations inside the volume. It also
implements the optional ``mountpt_operations.permission`` hook.
Access into the mount from the pseudoFS is gated by the VFS (parent and
mountpoint ``X_OK``). See :ref:`file-permission`.
@@ -0,0 +1,137 @@
.. _file-permission:
========================================
Filesystem Permission Interface
========================================
When ``CONFIG_FS_PERMISSION`` is enabled, the VFS applies POSIX-style
discretionary access control (DAC) using the caller's effective credentials
(``tg_euid`` / ``tg_egid``). This page describes the common inode helpers,
how mountpoints participate, and how access across a mount is gated by
pseudoFS directory modes.
Prerequisite reading: :ref:`user-identity`.
Configuration
=============
=============================== =============================================
Option Role
=============================== =============================================
``CONFIG_SCHED_USER_IDENTITY`` Per-task-group UID/GID credentials
``CONFIG_PSEUDOFS_ATTRIBUTES`` Store ``i_mode`` / ``i_owner`` / ``i_group``
on pseudoFS inodes
``CONFIG_FS_PERMISSION`` Enable DAC helpers and VFS enforcement
(depends on the two options above)
=============================== =============================================
Without ``CONFIG_FS_PERMISSION``, the helpers described here return success
and no mode-based checks are performed.
Helpers
=======
``inode_checkperm``
Check ``amode`` (``R_OK`` / ``W_OK`` / ``X_OK``) against an inode's
``i_owner`` / ``i_group`` / ``i_mode``. Empty macro returning ``0``
when ``CONFIG_FS_PERMISSION`` is disabled.
``inode_checkpathperm``
Require ``X_OK`` on every ancestor of an inode, and on the inode itself
when it is a pseudo directory or a mountpoint (directory search /
traverse). If ``amode`` is non-zero, also require that access on the
inode. Takes the inode tree read lock unless ``INODE_CHECK_LOCKED`` is
set (caller already holds ``inode_lock`` / ``inode_rlock``). Empty
macro returning ``0`` when ``CONFIG_FS_PERMISSION`` is disabled.
``inode_checkopenperm``
Validate that the inode supports the requested open access, then apply
mode checks for non-mountpoint inodes.
``fs_checkmode``
Core owner/group/other test used by the helpers above and by filesystems
such as tmpfs and littlefs.
Optional mountpoint hook
------------------------
``struct mountpt_operations`` may provide a ``permission`` method for
in-volume DAC. The field is at the **end** of the structure so existing
positional initialisers remain valid.
* **tmpfs** implements ``tmpfs_permission``.
* Filesystems without Unix ownership on disk (for example FAT and ROMFS)
leave the method ``NULL``.
The VFS mount-crossing gate does **not** depend on this hook. Entry into a
volume is enforced with ``inode_checkpathperm`` against the mountpoint
inode's stored ``i_mode``. In-volume checks remain the filesystem's job
(tmpfs and littlefs enforce DAC inside their own open/mkdir/path helpers.
``mops->permission`` is an optional common entry point for the same policy;
the VFS does not invoke it for mount-crossing).
Open vs traverse
================
Mountpoint inodes are **not** open-mode-checked by ``inode_checkopenperm``.
Applying the mount directory's mode bits as file open modes would require
read/write on the mount directory merely to open a file beneath it.
Traverse is separate: callers use ``inode_checkpathperm`` so parent
directories and the mountpoint itself still require ``X_OK``.
Typical order after a successful ``inode_find``:
1. ``inode_checkpathperm(inode, 0, 0)`` — search permission on the path
prefix (and on the mountpoint when entering a volume). Call sites that
already hold the tree lock pass ``INODE_CHECK_LOCKED``; mount and
pseudoFS create/remove may pass a non-zero ``amode`` (for example
``W_OK``) to combine traverse and target checks in one call.
2. Operation-specific checks — ``inode_checkopenperm``, or the
filesystem's own methods for paths inside a mount.
Mount-crossing
==============
Path walk stops at a mountpoint and returns that inode plus a relative path
into the volume. Without traverse checks, a restrictive mode on a pseudoFS
parent would not protect objects under a filesystem mounted beneath it.
Example::
/secure_dir # pseudoFS directory, mode 0700, owner root
/secure_dir/mnt # mounted volume (tmpfs, FAT, ...)
/secure_dir/mnt/a # object inside the volume
``inode_checkpathperm`` requires ``X_OK`` on ``secure_dir`` and on the
mountpoint ``mnt``. A non-root open of ``/secure_dir/mnt/a`` therefore
returns ``EACCES``, even if the mounted filesystem itself has no Unix DAC.
Where the checks run
--------------------
* After ``inode_find`` in open, unlink, mkdir, rmdir, rename, stat, chstat,
statfs, readlink, mount, and umount. ``inode_checkpathperm`` takes
``inode_rlock`` unless the caller already holds the tree lock
(``INODE_CHECK_LOCKED``).
* Inside ``inode_reserve`` / ``inode_remove`` for pseudoFS create and remove
(ancestor ``X_OK`` and parent ``W_OK`` in one call, under ``inode_lock``).
Who enforces what
-----------------
* **PseudoFS parent dirs**``X_OK`` (and ``W_OK`` when creating/removing)
* **Mountpoint inode**``X_OK`` to enter the volume (stored ``i_mode``)
* **Inside the volume** — Filesystem methods; optional ``mops->permission``
* **FAT / ROMFS** — No Unix ownership on disk; entry still gated by the
mountpoint ``i_mode``
References
==========
* ``fs/inode/fs_inode.c````inode_checkperm``, ``inode_checkpathperm``
* ``include/nuttx/fs/fs.h````struct mountpt_operations``
* ``fs/tmpfs/fs_tmpfs.c````tmpfs_permission``
* :ref:`user-identity` — credential model
* :doc:`/components/filesystem/littlefs` — littlefs in-volume DAC
* :doc:`/components/filesystem/tmpfs` — tmpfs overview
+1
View File
@@ -16,6 +16,7 @@ Implementation Details
device_nodes.rst
drivers_design.rst
file_descriptors.rst
file_permission.rst
hardfaults.rst
interrupt_controls.rst
ioctl.rst
+10 -3
View File
@@ -86,7 +86,9 @@ Configuration
``CONFIG_FS_PERMISSION``
Enables filesystem ownership and permission enforcement. Requires
``CONFIG_SCHED_USER_IDENTITY``.
``CONFIG_SCHED_USER_IDENTITY`` and ``CONFIG_PSEUDOFS_ATTRIBUTES``.
See :ref:`file-permission` for the VFS helpers, mount-crossing
traverse rules, and testing notes.
Pseudo-Filesystem Ownership
===========================
@@ -96,5 +98,10 @@ enabled, ``inode_alloc()`` assigns ``i_owner`` and ``i_group`` from the
caller's effective credentials. This covers
message queues (``mq_open()``), named semaphores (``sem_open()``), shared
memory objects (``shm_open()``), FIFOs (``mkfifo()``), and pseudo-files
created through the same inode reservation path. Open-time permission checks
use ``inode_checkopenperm()`` (or ``inode_checkperm()`` for message queues).
created through the same inode reservation path.
Path resolution requires directory search permission (``X_OK``) on ancestors
via ``inode_checkpathperm()``. Open-time checks on the final node use
``inode_checkopenperm()`` (or ``inode_checkperm()`` for named IPC
objects). Full details, including mounts under private pseudoFS parents,
are in :ref:`file-permission`.