fs/vfs: link() fails with ENOSYS instead of aliasing symlink#19210
Closed
FelipeMdeO wants to merge 1 commit into
Closed
fs/vfs: link() fails with ENOSYS instead of aliasing symlink#19210FelipeMdeO wants to merge 1 commit into
FelipeMdeO wants to merge 1 commit into
Conversation
NuttX VFS has no hard-link support; link() aliased symlink() and only under CONFIG_PSEUDOFS_SOFTLINKS, being absent otherwise. Always build fs_link.c and return -1/ENOSYS so callers can detect the missing support instead of getting a symlink or an undefined reference. Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The NuttX VFS has no hard-link support, yet
link()was implemented as a thinalias of
symlink():This is wrong in two independent ways, and fixing it correctly is why the
change is not a one-liner.
1. Incorrect POSIX semantics.
link()is defined to create a hard link.Silently creating a symbolic link instead is surprising and breaks callers
that rely on the distinction. In particular, the common portability pattern of
probing for hard-link support — call
link()and check for failure — was toldthe operation succeeded, having quietly created a symlink it never asked for.
The correct answer for a filesystem with no hard links is
-1/ENOSYS.2. The hard-link API was gated behind an unrelated feature. Returning
ENOSYSfrom the function body alone fixes nothing, because the whole offs_link.cwas compiled only underCONFIG_PSEUDOFS_SOFTLINKS. When soft linksare disabled,
link()did not exist at all — so editing its body changesnothing for those configurations;
link()simply remains an undefined symbol.Hard links and soft links are independent concepts, so gating
link()(hardlink) on
CONFIG_PSEUDOFS_SOFTLINKS(soft links) is a category error. Makinglink()reportENOSYSregardless of soft-link configuration thereforerequires decoupling it from that option.
That decoupling is a single logical change, but NuttX expresses the same build
condition in four parallel places, so the same
CONFIG_PSEUDOFS_SOFTLINKSguard had to be removed from each:
fs/vfs/fs_link.c#ifdefwrapping the function itselffs/vfs/Make.defsfs/vfs/CMakeLists.txtsyscall/syscall.csvThese are four copies of one condition, not four independent changes; leaving
the guard in any of them would make the tree inconsistent (e.g. compiled but
absent from the syscall table, or present in CMake but not in Make).
fs_symlink.candfs_readlink.cremain underCONFIG_PSEUDOFS_SOFTLINKS, asthey should — soft-link support is untouched (see Impact).
This was already a latent inconsistency, not just a cosmetic issue.
libs/libc/unistd/lib_linkat.cis built unconditionally and is implemented ontop of
link():So the tree already assumed
link()is always available — directlycontradicting the
CONFIG_PSEUDOFS_SOFTLINKSguard. Without soft links,linkat()referenced alink()that was never built. After this changefs_link.cis always compiled,link()always returns-1/ENOSYS, andlinkat()has a consistent, always-present backend.Impact
Soft-link support (
CONFIG_PSEUDOFS_SOFTLINKS) is fully preserved. Thischange does not touch soft links:
symlink()/readlink()remain gated by theoption and behave exactly as before. The two features are orthogonal —
CONFIG_PSEUDOFS_SOFTLINKSprovides symbolic links viasymlink(), whilelink()is the hard-link API. Enabling soft links should not, and now doesnot, change the behavior of the hard-link API. The only difference for a
CONFIG_PSEUDOFS_SOFTLINKS=ybuild is thatlink()no longer silently createsa symlink:
SOFTLINKS=y, beforeSOFTLINKS=y, aftersymlink()readlink()link()-1/ENOSYSA caller that wants a symbolic link must use
symlink()(the correct API,already available when the option is enabled); a caller of
link()wants ahard link, which NuttX supports in no configuration, so
ENOSYSis thecorrect result with or without soft links. No in-tree caller relied on the old
aliasing.
Other effects:
link()now exists and returnsENOSYSinstead of being an undefined reference, andlinkat()behavesconsistently in every configuration.
fs_link.cis now always compiled — a tinyENOSYSstub, no newdependencies and no Kconfig changes. No size impact beyond a few bytes.
link()is still declared in<unistd.h>; only its runtimebehavior changes, and it is now standards-compliant for a filesystem without
hard-link support.
CONFIG_PSEUDOFS_SOFTLINKSin aboard defconfig would pull in the entire symlink/readlink machinery just to
make a hard-link call report "unsupported", keeps the incorrect coupling, and
hides a VFS-wide issue behind a per-board workaround instead of fixing it in
the place the behavior actually lives.
Testing
Host: <Ubuntu 24.04 x86_64>
Board: esp32c3-devkit, configuration without
CONFIG_PSEUDOFS_SOFTLINKSBuilt and exercised on the esp32c3-devkit:
link()returns-1witherrno == ENOSYS, andlinkat()resolves consistently in a build with softlinks disabled.