Commit graph

2594 commits

Author SHA1 Message Date
Royyan Zahir
612fcb5c5a fs/mmap: back user anonymous mappings with pages in a kernel build
The kernel ran the process's own heap allocator on metadata the process
controls, and at teardown freed it from whatever address space was
current. User anonymous mappings are now fresh pages mapped into the
process, as the REVISIT asked.

Signed-off-by: Royyan Zahir <royzah@gmail.com>
2026-10-09 20:44:44 +08:00
Royyan Zahir
e55c073799 fs: raw storage needs PR_CAP_RAWIO
Opening a block or MTD node, mount() and umount2() need it, and so does
a BCH character node, which checks in its own open(): a node made by
bchdev_register() is a character driver, so the inode type cannot tell
it apart. The checks sit in file_vopen(), nx_mount() and nx_umount2(),
which every path reaches; kernel threads hold every capability.

Signed-off-by: Royyan Zahir <royzah@gmail.com>
2026-10-09 09:31:21 -03:00
Marco Casaroli
9eb725fe9b fs/v9fs: Set st_ino and d_ino from the qid path.
stat() returned st_ino 0 and readdir() returned d_ino 0 for every file
on a v9fs mount, although 9P identifies each file by its qid path.
Programs that skip directory entries with d_ino 0, or that compare
st_dev and st_ino to find out if two paths are the same file, do not
work on such a mount.

Fold the 64-bit qid path into ino_t the same way for both, so that they
match, and never return 0.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-09 06:12:58 -03:00
Marco Casaroli
3619c557a6 fs/v9fs: Report the end of a directory as the end, not as an error.
At the end of a directory the 9P server returns no entries.
v9fs_client_convertdir() then failed with -EIO, so readdir() returned
NULL with errno set to EIO at the end of every directory.  A program
that checks errno after readdir() reports an I/O error.

Return -ENOENT when the server returns no entries.  The VFS turns that
into a clean end of directory.  Also check the space for the fixed part
of an entry against the bytes left after head, not against the whole
buffer.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-09 06:12:58 -03:00
Marco Casaroli
97fc2b0c56 fs/v9fs: Add a blank line after a declaration.
nxstyle reports a missing blank line after a declaration in
v9fs_vfs_ioctl().  No functional change.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-09 06:12:58 -03:00
Marco Casaroli
7e6c2eaf00 fs/inode: Name the inode tree lock in the comments, not g_inode_sem.
Three comments say that the caller of inode_search() and
_inode_linktarget() holds the g_inode_sem semaphore.  That semaphore no
longer exists.  The caller holds the inode tree lock, from inode_lock()
or inode_rlock().  No code change.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-08 15:50:03 -03:00
Marco Casaroli
8308e4b782 fs/v9fs: Do not copy the comma after the virtio tag.
virtio_9p_create() copies the tag from "tag=" to the next comma, and the
length it computes includes the comma.  So a tag that is not the last
option never matches the mount tag of the device:

  nsh> mount -t v9fs -o tag=host,trans=virtio /mnt
  nsh: mount: mount failed: 19

Copy only the characters of the tag.  The allocation is zeroed, so one
more byte terminates it.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-08 13:29:48 +08:00
Marco Casaroli
506a69a03d fs/v9fs: Do not parse past the end of the mount options.
v9fs_client_init() steps over each option with "options += length + 1",
to skip the comma after it.  The last option has no comma, so the step
goes past the terminating NUL, and the loop parses the memory after the
string as more options.  If that memory has a "trans=" or "uname=", it
replaces the option given.

For example, NSH keeps the next argument after the options:

  nsh> mount -t v9fs -o trans=virtio,tag=host trans=x /mnt
  nsh: mount: mount failed: 2

The parser reads "trans=x", and there is no transport "x".  The same
thing happens to options in .rodata, as CONFIG_INIT_MOUNT_DATA is.

Skip the comma only when there is one.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-08 13:29:48 +08:00
Marco Casaroli
58bd942b35 fs/v9fs: Fix the nxstyle errors in client.c and virtio_9p.c.
nxstyle reports "Missing blank line after declarations" in three places.
Add the blank lines, because CI checks every file that a change touches.

No functional change.  The change is whitespace only.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-10-08 13:29:48 +08:00
Abhishek Mishra
c7cbee7a33 fs/smartfs: mask reserved flags from stat mode.
SMARTFS_DIRENT_RESERVED uses bits that overlap S_ISUID, S_ISGID, and S_ISVTX. smartfs_stat_common() currently clears only S_IFMT, allowing reserved directory-entry flags to appear in st_mode and ls output as setuid and setgid bits. Preserve only SMARTFS_DIRENT_MODE when constructing st_mode.

Assisted-by: GPT-5.6 Sol
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-10-07 13:28:02 -03:00
rongbaichuan
00a379c3f7 sched/semaphore: Fix pre-existing nxstyle issues in the touched files
The CI style check runs nxstyle over every file a pull request touches,
so the files changed by the previous two commits have to comply even
where the problems were not introduced here.  504 errors in 25 files are
fixed: whitespace, blank lines, brace placement, switch/case indentation,
label indentation and comment blocks only, with no functional change.

Assisted-by: DeepSeek Harness:deepseek-flash
Signed-off-by: rongbaichuan <rongbaichuan1027@163.com>
2026-09-25 10:37:43 +02:00
rongbaichuan
59d5ce0f31 sched/semaphore: Remove the return value check of nxsem_init/nxmutex_init
nxsem_init(), nxsem_destroy(), nxmutex_init() and nxmutex_destroy()
always return OK, so checking the result only leaves dead code: the
compiler cannot remove it, because these are cross-translation-unit calls
and the nxrmutex_destroy() test is duplicated into every inlined call
site.

Apply the convention already established in commit a47a36bc5b (PR #7473)
to the two definitions which still test the value and to the 54 remaining
call sites. No signature or prototype is changed.

Testing: stm32f103-minimum:nsh builds with -Os without new warnings.

Assisted-by: DeepSeek Harness:deepseek-flash
Signed-off-by: rongbaichuan <rongbaichuan1027@163.com>
2026-09-25 10:37:43 +02:00
Abhishek Mishra
4b86c1dd23 docs: document chroot jail root
Describe the jail, leftover pre-opened fds, the NSH command-form scrub,
and the flat-build trust boundary shared with credentials.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-09-20 22:27:38 +08:00
Abhishek Mishra
2977db2632 fs: add chroot() syscall
Add CONFIG_FS_CHROOT and POSIX chroot(). Store the jail as an
absolute path on the task group, and require euid 0 when user
identity is enabled.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-09-20 22:27:38 +08:00
Abhishek Mishra
a0adad6602 fs: start absolute lookups at the jail root
Prepare paths in inode_search_setup(): prepend tg_root, canonicalize
with a jail-floor dst_min, then walk from g_root_inode. Replace
SETUP_SEARCH / RELEASE_SEARCH with inode_search_setup() /
inode_search_release().

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
2026-09-20 22:27:38 +08:00
AlmAck
20752312ea fs/inode: bound fdlist_extend() against the requested row
fdlist_extend() grows a task group's descriptor table to 'row' rows of
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK entries each, and guards the growth
against OPEN_MAX:

  if (CONFIG_NFILE_DESCRIPTORS_PER_BLOCK * (orig_rows + 1) > OPEN_MAX)

The check sizes the table at orig_rows + 1, which assumes the caller
only ever grows by a single block.  The function then allocates 'row'
rows, so the two agree only for growth by one.

Callers do skip ahead.  fdlist_dup3() asks for
fd2 / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + 1, fdlist_dupfile() for the
row holding minfd, and fdlist_copy() for the row holding a parent
descriptor it is duplicating.  Any of those can request a row well past
orig_rows + 1.

Such a request passes the check and the function then allocates and
installs a table with more than OPEN_MAX descriptors.  With the defaults
(8 per block, OPEN_MAX 256) a process holding one row that calls
dup2(fd, 400) ends up with 51 rows, or 408 descriptor slots, against a
256 limit.

Check the row actually being requested.  For single-block growth
row == orig_rows + 1 and the comparison is unchanged.

Signed-off-by: AlmAck <gluca86@gmail.com>
2026-09-17 13:50:27 +08:00
Xiang Xiao
d5d134bc79 fs/aio: raise the default AIO_LISTIO_MAX so LTP keeps passing
The new CONFIG_FS_AIO_LISTIO_MAX option defaults to 10 and lio_listio()
now rejects nent > {AIO_LISTIO_MAX} with EINVAL.  The LTP release pinned
by apps/testing/ltp (20230516) submits 256 requests in a single batch from
conformance/interfaces/lio_listio/2-1.c, so ltp_interfaces_lio_listio_2_1
now fails on every configuration that enables CONFIG_TESTING_LTP
(sim:citest, rv-virt:citest, sim:posix_test):

  lio_listio/2-1.c Error at lio_listio() 22: Invalid argument

The EINVAL check itself is required by POSIX, so keep it and raise the
default instead; the limit no longer costs memory because the requests are
linked through the aiocb's own lio_link.

While here, keep _POSIX_AIO_LISTIO_MAX at its POSIX-mandated value of 2
and let AIO_LISTIO_MAX carry the configurable implementation limit.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
74d2c2d119 fs/aio: use list_clear_node() to mark non-batch requests
aio_fsync()/aio_read()/aio_write()/lio_listio() initialized
aiocbp->lio_link with list_initialize(), which makes the node
self-referential (prev = next = &node).  aio_signal() tests
list_in_list(&lio_link) to detect lio_listio batches, so it wrongly
entered the lio_listio completion path for every standalone AIO
operation and notified through the uninitialized
lio_sigevent/lio_sigwork.

With CONFIG_SIG_EVTHREAD=y, garbage lio_sigevent.sigev_notify ==
SIGEV_THREAD caused nxsig_notification() to queue &lio_sigwork.work
onto the low-priority work queue with garbage func/value.  After the
aiocb was freed, the dangling work_s was dispatched with worker=NULL,
crashing in work_dispatch().

Fix: initialize lio_link with list_clear_node() (prev = next = NULL)
so list_in_list() returns false for non-lio_listio operations and
aio_signal() skips the lio_listio path.

While there, reject a NULL aiocbp in aio_fsync(): POSIX Issue 6 no
longer defines a NULL special case, and the old DEBUGASSERT() panicked
debug builds.

Co-developed-by: dengwenqi <dengwenqi@xiaomi.com>
Co-developed-by: fangxinyong <fangxinyong@xiaomi.com>
Signed-off-by: fangxinyong <fangxinyong@xiaomi.com>
Signed-off-by: dengwenqi <dengwenqi@xiaomi.com>
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
3b3e97e4a8 fs/aio: add internal aio_read/aio_write to avoid lio_link overwrite
lio_listio() links each aiocbp->lio_link into its batch list before
submitting the I/O, but submitted the operations through the public
aio_read()/aio_write(), which re-initialized lio_link and destroyed
the list membership.  With an aiocb pre-filled with garbage (as in
ostest), the completion path then walked an invalid list.

Extract aio_read_internal()/aio_write_internal() that skip the
lio_link setup; aio_read()/aio_write() initialize lio_link (and
reject a NULL aiocbp) before calling the internal functions, while
lio_listio() calls the internal functions directly to preserve its
own lio_link setup.  For entries that are not part of a batch,
lio_listio() self-initializes lio_link instead.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
7142c0a19e fs/aio: initialize lio_link in aio_fsync()
aio_fsync() never initialized aiocbp->lio_link, but the reworked
aio_signal() tests list_in_list(&lio_link) on every completion.  With
an uninitialized (or zero-filled) lio_link the behavior was
unpredictable; initialize the node so standalone fsync operations are
self-consistent.

Signed-off-by: tengshuangshuang <tengshuangshuang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
4cec501584 fs/aio: fix aio_read/aio_write return values per POSIX
Per POSIX, aio_read() and aio_write() must return -1 and set errno to
EINVAL when the request cannot be queued (aio_reqprio < 0,
aio_offset < 0), and the error must also be retrievable via
aio_error().  Conversely, when queuing fails with a bad file
descriptor, the error belongs to the asynchronous operation: the
functions must return 0 and report EBADF through aio_error().

- Merge the offset/reqprio checks and return ERROR with errno set,
  after storing the result in aio_result for aio_error().
- Drop the aio_fildes < 0 early return: a closed descriptor is now
  caught by fcntl()/aio_queue() and reported through aio_result with
  the function returning OK.
- aio_error(): report -EINVAL (failed validation) through errno
  instead of returning it as an error value.

Signed-off-by: tengshuangshuang <tengshuangshuang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
b7d6c8ff41 fs/aio: fix aioc use-after-free and aio_cancel() issues
aioc_decant() frees the AIO container and detaches the aiocbp.  The
I/O workers (aio_read_worker, aio_write_worker, aio_fsync_worker)
called it before signaling completion, so aio_signal() and any code
touching the container afterwards ran on freed memory.  Additionally,
if the caller closed the file early the detached container could be
reused with a stale file reference.  Move aioc_decant() to after
aio_signal() and use aioc->aioc_aiocbp directly in the workers.

aio_cancel() also had two problems: with no aiocbp it looped over
g_aio_pending with a do/while that skipped the list re-entry check, so
a failed work_cancel() on an already running I/O caused an endless
loop; and an invalid fildes only checked 'fildes < 0' instead of
validating the descriptor, so a closed fd was not reported as EBADF.
Use a for-loop that always advances and validate the descriptor with
file_get()/file_put().

Co-developed-by: wushenhui <wushenhui@xiaomi.com>
Signed-off-by: wushenhui <wushenhui@xiaomi.com>
Signed-off-by: tengshuangshuang <tengshuangshuang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
2f4d017bb6 fs/aio: add configurable AIO_LISTIO_MAX limit
lio_listio() never validated 'nent' against {AIO_LISTIO_MAX}, so a
batch larger than the documented limit was silently accepted, and the
hard-coded _POSIX_AIO_LISTIO_MAX value of 2 was too small for real
workloads (LTP uses 10 entries per call).

Add the FS_AIO_LISTIO_MAX Kconfig option (default 10), use it for
_POSIX_AIO_LISTIO_MAX in include/limits.h, validate 'nent' in
lio_listio(), and report the limit through sysconf(_SC_AIO_LISTIO_MAX).

Signed-off-by: tengshuangshuang <tengshuangshuang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
1ea86e65fd aio: make the lio_listio() prototype match POSIX
POSIX declares lio_listio() as:

  int lio_listio(int, struct aiocb *restrict const [restrict], int,
                 struct sigevent *restrict);

Update the prototype in include/aio.h (and the implementation and
libc.csv entry) accordingly, and drop the parameter names from the
other aio_* prototypes for consistency.

Signed-off-by: guoshichao <guoshichao@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
d2489101ac fs/aio: skip lio_link teardown for failed submissions in LIO_WAIT mode
When a queued operation fails immediately (bad fd, EINVAL, or a failed
aio_read/aio_write submission), lio_listio() unconditionally deleted
the aiocbp from the request list.  In LIO_WAIT mode (or when no sig was
requested) the lio_link nodes were never linked into the list, so
list_delete() corrupted memory and crashed.

Only unlink the node when it was actually linked, i.e. when
mode == LIO_NOWAIT and a sigevent was provided.

Signed-off-by: tengshuangshuang <tengshuangshuang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
2466219100 fs/aio: guard against all-NULL aiocb lists in lio_listio()
When lio_listio() is called with LIO_NOWAIT and a non-NULL sig, and no
I/O could be queued (or all entries are LIO_NOP/NULL), the completion
notification dereferences a NULL aiocbp picked from an empty iteration,
crashing nxsig_notification().

Scan the list for any non-NULL entry before delivering the
notification, and skip it entirely when the list contains only NULL
entries.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
ad364be818 fs/aio: rework lio_listio() with a lock-protected request list
Previously, lio_listio() called aio_read()/aio_write() to submit the
I/O and only then initialized the per-request notification state
(aio_priv based), so a worker thread could complete an operation before
that state was set up (thread-unsafe), and the completion notification
hijacked the per-request sigevent machinery.

Rework the implementation: lio_listio() now links every aiocb of the
batch into a list (lio_link) before any I/O is submitted.  When an
operation completes, aio_signal() removes its node from the list under
aio_lock() and delivers the lio_listio completion notification only
when the list becomes empty.  The unused aio_priv field is replaced by
the lio_link/lio_sigevent/lio_sigwork fields in struct aiocb.

Co-developed-by: wushenhui <wushenhui@xiaomi.com>
Signed-off-by: wushenhui <wushenhui@xiaomi.com>
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Xiang Xiao
f35993c638 fs/aio: move lio_listio() to fs/aio
lio_listio() submits I/O through the internal aio_read/aio_write
helpers and is only built when CONFIG_FS_AIO is enabled.  Keeping it in
libs/libc splits one subsystem across two directories and forces fs/aio
to export internal interfaces to the libc build.

Move the file (and its two build system entries) from libs/libc/aio to
fs/aio so that the whole AIO implementation lives in one place.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2026-09-14 17:12:49 -03:00
Arnav Sharma
b13be31798 fs/fat: fix nxstyle violations flagged by CI
Fix blank-line-after-declarations and switch-case alignment issues in fs/fat files touched by the unlink-while-open change, including pre-existing violations in the same regions. No functional change.

Signed-off-by: Arnav Sharma <2006arnavsharma@gmail.com>
2026-09-11 10:55:25 -03:00
Arnav Sharma
8048aa0f81 fs/fat: defer cluster-chain free for files unlinked while open
Add a FAT local canonical open-file object to maintain shared state between multiple open handles of the same file. When an open file is unlinked, remove its directory entry but defer freeing the FAT cluster chain until the last open reference is closed. The shared object maintains the file size, starting cluster, directory-entry location, reference count, and pending-delete state. This avoids identifying open deleted files solely by their cluster number and correctly handles empty files, multiple opens, dup(), rename, and directory or cluster reuse. Pending deleted files no longer write metadata back to a removed or reused directory entry, while fstat(), truncate(), sync(), and subsequent writes continue to operate on the shared in-memory state. The implementation is kept within fs/fat and does not introduce a generic VFS inode mechanism. Fixes: #20037

Signed-off-by: Arnav Sharma <2006arnavsharma@gmail.com>
2026-09-11 10:55:25 -03:00
Marco Casaroli
1aa32bbc07 libs/libc/elf: Place an FDPIC object's segments independently.
An ET_DYN object is loaded into one allocation with its data behind its
text, because its data references sit at a fixed distance from the code
that makes them.  An FDPIC object does not work that way: it reaches its
data through a base register, so the two segments can be placed wherever
suits, and the point of the format is that the read-only one is left on
the media and executed there while only the writable one is copied.  One
copy of the text then serves every instance.

So libelf_load() grows a second case.  The object announces itself in the
OS/ABI byte, which is noted once in libelf_loadhdrs() rather than
re-derived; e_flags cannot be used for this, as an FDPIC object's are an
unremarkable EABI version and testing them would reject every valid
module.  Text is taken from the media address plus the segment's own file
offset -- the same arithmetic the ET_REL path already does with
sh_offset -- and libelf_loadfile() does not read it.  If the filesystem
cannot show its media, the loader copies the text to RAM instead.  The
module then loses the shared text and the flash saving, but it runs.

Obtaining that address needs two mechanisms, and they are not
interchangeable.  A compacting filesystem can move a file's blocks, so it
hands out an address only with a pin that holds them still and expects
the pin back; xipfs is the one in tree.  A filesystem whose layout never
changes has nothing to hold and answers FIOC_XIPBASE with a bare address;
romfs and tmpfs are those.  libelf_xipacquire() asks for the pin first,
because a filesystem that needs one is not safe without it, and
libelf_unload() gives it back.  The loader asks for a pin only if it can
hold one, or the pin would stay for ever.

The pin is thus not specific to FDPIC.  Any module that executes in place
from a compacting filesystem takes one, and gives it back at unload.

mmap() is not used, though both filesystems implement it.  The mapping
would be recorded against whichever task called the loader, while the
release happens when the module's own task exits, which is a different
group -- so the pin would outlive the module and the extent would never
become movable again.

Unloading has to change with placement: the existing path frees only
textalloc because ET_DYN had a single allocation, which would leak an
FDPIC object's data and free media the filesystem only lent us.

Nothing here runs for a non-FDPIC object; every branch is behind the flag
and the single-allocation path is untouched.  Built and booted
mps3-an547:picostest, which is CONFIG_ELF with CONFIG_PIC, with no change
in behaviour.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
2026-09-08 16:31:16 -03:00
Hritik Naik
2a6a86cb68 fs/procfs: fix buffer overflow in mount_sprintf
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
vsnprintf() returns the total formatted string length even when truncated to info->line. Passing this untruncated length to procfs_memcpy causes a read beyond the 64-byte line staging buffer.

Fixes #20011

Signed-off-by: Hritik Naik <hritiknaik16@gmail.com>
2026-09-06 12:16:02 -03:00
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