[newlib-cygwin/main] Cygwin: fix errno values set by readlinkat

Corinna Vinschen corinna@sourceware.org
Tue Apr 18 11:54:44 GMT 2023


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=fa84aa4dd2fb43eaf7fcdfb040aef854f2f19d01

commit fa84aa4dd2fb43eaf7fcdfb040aef854f2f19d01
Author:     Corinna Vinschen <corinna@vinschen.de>
AuthorDate: Tue Apr 18 13:52:50 2023 +0200
Commit:     Corinna Vinschen <corinna@vinschen.de>
CommitDate: Tue Apr 18 13:52:50 2023 +0200

    Cygwin: fix errno values set by readlinkat
    
    readlinkat(fd, "", ...) is supposed to return ENOENT per POSIX,
    but Cygwin returns EBADF.
    
    At the same time, we have to maintain the special feature of
    glibc that readlinkat(fd, "", ...) operates on fd, if fd is pointing
    at a symlink opened with O_PATH|O_NOFOLLOW.
    
    And, while fixing that, readlinkat(fd, path, ...) *still* has to set errno
    to EBADF, if fd is an invalid descriptor *and* path is a relative path.
    
    This required to change the evaluation order in the helper function
    gen_full_path_at.
    
    Last but not least, in case of the aforementioned glibc-like special
    handling for symlink descriptors, we have to make sure that errors from
    gen_full_path_at are not spilled into that special handling.
    
    Fixes: 6cc05784e16a ("Cygwin: readlinkat: allow pathname to be empty")
    Reported-by: Bruno Haible <bruno@clisp.org>
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/release/3.4.7 |  3 +++
 winsup/cygwin/syscalls.cc   | 42 ++++++++++++++++++++++--------------------
 2 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/winsup/cygwin/release/3.4.7 b/winsup/cygwin/release/3.4.7
index 8ecfbc30a24b..7bc7d4a1ccdb 100644
--- a/winsup/cygwin/release/3.4.7
+++ b/winsup/cygwin/release/3.4.7
@@ -18,3 +18,6 @@ Bug Fixes
 
 - Fix return value of ilogbl(NaN).
   Addresses: https://cygwin.com/pipermail/cygwin/2023-April/253511.html
+
+- Fix error handling in readlinkat.
+  Addresses: https://cygwin.com/pipermail/cygwin/2023-April/253510.html
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index d5b44c37858c..b109720568dc 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -4415,19 +4415,6 @@ gen_full_path_at (char *path_ret, int dirfd, const char *pathname,
       set_errno (EFAULT);
       return -1;
     }
-  if (pathname)
-    {
-      if (!*pathname)
-	{
-	  set_errno (ENOENT);
-	  return -1;
-	}
-      if (strlen (pathname) >= PATH_MAX)
-	{
-	  set_errno (ENAMETOOLONG);
-	  return -1;
-	}
-    }
   if (pathname && isabspath_strict (pathname))
     stpcpy (path_ret, pathname);
   else
@@ -4459,6 +4446,16 @@ gen_full_path_at (char *path_ret, int dirfd, const char *pathname,
 	}
       if (pathname)
 	{
+	  if (!*pathname)
+	    {
+	      set_errno (ENOENT);
+	      return -1;
+	    }
+	  if (strlen (pathname) >= PATH_MAX)
+	    {
+	      set_errno (ENAMETOOLONG);
+	      return -1;
+	    }
 	  if (p[-1] != '/')
 	    *p++ = '/';
 	  stpcpy (p, pathname);
@@ -4803,21 +4800,26 @@ readlinkat (int dirfd, const char *__restrict pathname, char *__restrict buf,
   __try
     {
       char *path = tp.c_get ();
+      int save_errno = errno;
       int res = gen_full_path_at (path, dirfd, pathname);
       if (res)
 	{
-	  if (errno != ENOENT)
+	  if (errno != ENOENT && errno != ENOTDIR)
 	    __leave;
 	  /* pathname is an empty string.  This is OK if dirfd refers
 	     to a symlink that was opened with O_PATH | O_NOFOLLOW.
-	     In this case, readlinkat operates on the symlink. */
+	     In this case, readlinkat operates on the symlink.
+	     Don't propagate errors from gen_full_path_at after this point. */
+	  errno = save_errno;
 	  cygheap_fdget cfd (dirfd);
-	  if (cfd < 0)
-	    __leave;
-	  if (!(cfd->issymlink ()
+	  if (cfd < 0
+	      || (!(cfd->issymlink ()
 		&& cfd->get_flags () & O_PATH
-		&& cfd->get_flags () & O_NOFOLLOW))
-	    __leave;
+		&& cfd->get_flags () & O_NOFOLLOW)))
+	    {
+	      set_errno (ENOENT);
+	      __leave;
+	    }
 	  strcpy (path, cfd->get_name ());
 	}
       return readlink (path, buf, bufsize);


More information about the Cygwin-cvs mailing list