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;

Reply via email to