aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/port/dirmod.c28
1 files changed, 27 insertions, 1 deletions
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index 7ce042e75d0..ea191e99c65 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -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;