Commit graph

2562 commits

Author SHA1 Message Date
Megha Rajput
c5619cb3fe fs/inode: propagate inode search errors
inode_reserve() previously continued processing all negative return
values from inode_search(). Only -ENOENT indicates that the target
inode is absent and creation may continue.

Propagate other search errors through the existing cleanup path to
avoid continuing inode creation with invalid insertion metadata.
Assisted-by: GitHub Copilot
Signed-off-by: Megha Rajput <i.meghar.2408@gmail.com>
2026-09-04 13:50:54 -03:00
yukangzhi
72e0b292f8 fs: fix pre-existing nxstyle issues in touched files
Fix coding style violations detected by CI whole-file nxstyle scan:
- fs/smartfs/smartfs_utils.c: add missing braces after if (L334),
  fix bad alignment (L414), fix switch brace alignment (L1531)
- fs/hostfs/hostfs.c: add blank line after declaration (L576)

These are pre-existing style issues in master, not introduced by
this PR, but reported because CI checks the entire touched file.

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
yukangzhi
fa7865f109 fs/vfs: fix link() returning EXDEV instead of ENAMETOOLONG
When inode_find() for path2 fails due to ENAMETOOLONG (or ELOOP),
the else branch incorrectly falls through to the EXDEV check based
on whether target is a mountpoint.  This causes link() to report
EXDEV for overly long path2, violating POSIX which requires
ENAMETOOLONG in this case.

Fix by propagating the original inode_find() error code when it is
not ENOENT or ENOTDIR (i.e., not a simple "path does not exist"
condition).

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
vela-autotest05
152ebca599 fs/inode: fix relative-path truncation causing wrong EISDIR
Root cause: _inode_search() built the absolute form of a relative
path with snprintf(buf, PATH_MAX, "%s/%s", cwd, path), silently
truncating it when cwd + "/" + path exceeded PATH_MAX. The truncated
buffer was then handed to _inode_canonicalize(), which collapsed
".." segments against the wrong cut-off suffix. A valid relative
path of PATH_MAX-1 bytes (legal per pathconf(_PC_PATH_MAX)) could
thus collapse onto a directory and open() returned EISDIR instead
of resolving the file.

Fix: size the temp buffer to hold the full uncanonicalized
"<cwd>/<path>" form so canonicalization sees the complete path.
lib_get_tempbuffer falls back to a malloc'd buffer when the size
exceeds PATH_MAX (CONFIG_LIBC_TEMPBUFFER_MALLOC). The existing
PATH_MAX check in _inode_canonicalize() still rejects any
canonicalized result that is too long, so ENAMETOOLONG semantics
are preserved.

Signed-off-by: dengwenqi <dengwenqi@xiaomi.com>
2026-08-28 23:07:24 +08:00
yukangzhi
1312bc84a2 fs: remove redundant ".." handling after VFS canonicalization
Since _inode_canonicalize() now resolves all "." and ".." segments
in the common VFS layer before inode search, the relpath passed to
each filesystem will never contain ".." segments.  Remove the
now-dead ".." handling code from individual filesystem layers and
the inode search internals.

Files modified (redundant ".." path resolution removed):
- fs/hostfs/hostfs.c: remove depth-tracking escape check in
  hostfs_mkpath(), simplify to direct path concatenation.
- fs/rpmsgfs/rpmsgfs.c: same as hostfs, remove depth-tracking in
  rpmsgfs_mkpath().
- fs/smartfs/smartfs_utils.c: remove "." and ".." segment checks
  in smartfs_finddirentry(), de-indent the remaining search logic.
- fs/inode/fs_inodesearch.c: remove _inode_isdotdot() function,
  simplify _compute_path_depth() to only count forward segments,
  remove dead else-if branch in _inode_search().

Files NOT modified (and why):
- fs/littlefs/littlefs/lfs.c: third-party upstream library (git
  submodule), must not be modified locally.
- fs/fatfs/fatfs/source/ff.c: third-party upstream library.
- fs/lwext4/lwext4/src/ext4*.c: third-party upstream library.
- fs/cromfs/fs_cromfs.c: handles "." and ".." as directory entries
  (structural, not path resolution), so its code stays.
- fs/vfs/fs_symlink.c: constructs relative paths containing ".."
  (writes, not parses relpath).

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
yukangzhi
67cd42677b fs/inode: canonicalize path before inode search to fix .. resolution
Add _inode_canonicalize() to remove '.' and '..' segments from the
absolute path before the inode tree traversal begins. This fixes the
case where paths containing '..' that resolve back to a mountpoint
root (e.g., /tmp/subdir/..) were not being handed to the filesystem.

Previously, _compute_path_depth() returned 0 for such paths, causing
the VFS to skip the mountpoint and attempt to find 'subdir' in the
pseudo filesystem -- which fails with ENOTDIR.

With canonicalization, /tmp/subdir/.. becomes /tmp before the search,
so the mountpoint is correctly matched. This fixes chdir('..'),
stat('../..'), opendir('../..'), and similar operations from within
mountpoint subdirectories.

The implementation uses an in-place two-pointer algorithm with no
additional stack allocation, safe for NuttX's small kernel stacks.

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
yukangzhi
e396baf06f fs/vfs/rename: fix rename to same file and rename to subdirectory
Fix two POSIX compliance issues in mountptrename():

1. When old and new are hard links to the same file (same st_dev and
   st_ino), POSIX requires rename() to succeed without removing either
   link. Previously, NuttX would unlink(new) then rename(old, new),
   effectively losing one link. Fix by comparing inode identity before
   any destructive operation.

2. When new is a subdirectory of old (e.g., rename('a', 'a/b')), POSIX
   requires EINVAL. Previously, NuttX would rmdir(new) first, then the
   filesystem's rename() would fail -- but new was already deleted,
   causing data loss. Fix by detecting the subdirectory relationship
   (newrelpath starts with oldrelpath + '/') before any rmdir/unlink.

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
yukangzhi
1519a7862f fs/inode: fix off-by-one in _inode_checkpath NAME_MAX check
The loop condition 'namelen <= NAME_MAX' allowed filenames of
NAME_MAX+1 characters to pass validation. When the filename segment
reached exactly NAME_MAX+1 chars and was at the end of the path
string, the loop exited due to *path == '\0' and returned OK instead
of -ENAMETOOLONG.

Fix by moving the NAME_MAX check inside the loop body with an
immediate return on violation. Also fix the post-loop return to
explicitly check pathlen >= PATH_MAX instead of relying on *path
which conflated the two exit conditions.

Before: creat() with 97-char filename (NAME_MAX=96) succeeded
After:  creat() with 97-char filename correctly returns ENAMETOOLONG

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 23:07:24 +08:00
zhaoxingyu1
582693f2ce fs/smartfs: change fs_heap to lib_tempbuffer
Replace fs_heap_malloc/free with lib_get_tempbuffer/lib_put_tempbuffer
in smartfs_finddirentry(). This aligns smartfs with the common VFS
tempbuffer allocation pattern and is a prerequisite for the
canonicalization cleanup that removes redundant ".." handling from
individual filesystem layers.

Signed-off-by: zhaoxingyu1 <zhaoxingyu1@xiaomi.com>
2026-08-28 23:07:24 +08:00
Kaben
9c467c5114 fs/hostfs: fix pre-existing nxstyle issues in touched files
Add missing blank lines after declarations and fix one bad alignment
in hostfs/rpmsgfs-related files. These are pre-existing style issues
flagged by CI's whole-file nxstyle check when our PR touches these
files. No logic change (git diff -w is blank-line-only additions).

Signed-off-by: yukangzhi <yukangzhi@xiaomi.com>
2026-08-28 18:35:37 +08:00
zhengyu16
41ec966941 fs/rpmsgfs: add link, symlink, readlink and lstat support
Implement link(), symlink(), readlink() and lstat() in rpmsgfs.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-28 18:35:37 +08:00
zhengyu16
86193c95ca fs/hostfs: add link, symlink, readlink and lstat support
Implemented link(), symlink(), readlink() and lstat() in hostfs.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-28 18:35:37 +08:00
zhaoxingyu1
c7a78c01c1 fs/hostfs: change fs_heap to lib_tempbuffer
Migrate hostfs path buffer allocation from fs_heap and stack
arrays to lib_get_tempbuffer/lib_put_tempbuffer.

Signed-off-by: zhaoxingyu1 <zhaoxingyu1@xiaomi.com>
2026-08-28 18:35:37 +08:00
buxiasen
40844c002c fs/hostfs: move global lock into hostfs_mountpt_s
Replace the global `g_lock` with a per-filesystem `fs->fs_lock`
to improve concurrency for multi-mount scenarios.

Signed-off-by: buxiasen <buxiasen@xiaomi.com>
2026-08-28 18:35:37 +08:00
wangxingxing
ea4a4585cd fs/inode: fix off-by-one error in _inode_checkpath NAME_MAX check
The _inode_checkpath function uses `namelen < NAME_MAX` to validate
path segment lengths. When a filename is exactly NAME_MAX characters
long and is followed by more path segments (e.g. /dir/), the loop
exits with namelen == NAME_MAX before processing the '/' separator,
causing a spurious ENAMETOOLONG error.

Per POSIX, NAME_MAX is the maximum number of bytes in a filename not
including the terminating null, so a filename of exactly NAME_MAX
characters is valid. Change the condition to `namelen <= NAME_MAX`
so the loop can process the trailing '/' separator and correctly
reset namelen for the next path segment.

Signed-off-by: wangxingxing <wangxingxing@xiaomi.com>
2026-08-28 12:09:46 +08:00
guohao15
24d371c0fe fs/inode: return ENAMETOOLONG for path/filename longer than NAME_MAX
Add a helper _inode_checkpath() that validates the path before the
search: it returns -ENOENT for an empty path and -ENAMETOOLONG when any
single path component exceeds NAME_MAX or the whole path exceeds
PATH_MAX.  inode_search() now runs this check first so that oversized
paths and file names are rejected with the correct POSIX error code.

Signed-off-by: guohao15 <guohao15@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhaoxingyu1
be16f230e3 fs/inode: support path ending with '..' and '.' in inode_search
example: stat(".", buf) and stat("..", buf)

Signed-off-by: zhaoxingyu1 <zhaoxingyu1@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhaoxingyu1
f32800568b fs/inode: support relative path when inode_search
Add support for resolving relative path components (in particular the
".." parent references) during the inode search.  A helper
_compute_path_depth() computes the remaining path depth so that a mount
point is only treated as the terminal node when the depth is positive,
and "../" components walk back up to the parent inode.

Signed-off-by: zhaoxingyu1 <zhaoxingyu1@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhaoxingyu1
4fb02e8766 fs/inode: change fs_heap to lib_get_tempbuffer/lib_put_tempbuffer
Replace the fs_heap_asprintf()/fs_heap_free() based allocation of the
path buffer in the inode search with the lib_get_tempbuffer()/
lib_put_tempbuffer() pool.  Fixed PATH_MAX sized temporary buffers avoid
per-call heap allocation and keep the buffer allocator consistent with
the rest of the path-resolution code.

Signed-off-by: zhaoxingyu1 <zhaoxingyu1@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhengyu16
8c7ca1fb0e fs/inode: return ENOTDIR if a path prefix is not a directory
While walking the path components, a non-final component must refer to a
directory.  When descending into a child, verify the parent inode is a
pseudo directory; if it is not, stop the search and return -ENOTDIR as
required by POSIX for a path prefix that is not a directory.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhengyu16
e93046d1b1 fs/inode: return ENOENT if pathname is empty in inode_search
An empty pathname does not name any inode.  Return -ENOENT early in
inode_search() when the path is an empty string, instead of continuing
into the search logic with a zero-length path.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhengyu16
e73947e9e7 fs/inode: format link path before resolving link target
When resolving a symbolic link target, call the public inode_search()
instead of the internal _inode_search() so that the link target path is
first formatted (leading '/' handling and relative-path conversion)
before the lookup.  This ensures link targets are resolved through the
same normalization path as ordinary lookups.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-28 12:09:46 +08:00
zhengyu16
0e64dd76a0 fs/vfs: add lstat interface to mountpt_operations
Add an lstat method to mountpt_operations so that mounted file systems
can report link metadata without dereferencing symbolic links.

In mountptrename() and stat_recursive(), prefer lstat() over stat()
when it is available so that rename() and the non-following stat path
operate on the link itself rather than its target, matching POSIX
semantics.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00
zhengyu16
b1bfa70b24 fs/vfs/rename: rename a directory to an empty directory
resolve rename{7}:
On a call to rename(old, new), when the old argument points to the pathname of a directory, if the directory named by the new argument exists and is empty it shall be removed and old renamed to new.

resolve rename{23}:
EEXIST or ENOTEMPTY in errno and a return value of -1 on a call to rename(old, new) when the link named by new is a directory containing
entries other than dot and dot-dot.  The named files are not changed.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00
zhengyu16
52dac57f76 fs/vfs: add link, symlink and readlink support for mountpt
1. add three func to mountpt_operations:
   link
   symlink
   readlink
2. modify fs_link、fs_symlink、fs_readlink for mountpt

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00
zhengyu16
ebfe22bfb9 fs: rename PSEUDOFS_SOFTLINKS to FS_LINKS
The link support is no longer limited to the pseudo file system and now
covers both soft (symbolic) links and hard links across the VFS.  Rename
the configuration option PSEUDOFS_SOFTLINKS to the more accurate FS_LINKS
and update all references in the source, headers, Kconfig, documentation
and board defconfigs accordingly.

This is a configuration rename; any out-of-tree defconfig that still
selects PSEUDOFS_SOFTLINKS must be updated to FS_LINKS.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00
zhengyu16
3d09479367 fs/vfs: add hardlink function of pseudofs
1. add the hardlink function
2. _POSIX_LINK_MAX judgement

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00
zhangyu117
d7771b6158 nuttx/atomic: replace atomic_fetch_xxx with atomic_xxx just like zephyr
Rename atomic_fetch_add/sub/or/and/xor to atomic_add/sub/or/and/xor
to avoid conflicts with the C/C++ standard library naming. The
atomic_fetch_xxx naming is reserved by the standard; keeping it causes
function name conflicts when source files indirectly include both
<nuttx/atomic.h> and <atomic>/<stdatomic.h>.

Signed-off-by: zhangyu117 <zhangyu117@xiaomi.com>
2026-08-24 13:20:45 +08:00
yi chen
b9c9c238e8 fs/romfs: reject negative resulting position in romfs_seek()
romfs_seek() clamps the computed position to the file size when it
exceeds rf_size, but never checks for a negative result. lseek(fd,
offset, SEEK_SET/SEEK_CUR/SEEK_END) with an offset that produces a
negative position (e.g. a negative SEEK_SET offset, or a SEEK_CUR/
SEEK_END offset more negative than the current position/file size)
is written straight into filep->f_pos.

The subsequent romfs_read() computes
`rf->rf_startoffset + filep->f_pos` into a uint32_t, so a negative
f_pos wraps around to a huge unsigned offset, and romfs_hwread()'s
XIP path memcpy()s from rm_xipbase plus that offset -- an
out-of-bounds read far past the mapped flash region.

Add the same "if (position < 0) return -EINVAL" guard already used
by fs/fat/fs_fat32.c's seek function, before the existing
end-of-file clamp.

Signed-off-by: yi chen <94xhn1@gmail.com>
Assisted-by: Claude:claude-sonnet-5
2026-08-21 02:20:33 +08:00
Abhishek Mishra
e29db6724c sched,fs,docs: support setuid sudo helper
Supports the UNIX setuid-on-exec sudo helper. Documents the model,
generates an extra ROMFS user and /etc/sudoers for a non-root test,
reports BINFS modes from the builtin table so ls -l matches execute
bits, and skips NULL environment entries when sanitizing a setuid exec.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-08-18 15:57:52 +08:00
Jukka Laitinen
a3f733d387 fs/shm: Add a flag FS_SHMFS_NO_ALIGN to remove SHM alignment
This can be used on small systems to save RAM if the SHM object
doesn't need to be cache-aligned.

Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
2026-08-18 09:45:40 +02:00
Lwazi Dube
ff6597806d fs/vfs/fs_read.c: Allow NULL iov_base when CONFIG_ARCH_TEXT_VBASE == 0
For kernel builds where CONFIG_ARCH_TEXT_VBASE is set to 0, allow a NULL
buffer in file_readv() to prevent ELF binary loading failures for
binaries located at address 0.

This fix was originally introduced in #18830, but was inadvertently
reverted by someone unaware that platforms with CONFIG_ARCH_TEXT_VBASE
equal to 0 cannot function at all without it. This commit restores the
necessary check to prevent regressions in zero-based text kernel
configurations. Most platforms remain completely unaffected since only
about 5 boards utilize a text virtual base of zero.

Signed-off-by: Lwazi Dube <lwazeh@gmail.com>
2026-08-16 16:19:23 -03:00
Alan Carvalho de Assis
c1891e07c9 fs: resolve a trailing lone '.' path component
inode_nextname() already skipped a '.' segment mid-path (e.g. "./foo"),
but only checked for a '/' right after it -- a path ending in a bare
'.' (e.g. "/foo/.", or "." itself once AT_FDCWD resolution prepends
$PWD) fell through and was looked up as a literal child named ".",
which no real node is ever named, failing with ENOENT.

This broke every "operate on the current directory" idiom relative
paths rely on: bare `ls`, `stat .`, `cd .`, etc., all failed outright
even though the equivalent absolute path worked fine. Found while
testing the Toybox port's interactive REPL, but this is generic VFS
path resolution, not Toybox-specific.

Signed-off-by: Alan C. Assis <acassis@gmail.com>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 10:20:52 +08:00
Abhishek Mishra
1014c49881 sched: add supplementary group IDs (setgroups/getgroups/initgroups)
Track supplementary GIDs per task group, wire setgroups/getgroups
syscalls when CONFIG_SCHED_NGROUPS > 0, and honor them in DAC checks
via nxsched_has_gid().  When NGROUPS is 0, libc provides getgroups/
setgroups stubs.  initgroups() fails instead of silently truncating
when membership exceeds CONFIG_SCHED_NGROUPS.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-08-12 16:06:03 -03:00
alexcekay
a450392da5 fs/cromfs: Fix stale cache read in read() fast path.
cromfs_read()'s fast path decompresses a block directly into the
caller's buffer whenever a read reaches a block at its start and the
caller has room for the whole decompressed block, bypassing the
per-file decompression cache (ff_buffer). It nonetheless marked that
block as cached by setting ff_offset, without ever writing ff_buffer
itself.

A later read of the same block that fell onto the slow path trusted
that false cache tag, skipped decompression, and copied from
ff_buffer without it ever having been populated for that block. A
repeated identical fast-path read of the same block hit the same
false tag and skipped decompression entirely, leaving the caller's
buffer untouched and returning whatever was already there.

Fixed by having the fast path only read the cache, never populate it:
reuse ff_buffer when a prior slow-path read already cached the same
block, otherwise decompress straight into the caller's buffer without
touching ff_offset/ff_buffer.

Co-authored-by: Pavlo
Assisted-by: Claude Code:claude-sonnet-5
Signed-off-by: alexcekay <alexander@auterion.com>
2026-08-12 12:12:46 -03:00
raiden00pl
45c75f3bda fs/romfs: fix node cache overflow in directories with >256 entries
romfs_cachenode() tracked the allocated size of rn_child in a uint8_t
while rn_count is a uint16_t. Past 256 entries the size wraps to zero,
the grow condition rn_count == num - 1 can never be true again and the
array is not reallocated: entries are written beyond the allocation,
corrupting the heap.

Track the allocated size in a size_t.

Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
2026-08-11 10:53:19 +08:00
Alan Carvalho de Assis
87260499e1 cmake: Use NUTTX(_DIR/_BIN_DIR) instead CMAKE(_SRC_DIR/_BIN_DIR)
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
This change fixes NuttX’s CMake support when NuttX is embedded
in another project via add_subdirectory(). CMake’s CMAKE_SOURCE_DIR
and CMAKE_BINARY_DIR refer to the outermost project, causing NuttX
to access its .config, generated files, host tools, and build artifacts
in the parent project’s directories. The fix introduces NUTTX_DIR and
NUTTX_BINARY_DIR, based on CMAKE_CURRENT_SOURCE_DIR and
CMAKE_CURRENT_BINARY_DIR, and consistently uses them for NuttX
self-references while preserving existing standalone builds. It fixes
the Kconfig initialization failure reported in #19697 and allows an
embedded sim:nsh build to configure, build, and boot successfully.
The change affects only the CMake build system (not Make or Kconfig
defaults), requires the corresponding nuttx-apps change, and does not
extend add_subdirectory() support to cross-compiled non-sim boards due
to CMake’s toolchain-file limitation.

Fixes #19697.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Alan Carvalho de Assis <acassis@gmail.com>
2026-08-09 11:13:08 -03:00
Justin Hammond
8f23897d03 fs/fat: Restore the no-short-name marker when the name will not shorten.
fat_path2dirname() marks a parsed name as needing long file name
entries by clearing the first byte of the short name buffer, and
fat_dirnamewrite() writes the long name entries only while that marker
survives.  Since commit bc9e1ffb01, a name short enough to fit the 8.3
form is speculatively re-parsed as a short name, and the re-parse
fills the short name buffer with spaces before it examines a single
character.  When it then rejects the name (lower case, for example) the
spaces stay behind, the marker is gone, and the file is created with
eleven spaces for a name: no long name entries, a blank alias.

Every such file aliases to every other, since every rejected name
converts to the same blank entry.  Create a.txt, then create big1, and
both names now open one file; a directory of them lists as a single
nameless entry.  Any application that writes two lowercase short-named
files and reads the first back gets the second's contents.

Restore the marker when the speculative parse fails.

Tested on FAT32 with CONFIG_FAT_LFN: lower case, upper case, mixed
case and over-length names now create distinct, correctly named
entries that survive unmount and reboot; upper case 8.3 names still
produce plain short entries with no long name chain.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-08 10:05:43 +02:00
ywhkkx
1ceda8c02f fs/pseudofile: fix buffer sizing and sparse growth.
Some checks are pending
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Grow the in-memory pseudofile buffer by doubling instead of
1<<LOG2_CEIL, which can under-allocate on 32-bit targets for large
expand sizes. Also:
* reject size_t wrap before expand on write (-EFBIG)
* clear newly addressed bytes when the file grows
* route truncate growth through the same expand path

Impact: CONFIG_PSEUDOFS_FILE expand/write/truncate only; no API or
build-system change.

Testing: host arithmetic PoC blocked; WSL sim:pseudofile-poc
(SIM_M32+KASAN) write returns -ENOMEM instead of SIGSEGV in memcpy.

Signed-off-by: ywhkkx <2076064543@qq.com>
2026-08-07 10:49:43 -04:00
Abhishek Mishra
08afc5f2cf fs: enforce path search and unify inode permission helpers
Require X_OK on path ancestors and mountpoint gates via
inode_checkpathperm(), and use inode_checkperm() for inode mode checks.
inode_checkpathperm() takes the inode tree read lock (or INODE_CHECK_LOCKED
when the caller already holds it) and can fold a non-zero amode check onto
the target inode in the same call.

When CONFIG_FS_PERMISSION is disabled, inode_checkperm/inode_checkpathperm
are empty macros returning 0 so there is no stub .text cost.

Skip mountpoint R/W open-mode checks in inode_checkopenperm(); keep
traversal separate from open.  Add an optional mountpt_operations.permission
hook (tmpfs when CONFIG_FS_PERMISSION) for in-volume DAC without making the
VFS mount-crossing path depend on it.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-08-02 18:48:40 -03:00
Abhishek Mishra
db6ab892a2 fs/binfmt: close symlink TOCTOU and harden setuid/setgid exec hygiene
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
Perform pseudo-filesystem permission checks inside inode_reserve() and
inode_remove() while the inode tree lock is held, and hold that lock across
pseudorename mutations so symlink swaps cannot bypass directory checks.
Hold a read lock around pseudo-fs open permission checks.

On setuid/setgid exec, update saved set-IDs, mark the task group secure,
sanitize dangerous environment variables, clear debug/dumpable flags, and
add issetugid(), secure_getenv(), and PR_SET/GET_DUMPABLE support.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-08-01 15:32:03 -03:00
Abhishek Mishra
fe28fb033c fs: enforce permission checks when opening IPC pseudo-inodes
Use inode_checkopenperm() for message queues, named semaphores, and
shm, and reallocate mqueue state when reopening after the last close.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-07-30 09:48:10 +08:00
Abhishek Mishra
4bbe61ead5 fs/inode: assign owner and group when reserving pseudo-inodes
Set i_owner and i_group from the caller's effective credentials in
inode_reserve(), covering IPC objects, FIFOs, and pseudo-files.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-07-30 09:48:10 +08:00
Marco Casaroli
a104fb8047 fs/mount: Name xipfs in statfs and fs_gettype.
A file system that answers statfs with a magic nothing maps to shows up as
"Unrecognized" in df.  Give xipfs its constant alongside the others in
sys/statfs.h and the case in fs_gettype that turns it into a name.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-07-29 07:49:03 -03:00
Marco Casaroli
e0cc0244c5 fs/xipfs: Add a contiguous execute-in-place file system.
ROMFS is the usual way to carry executables on a NOMMU target with memory
mapped NOR flash: it can hand out a real flash pointer from mmap(), so the
NXFLAT loader maps a module's text in place instead of copying it into RAM.
But a ROMFS image is built on the host and is read only, so a module cannot
be downloaded onto the board at run time.

xipfs is a writable file system with the same in-place property.  Each file
is stored as one physically contiguous, erase-block aligned extent, so an
mmap() of it resolves to flash_base + extent_offset and a loader can execute
the file where it already lies.  This needs the underlying MTD driver to
answer BIOC_XIPBASE; on the RP2350 rp23xx_flash_mtd.c does.

Files are write once.  A file is created, its size is declared, it is
written sequentially, closed, and is thereafter immutable until it is
deleted.  That is the whole life cycle of a downloaded module, and it is
what licenses the design: the exact extent is reserved at create time, so
no file ever grows, moves, or fragments internally.  Random writes, appends
and truncation of a written file are not supported and are refused.

The only source of fragmentation is therefore free space holes left by
deletes.  Allocation fails with -ENOSPC when no single contiguous run is
large enough, and never defragments on its own; the caller decides whether
to compact and retry, through XIPFSIOC_DEFRAG.  Defragmentation is manual,
best effort and interruptible: it is a loop of atomic single-extent
relocations, each one copy, commit, erase, so every stop point -- a time
budget, a pinned extent, an erase error -- leaves a consistent layout that
is simply less compact.  It reports the largest contiguous run it achieved,
which is what tells the caller whether the retry will fit.

Metadata is committed power safely.  Two metadata block sets are used in
ping-pong, each generation carrying a sequence number and a CRC, and every
state change is ordered as write the new data, flip the metadata reference,
then erase what the old one referenced.  Mount scans both sets and selects
the last fully valid generation, so a torn write costs the interrupted
operation and nothing else.

A mapping takes a pin on the extent, and the pin lives on the extent rather
than on the file descriptor, so three running instances of one module hold
three pins and the extent becomes movable only when the last one goes.
Defragmentation skips pinned extents, which is what stops it relocating
code that is executing.  The pin is released by munmap() or by the task
teardown walk, so a task that dies without unmapping does not leak it.

Directories are records in that same generation, carrying their own identity
and the identity of the directory holding them; the root is implicit and owns
identity zero.  They are deliberately NOT objects in the data region, which
is what keeps the commit story in one piece: mkdir and rmdir add or remove a
record and commit one generation, exactly as create and unlink do, so there
is never a multi-object update to journal or an orphan to collect at mount.
An empty directory therefore exists, survives a remount, and costs one entry
out of the volume's fixed supply and no flash blocks at all.

A name is one path component; depth comes from the parent, so XIPFS_NAME_MAX
bounds a component, which is what statfs reports it as.  Mount rebuilds the
tree and checks that it is one: identities unique, names unique within a
directory, every parent a live directory, and following parents reaching the
root -- a cycle on the medium would otherwise hang a path walk rather than
merely answering wrongly.  '.' and '..' are refused as components, since an
entry stored under either could never be reached again.

The commands that act on the volume rather than on one file --
XIPFSIOC_DEFRAG and XIPFSIOC_LISTPINNED -- are reached through the ioctldir
method, on a descriptor for the mountpoint directory.  They are accepted on
a descriptor for a file inside the volume too, but that route holds the file
open for the duration and an open extent cannot be relocated, so a pass
asked for that way is obstructed by the act of asking.

mmap() falls back to the generic RAM copy for ordinary readers when the
media cannot be addressed directly.  A module loader must not silently get
a RAM copy, so MAP_XIP_STRICT is added: with it the mapping either resolves
in place or fails with -ENXIO, which the caller can turn into defragment
and retry.

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-07-29 07:49:03 -03:00
Alan Carvalho de Assis
c027e7c3e4 tools: fix stale archive members surviving a Kconfig-driven CSRCS change
During the Toybox port to NuttX, Claude noticed that changes in the
menuconfig weren't taking affect. This issue exists for a long time on
NuttX, in fact BayLibre's presentation from 2017 make jokes about our
building system not been reliable:
https://www.youtube.com/watch?v=XUJK2htXxKw&t=320s

Stale archive members from $(AR)'s additive-only behavior can linger
after Kconfig toggles change which files provide a symbol, causing dead
weight or "multiple definition" link errors on incremental builds.
Fixed by splitting ARCHIVE into two macros: ARCHIVE keeps the original
additive behavior for apps/libapps.a, which many independent
subdirectories contribute to across a build, while the new
ARCHIVE_REBUILD deletes then archives for the far more common case
of a single Makefile building its own self-contained $(OBJS)
- all 39 such call sites now use it.

Assisted-By: Claude Sonnet 5
Signed-off-by: Alan C. Assis <acassis@gmail.com>
2026-07-28 21:26:03 -03:00
Marco Casaroli
6da4269c46 fs/vfs: Add ioctldir for volume ioctls via the mountpoint directory.
FIOC_REFORMAT, FIOC_OPTIMIZE, FIOC_INTEGRITY and FIOC_DUMP act on a volume,
not on any one file, but the only route into a file system has been the
per-file ioctl method.  A caller therefore has to open an unrelated file
just to name the volume it means.

For nxffs that is not merely awkward, it is a dead end.  nxffs_ioctl()
refuses FIOC_REFORMAT while any file on the volume is open:

    if (volume->ofiles)
      {
        ferr("ERROR: Open files\n");
        ret = -EBUSY;

and every open file is on that list (nxffs_open.c).  The descriptor used to
issue the command is itself such a file, so the check can never pass and
FIOC_REFORMAT is unreachable through the only interface that exposes it.

Add an optional ioctldir method to struct mountpt_operations, reached by
issuing the ioctl on a descriptor for the mountpoint directory:

    fd = open("/mnt/nxffs", O_RDONLY | O_DIRECTORY);
    ioctl(fd, FIOC_REFORMAT, 0);

It takes the same (mountpt, dir) pair as opendir/readdir/rewinddir, so it
reads as a member of the directory-operations family; the file system
recovers the volume from the mountpoint inode and may ignore dir.  The
member is placed at the end of the structure rather than beside the other
directory operations on purpose: every file system initialises
mountpt_operations positionally, so a member inserted mid-structure would
force all of them to add a slot for a method they do not implement.
Appending keeps the change to one file system.

dir_ioctl() gives that method the first chance at every command when the
directory belongs to a mounted volume and the file system provides one, and
falls back to its own handling of FIOC_FILEPATH and BIOC_FLUSH when the file
system answers -ENOTTY.  Trying the file system first is what lets a file
system override a command the VFS would otherwise answer generically; the
-ENOTTY fallback is what keeps the generic answers available to everyone
else.  A file system that leaves the method NULL is unaffected: the VFS
answers exactly as before.

The existing per-file method could not simply be reused for this.  It takes
a struct file, and every implementation that has an ioctl -- fat, romfs,
tmpfs, spiffs among them -- asserts on filep->f_priv and dereferences it,
so handing it a directory descriptor with no open file behind it would
fault.  Making the entry point separate keeps that contract intact and
makes support explicit rather than assumed.

nxffs implements it, which is what makes its FIOC_REFORMAT reachable.  The
per-file path is left in place and both share one implementation, so
nothing that works today stops working.  spiffs, which has the same shape
of volume commands, can follow.

Measured on sim:nxffs, with one file written to the volume and then the
same sequence of ioctls issued on a file descriptor, on a descriptor for the
mountpoint directory, and on a descriptor for a pseudo file system directory.
Before:

    FIOC_REFORMAT via file fd:  ret=-1 errno=16 (EBUSY, as expected)
    FIOC_REFORMAT via dir fd:   ret=-1 errno=25
    FIOC_FILEPATH via dir fd:   ret=0  "/mnt/nxffs//"
    BIOC_FLUSH    via dir fd:   ret=0
    bogus cmd     via dir fd:   ret=-1 errno=25
    FIOC_FILEPATH via /dev fd:  ret=0  "/dev//"
    bogus cmd     via /dev fd:  ret=-1 errno=25
    name still in the raw MTD image afterwards: yes

After:

    FIOC_REFORMAT via file fd:  ret=-1 errno=16 (EBUSY, as expected)
    FIOC_REFORMAT via dir fd:   ret=0
    FIOC_FILEPATH via dir fd:   ret=0  "/mnt/nxffs//"
    BIOC_FLUSH    via dir fd:   ret=0
    bogus cmd     via dir fd:   ret=-1 errno=25
    FIOC_FILEPATH via /dev fd:  ret=0  "/dev//"
    bogus cmd     via /dev fd:  ret=-1 errno=25
    name still in the raw MTD image afterwards: no

Only the FIOC_REFORMAT line on the directory descriptor changes, and the raw
MTD image confirms the volume really was erased.  FIOC_FILEPATH and
BIOC_FLUSH on a directory still answer even though nxffs is now consulted
ahead of them, an unrecognised command is still refused rather than
forwarded blindly, and a directory in the pseudo file system, which has no
ioctldir at all, is untouched.

Assisted-by: Claude Code:claude-opus-4-8
Assisted-by: Claude Code:claude-fable-5
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-07-25 07:28:22 -03:00
Lingao Meng
6fa47d6de4 fs/hostfs: Use configured path length
hostfs keeps its own HOSTFS_MAX_PATH wrapper for internal buffers, but
it should not hard-code a path length separate from the system path
configuration.

Define HOSTFS_MAX_PATH from PATH_MAX instead. PATH_MAX is backed by
CONFIG_PATH_MAX, whose default remains 256, so the default hostfs
behavior does not change while configurations that choose a larger path
limit are honored consistently.

Assisted-by: Claude:Claude-Fable-5
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2026-07-23 15:34:02 +08:00
Lingao Meng
778f7f00cb fs/hostfs: Fix long root path construction
hostfs_mkpath() appends a relative path to the configured host root
with strlcat(). The third argument to strlcat() is the total
destination buffer size, not the remaining free space.

Passing pathlen - strlen(path) makes the effective limit shrink after
a long host root has already been copied. With a sufficiently long
root, a valid relative path can be dropped or truncated, so operations
under the mount point may resolve to the host root instead of the
requested child path.

Pass the full destination buffer size and let strlcat() account for the
current string length internally.

The companion examples/hostfs_longpath app validates this regression by
mounting hostfs with a long host root, writing a probe file below the
mount point, and reading it back. The old size argument drops the
relative component in that scenario; this fix preserves it.

Assisted-by: Claude:Claude-Fable-5
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2026-07-23 15:34:02 +08:00
Lingao Meng
18c462ff20 fs/hostfs: Handle POSIX byte-range locks
fcntl(F_GETLK/F_SETLK/F_SETLKW) is handled by VFS and reaches file
systems as private FIOC_* ioctl commands. hostfs previously forwarded
those private ioctl command numbers to the host ioctl backend, which is
not the POSIX file-locking interface and cannot be interpreted by the
host OS.

Keep hostfs on the generic host_ioctl() path and define the FIOC_* lock
command values in the hostfs host ABI. The POSIX sim backend recognizes
those commands in host_ioctl() and translates struct flock fields to the
host ABI before calling host fcntl(). Other hostfs backends keep their
existing unsupported-host-ioctl behavior.

F_SETLKW is implemented in the POSIX sim backend by retrying
non-blocking host F_SETLK with a short sleep. This preserves the
blocking NuttX API without forwarding host F_SETLKW directly.

Testing:

  - Host: Ubuntu 22.04 x86_64.

  - Board/config: sim:nsh with CONFIG_FS_HOSTFS=y,
    CONFIG_SIM_HOSTFS=y and CONFIG_EXAMPLES_SIM_POSIX=y.

  - make -j16.

  - Ran examples/sim_posix from nuttx-apps. The test mounted a long
    /tmp hostfs path, opened a host-backed file, and verified
    fcntl(F_SETLK), fcntl(F_GETLK), fcntl(F_SETLKW), and unlocking with
    F_UNLCK. The app printed "sim_posix: hostfs locks ok" and
    "sim_posix: PASS".

Assisted-by: Claude:Claude-Fable-5
Signed-off-by: Lingao Meng <menglingao@xiaomi.com>
2026-07-23 15:34:02 +08:00