Make unlink() work for junction points on Windows.
authorThomas Munro <tmunro@postgresql.org>
Sat, 6 Aug 2022 00:01:42 +0000 (12:01 +1200)
committerThomas Munro <tmunro@postgresql.org>
Sat, 6 Aug 2022 00:50:59 +0000 (12:50 +1200)
To support harmonization of Windows and Unix code, teach our unlink()
wrapper that junction points need to be unlinked with rmdir() on
Windows.

Tested-by: Andrew Dunstan <andrew@dunslane.net>
Discussion: https://postgr.es/m/CA%2BhUKGLfOOeyZpm5ByVcAt7x5Pn-%3DxGRNCvgiUPVVzjFLtnY0w%40mail.gmail.com

src/port/dirmod.c

index 7ce042e75d0180deaec9f51db66265098335365f..ea191e99c65e2ff0d214f09877c069d24091c10b 100644 (file)
@@ -99,6 +99,32 @@ int
 pgunlink(const char *path)
 {
    int         loops = 0;
+   struct stat st;
+
+   /*
+    * This function might be called for a regular file or for a junction
+    * point (which we use to emulate symlinks).  The latter must be unlinked
+    * with rmdir() on Windows.  Before we worry about any of that, let's see
+    * if we can unlink directly, since that's expected to be the most common
+    * case.
+    */
+   if (unlink(path) == 0)
+       return 0;
+   if (errno != EACCES)
+       return -1;
+
+   /*
+    * EACCES is reported for many reasons including unlink() of a junction
+    * point.  Check if that's the case so we can redirect to rmdir().
+    *
+    * Note that by checking only once, we can't cope with a path that changes
+    * from regular file to junction point underneath us while we're retrying
+    * due to sharing violations, but that seems unlikely.  We could perhaps
+    * prevent that by holding a file handle ourselves across the lstat() and
+    * the retry loop, but that seems like over-engineering for now.
+    */
+   if (lstat(path, &st) < 0)
+       return -1;
 
    /*
     * We need to loop because even though PostgreSQL uses flags that allow
@@ -107,7 +133,7 @@ pgunlink(const char *path)
     * someone else to close the file, as the caller might be holding locks
     * and blocking other backends.
     */
-   while (unlink(path))
+   while ((S_ISLNK(st.st_mode) ? rmdir(path) : unlink(path)) < 0)
    {
        if (errno != EACCES)
            return -1;