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;