Fix lstat() for broken junction points on Windows.
authorThomas Munro <tmunro@postgresql.org>
Tue, 25 Oct 2022 02:20:00 +0000 (15:20 +1300)
committerAndrew Dunstan <andrew@dunslane.net>
Thu, 7 Nov 2024 23:06:40 +0000 (09:36 +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 <tmunro@postgresql.org>
Author: Alexandra Wang <alexandra.wang.oss@gmail.com>

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

index 0e5f91adfa00f21a8c0c48889dbd33e77a439a78..60253d1e3f33b37e2d549a83119481fff249dc31 100644 (file)
@@ -164,6 +164,12 @@ static const struct
    },
    {
        ERROR_DELETE_PENDING, ENOENT
+   },
+   {
+       ERROR_INVALID_NAME, ENOENT
+   },
+   {
+       ERROR_CANT_RESOLVE_FILENAME, ENOENT
    }
 };
 
index 001e329a18d2438955f4084566d93368cf3acea7..49fbb3ce649b3d9e110480f6a2c3f49e5c715ee1 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;
 }