windows: improve uv_fs_unlink

* It's now more efficient, the file is not opened twice.
* It no longer allows deletion of non-symlink directory reparse points.
This commit is contained in:
Bert Belder 2012-07-31 16:52:31 +02:00
parent 7edc29a414
commit 7f6b86c687
2 changed files with 59 additions and 26 deletions

View File

@ -338,17 +338,20 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
return -1; return -1;
} }
/* Compute the length of the target. */ /* If needed, compute the length of the target. */
target_len = WideCharToMultiByte(CP_UTF8, if (target_ptr != NULL || target_len_ptr != NULL) {
0, /* Compute the length of the target. */
w_target, target_len = WideCharToMultiByte(CP_UTF8,
w_target_len, 0,
NULL, w_target,
0, w_target_len,
NULL, NULL,
NULL); 0,
if (target_len == 0) { NULL,
return -1; NULL);
if (target_len == 0) {
return -1;
}
} }
/* If requested, allocate memory and convert to UTF8. */ /* If requested, allocate memory and convert to UTF8. */
@ -615,14 +618,15 @@ void fs__rmdir(uv_fs_t* req) {
void fs__unlink(uv_fs_t* req) { void fs__unlink(uv_fs_t* req) {
const WCHAR* pathw = req->pathw; const WCHAR* pathw = req->pathw;
int result;
HANDLE handle; HANDLE handle;
BY_HANDLE_FILE_INFORMATION info; BY_HANDLE_FILE_INFORMATION info;
int is_dir_symlink; FILE_DISPOSITION_INFORMATION disposition;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
handle = CreateFileW(pathw, handle = CreateFileW(pathw,
0, FILE_READ_ATTRIBUTES | DELETE,
0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, NULL,
OPEN_EXISTING, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
@ -639,19 +643,44 @@ void fs__unlink(uv_fs_t* req) {
return; return;
} }
is_dir_symlink = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); /* Do not allow deletion of directories, unless it is a symlink. When */
/* the path refers to a non-symlink directory, report EPERM as mandated */
/* by POSIX.1. */
/* Check if it is a reparse point. If it's not, it's a normal directory. */
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
CloseHandle(handle);
return;
}
/* Read the reparse point and check if it is a valid symlink. */
/* If not, don't unlink. */
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
DWORD error = GetLastError();
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
error = ERROR_ACCESS_DENIED;
SET_REQ_WIN32_ERROR(req, error);
CloseHandle(handle);
return;
}
}
/* Try to set the delete flag. */
disposition.DeleteFile = TRUE;
status = pNtSetInformationFile(handle,
&iosb,
&disposition,
sizeof disposition,
FileDispositionInformation);
if (NT_SUCCESS(status)) {
SET_REQ_SUCCESS(req);
} else {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
}
CloseHandle(handle); CloseHandle(handle);
/* Todo: very inefficient; fix this. */
if (is_dir_symlink) {
int result = _wrmdir(req->pathw);
SET_REQ_RESULT(req, result);
} else {
result = _wunlink(pathw);
SET_REQ_RESULT(req, result);
}
} }

View File

@ -4132,6 +4132,10 @@ typedef struct _FILE_BASIC_INFORMATION {
DWORD FileAttributes; DWORD FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
typedef struct _FILE_MODE_INFORMATION { typedef struct _FILE_MODE_INFORMATION {
ULONG Mode; ULONG Mode;
} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;