]> sourceware.org Git - newlib-cygwin.git/commitdiff
Cygwin: fix errno values set by readlinkat
authorCorinna Vinschen <corinna@vinschen.de>
Tue, 18 Apr 2023 11:52:50 +0000 (13:52 +0200)
committerCorinna Vinschen <corinna@vinschen.de>
Tue, 18 Apr 2023 11:53:09 +0000 (13:53 +0200)
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>
winsup/cygwin/release/3.4.7
winsup/cygwin/syscalls.cc

index 8ecfbc30a24b64111028b84403402b19259feeb5..7bc7d4a1ccdbeca4e7a0369d3e9a2283287c11a6 100644 (file)
@@ -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
index fff8af009fd15f9fda563d694439307cf628e123..a4e4af626db1ff680040cb41c08d43aa13886650 100644 (file)
@@ -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);
This page took 0.036847 seconds and 5 git commands to generate.