Fix lstat() for broken junction points on Windows.
authorThomas Munro
Tue, 25 Oct 2022 02:20:00 +0000 (15:20 +1300)
committerAndrew Dunstan
Thu, 7 Nov 2024 23:16:39 +0000 (09:46 +1030)
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
(cherry picked from commit 387803d81d6256fcb60b9192bb5b00042442b4e3)

Author: Thomas Munro 
Author: Alexandra Wang 

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

index c9bfc9fa4a8359316767813951c9f898867ccf33..b7406c2c04e4aec1c3a1acde18fde826fff81e72 100644 (file)
@@ -164,6 +164,12 @@ static const struct
    },
    {
        ERROR_DELETE_PENDING, ENOENT
+   },
+   {
+       ERROR_INVALID_NAME, ENOENT
+   },
+   {
+       ERROR_CANT_RESOLVE_FILENAME, ENOENT
    }
 };
 
index 82bd2d0bfa600494973c64a94600488dcd67d377..d4c70c5a88a9ab6ba849c796db79e4464d94994e 100644 (file)
@@ -127,15 +127,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;
@@ -171,10 +186,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;
 }