summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2015-09-07 18:26:36 +0300
committerEli Zaretskii <eliz@gnu.org>2015-09-07 18:26:36 +0300
commitcaf4a0192621f49b677ad05fe86e358020a88b7e (patch)
tree0ca50fcfa4969ef71319af161a310772ea18c894
parent066b26d6c47d1384d5ed3ce09c6379a9df350256 (diff)
downloademacs-caf4a0192621f49b677ad05fe86e358020a88b7e.tar.gz
Fix deletion of symlinks to directories on MS-Windows
* src/w32.c (sys_unlink): If 'unlink' fails, and the argument is a symlink to a directory, try again with 'rmdir'. (is_symlink): If the argument is a symlink to a directory, set a bit in the return value to indicate that fact.
-rw-r--r--src/w32.c33
1 files changed, 29 insertions, 4 deletions
diff --git a/src/w32.c b/src/w32.c
index cc55507919c..bb514960e43 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -4534,6 +4534,8 @@ sys_rmdir (const char * path)
int
sys_unlink (const char * path)
{
+ int rmstatus, e;
+
path = map_w32_filename (path, NULL);
if (w32_unicode_filenames)
@@ -4541,9 +4543,18 @@ sys_unlink (const char * path)
wchar_t path_w[MAX_PATH];
filename_to_utf16 (path, path_w);
- /* On Unix, unlink works without write permission. */
+ /* On Unix, unlink works without write permission. */
_wchmod (path_w, 0666);
- return _wunlink (path_w);
+ rmstatus = _wunlink (path_w);
+ e = errno;
+ /* Symlinks to directories can only be deleted by _rmdir;
+ _unlink returns EACCES. */
+ if (rmstatus != 0
+ && errno == EACCES
+ && (is_symlink (path) & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ rmstatus = _wrmdir (path_w);
+ else
+ errno = e;
}
else
{
@@ -4551,8 +4562,17 @@ sys_unlink (const char * path)
filename_to_ansi (path, path_a);
_chmod (path_a, 0666);
- return _unlink (path_a);
+ rmstatus = _unlink (path_a);
+ e = errno;
+ if (rmstatus != 0
+ && errno == EACCES
+ && (is_symlink (path) & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ rmstatus = _rmdir (path_a);
+ else
+ errno = e;
}
+
+ return rmstatus;
}
static FILETIME utc_base_ft;
@@ -5626,7 +5646,8 @@ symlink (char const *filename, char const *linkname)
/* A quick inexpensive test of whether FILENAME identifies a file that
is a symlink. Returns non-zero if it is, zero otherwise. FILENAME
must already be in the normalized form returned by
- map_w32_filename.
+ map_w32_filename. If the symlink is to a directory, the
+ FILE_ATTRIBUTE_DIRECTORY bit will be set in the return value.
Note: for repeated operations on many files, it is best to test
whether the underlying volume actually supports symlinks, by
@@ -5684,6 +5705,8 @@ is_symlink (const char *filename)
attrs_mean_symlink =
(wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
&& (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+ if (attrs_mean_symlink)
+ attrs_mean_symlink |= (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
}
else if (_mbspbrk (filename_a, "?"))
{
@@ -5697,6 +5720,8 @@ is_symlink (const char *filename)
attrs_mean_symlink =
(wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
&& (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+ if (attrs_mean_symlink)
+ attrs_mean_symlink |= (wfda.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
}
if (fh == INVALID_HANDLE_VALUE)
return 0;