Fix lstat() for broken junction points on Windows.
authorThomas Munro <[email protected]>
Tue, 25 Oct 2022 02:20:00 +0000 (15:20 +1300)
committerThomas Munro <[email protected]>
Tue, 25 Oct 2022 03:19:01 +0000 (16:19 +1300)
When using junction points to emulate symlinks on Windows, one edge case
was not handled correctly by commit c5cb8f3b: if a junction point is
broken (pointing to a non-existent path), we'd report ENOENT.  This
doesn't break any known use case, but was noticed while developing a
test suite for these functions and is fixed here for completeness.

Also add translation ERROR_CANT_RESOLVE_FILENAME -> ENOENT, as that is
one of the errors Windows can report for some kinds of broken paths.

Discussion: https://postgr.es/m/CA%2BhUKG%2BajSQ_8eu2AogTncOnZ5me2D-Cn66iN_-wZnRjLN%2Bicg%40mail.gmail.com

src/port/win32error.c
src/port/win32stat.c

index a78d323827179577f64ef299ac8f9947b794535f..67ce805d77512c99a5cbd2dcf8e2cb6322e75c38 100644 (file)
@@ -167,6 +167,9 @@ static const struct
    },
    {
        ERROR_INVALID_NAME, ENOENT
+   },
+   {
+       ERROR_CANT_RESOLVE_FILENAME, ENOENT
    }
 };
 
index 5f3d0d22ff417ebd4b6d720f573c3e01b0f874b7..ce8d87093d85b86c88cf90c47c40d86bb8825a40 100644 (file)
@@ -125,15 +125,30 @@ _pglstat64(const char *name, struct stat *buf)
 
    hFile = pgwin32_open_handle(name, O_RDONLY, true);
    if (hFile == INVALID_HANDLE_VALUE)
-       return -1;
-
-   ret = fileinfo_to_stat(hFile, buf);
+   {
+       if (errno == ENOENT)
+       {
+           /*
+            * If it's a junction point pointing to a non-existent path, we'll
+            * have ENOENT here (because pgwin32_open_handle does not use
+            * FILE_FLAG_OPEN_REPARSE_POINT).  In that case, we'll try again
+            * with readlink() below, which will distinguish true ENOENT from
+            * pseudo-symlink.
+            */
+           memset(buf, 0, sizeof(*buf));
+           ret = 0;
+       }
+       else
+           return -1;
+   }
+   else
+       ret = fileinfo_to_stat(hFile, buf);
 
    /*
     * Junction points appear as directories to fileinfo_to_stat(), so we'll
     * need to do a bit more work to distinguish them.
     */
-   if (ret == 0 && S_ISDIR(buf->st_mode))
+   if ((ret == 0 && S_ISDIR(buf->st_mode)) || hFile == INVALID_HANDLE_VALUE)
    {
        char        next[MAXPGPATH];
        ssize_t     size;
@@ -169,10 +184,12 @@ _pglstat64(const char *name, struct stat *buf)
            buf->st_mode &= ~S_IFDIR;
            buf->st_mode |= S_IFLNK;
            buf->st_size = size;
+           ret = 0;
        }
    }
 
-   CloseHandle(hFile);
+   if (hFile != INVALID_HANDLE_VALUE)
+       CloseHandle(hFile);
    return ret;
 }