This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit c1891e07c913450f7065a43f4aabd7c18b0efa27 Author: Alan Carvalho de Assis <[email protected]> AuthorDate: Wed Jul 29 22:02:16 2026 -0300 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 <[email protected]> Assisted-by: Claude Sonnet 5 <[email protected]> --- fs/inode/fs_inodesearch.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/fs/inode/fs_inodesearch.c b/fs/inode/fs_inodesearch.c index 1aecb529ad0..67a14cf474e 100644 --- a/fs/inode/fs_inodesearch.c +++ b/fs/inode/fs_inodesearch.c @@ -558,15 +558,32 @@ FAR const char *inode_nextname(FAR const char *name) name++; } - /* Skip single '.' path segment, but not '..' */ + /* Skip single '.' path segment, but not '..'. This includes a lone + * trailing '.' as the final path component (e.g. "/foo/."), which + * refers to "foo" itself the same way "/foo/./" would -- without this, + * a trailing '.' is instead treated as a literal child name to look up + * under "foo" and fails to resolve, since no real node is ever named + * ".", rather than resolving to the node the search already reached. + */ - if (*name == '.' && *(name + 1) == '/') + if (*name == '.' && (*(name + 1) == '/' || *(name + 1) == '\0')) { - /* If there is a '/' after '.', - * continue searching from the next character - */ + if (*(name + 1) == '/') + { + /* If there is a '/' after '.', + * continue searching from the next character + */ - name = inode_nextname(name); + name = inode_nextname(name); + } + else + { + /* Lone trailing '.': point past it, at the terminating NUL, + * the same as if the path had ended one character earlier. + */ + + name++; + } } return name;
