diff --git a/src/port/dirmod.c b/src/port/dirmod.c index 7ce042e75d..ea191e99c6 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;