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>
This commit is contained in:
Alan Carvalho de Assis
2026-08-14 10:20:52 +08:00
committed by Xiang Xiao
parent 3cbf6f2168
commit c1891e07c9
+23 -6
View File
@@ -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;