Make unlink() work for junction points on Windows.
authorThomas Munro
Sat, 6 Aug 2022 00:01:42 +0000 (12:01 +1200)
committerAndrew Dunstan
Thu, 7 Nov 2024 23:06:10 +0000 (09:36 +1030)
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
Discussion: https://postgr.es/m/CA%2BhUKGLfOOeyZpm5ByVcAt7x5Pn-%3DxGRNCvgiUPVVzjFLtnY0w%40mail.gmail.com
(cherry picked from commit f357233c9db8be2a015163da8e1ab0630f444340)

Author: Thomas Munro 
Author: Alexandra Wang 

src/port/dirmod.c

index 763bc5f915a670203ab19bbf2976a533d46e38aa..9403c8adb8acb093f4ae1d94397ec990808dd7ba 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;