From d31232a19fce75e7f13c422025adf7d704842f68 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Thu, 16 Jan 2014 21:00:21 -0700 Subject: [PATCH 5/5] ntdll: Add support for deleting junction points. Signed-off-by: Daniel Lehman --- dlls/ntdll/file.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/tests/file.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index e2a2085..c9f5207 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -1819,6 +1819,43 @@ cleanup: } +/* + * Retrieve the unix name corresponding to a file handle, remove that symlink, and then recreate + * a directory at the location of the old filename. + */ +NTSTATUS FILE_RemoveSymlink(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer) +{ + int dest_fd, needs_close; + ANSI_STRING unix_name; + NTSTATUS status; + + /* ntifs.h header suggests FILE_SPECIAL_ACCESS but it does not return ACCESS_DENIED */ + if ((status = server_get_unix_fd( handle, FILE_WRITE_ACCESS, &dest_fd, &needs_close, NULL, NULL ))) + return status; + if (needs_close) close( dest_fd ); + + if ((status = server_get_unix_name( handle, &unix_name ))) + return status; + + TRACE("Deleting symlink %s\n", unix_name.Buffer); + if (unlink( unix_name.Buffer ) < 0) + { + status = FILE_GetNtStatus(); + goto cleanup; + } + if (mkdir( unix_name.Buffer, 0775 ) < 0) + { + status = FILE_GetNtStatus(); + goto cleanup; + } + status = STATUS_SUCCESS; + +cleanup: + RtlFreeAnsiString( &unix_name ); + return status; +} + + /************************************************************************** * NtFsControlFile [NTDLL.@] * ZwFsControlFile [NTDLL.@] @@ -1913,6 +1950,36 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc io->Information = 0; status = STATUS_SUCCESS; break; + case FSCTL_DELETE_REPARSE_POINT: + { + REPARSE_GUID_DATA_BUFFER *buffer = in_buffer; + + if (!buffer) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + if (in_size < REPARSE_GUID_DATA_BUFFER_HEADER_SIZE) + { + status = STATUS_IO_REPARSE_DATA_INVALID; + break; + } + + switch(buffer->ReparseTag) + { + case IO_REPARSE_TAG_MOUNT_POINT: + status = FILE_RemoveSymlink( handle, buffer ); + if (status == STATUS_SUCCESS) + io->Information = 0; + break; + default: + FIXME("stub: FSCTL_DELETE_REPARSE_POINT(%x)\n", buffer->ReparseTag); + status = STATUS_NOT_IMPLEMENTED; + break; + } + break; + } case FSCTL_GET_REPARSE_POINT: { REPARSE_DATA_BUFFER *buffer = out_buffer; diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 8647aa1..c7673cb 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4280,6 +4280,7 @@ static void test_junction_points(void) static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0}; static const WCHAR fooW[] = {'f','o','o',0}; static WCHAR volW[] = {'c',':','\\',0}; + REPARSE_GUID_DATA_BUFFER guid_buffer; static const WCHAR dotW[] = {'.',0}; DWORD dwret, dwLen, dwFlags; UNICODE_STRING nameW; @@ -4410,6 +4411,77 @@ static void test_junction_points(void) buffer_len -= REPARSE_DATA_BUFFER_HEADER_SIZE; ok(buffer->ReparseDataLength == buffer_len, "expected %d, got %d\n", buffer_len, buffer->ReparseDataLength); + /* Delete the junction point */ + hJunction = CreateFileW(junction_path, GENERIC_READ, 0, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + memset(&guid_buffer, 0x00, sizeof(guid_buffer)); + guid_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + memset(&iosb, 0xff, sizeof(iosb)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_DELETE_REPARSE_POINT, &guid_buffer, + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0); + ok(status == STATUS_ACCESS_DENIED, "expected %x, got %x\n", STATUS_ACCESS_DENIED, status); + ok(iosb.Information == ~0, "expected ~0, got %lx\n", iosb.Information); + CloseHandle(hJunction); + + hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_DELETE_REPARSE_POINT, NULL, 0, NULL, 0); + ok(status == STATUS_INVALID_BUFFER_SIZE, "expected %x, got %x\n", STATUS_INVALID_BUFFER_SIZE, status); + ok(iosb.Information == ~0, "expected ~0, got %lx\n", iosb.Information); + + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_DELETE_REPARSE_POINT, &guid_buffer, 2, NULL, 0); + ok(status == STATUS_IO_REPARSE_DATA_INVALID, "expected %x, got %x\n", STATUS_IO_REPARSE_DATA_INVALID, status); + ok(iosb.Information == ~0, "expected ~0, got %lx\n", iosb.Information); + + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_DELETE_REPARSE_POINT, &guid_buffer, + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0); + ok(status == STATUS_SUCCESS, "expected 0, got %x\n", status); + ok(!iosb.Information, "expected 0, got %lx\n", iosb.Information); + CloseHandle(hJunction); + + /* Check deleting a junction point as if it were a directory */ + hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + buffer_len = build_reparse_buffer(nameW.Buffer, buffer); + memset(&iosb, 0xff, sizeof(iosb)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_SET_REPARSE_POINT, buffer, buffer_len, NULL, 0); + ok(status == STATUS_SUCCESS, "expected 0, got %x\n", status); + ok(!iosb.Information, "expected 0, got %lx\n", iosb.Information); + CloseHandle(hJunction); + bret = RemoveDirectoryW(junction_path); + ok(bret, "Failed to delete junction point as directory!\n"); + dwret = GetFileAttributesW(junction_path); + ok(dwret == INVALID_FILE_ATTRIBUTES, "Junction point still exists (attributes: 0x%x)!\n", dwret); + + /* Check deleting a junction point as if it were a file */ + bret = CreateDirectoryW(junction_path, NULL); + ok(bret, "Failed to create junction point target directory.\n"); + hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); + buffer_len = build_reparse_buffer(nameW.Buffer, buffer); + memset(&iosb, 0xff, sizeof(iosb)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_SET_REPARSE_POINT, buffer, buffer_len, NULL, 0); + ok(status == STATUS_SUCCESS, "expected 0, got %x\n", status); + ok(!iosb.Information, "expected 0, got %lx\n", iosb.Information); + CloseHandle(hJunction); + bret = DeleteFileW(junction_path); + ok(!bret, "Succeeded in deleting junction point as file!\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "Expected last error 0x%x for DeleteFile on junction point (actually 0x%x)!\n", + ERROR_ACCESS_DENIED, GetLastError()); + dwret = GetFileAttributesW(junction_path); + ok(dwret != INVALID_FILE_ATTRIBUTES, "Junction point doesn't exist (attributes: 0x%x)!\n", dwret); + ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: 0x%x)\n", dwret); + + /* Test deleting a junction point's target */ + dwret = GetFileAttributesW(junction_path); + ok(dwret == 0x410 || broken(dwret == 0x430) /* win2k */ || broken(dwret == 0xc10) /* vista */, + "Unexpected junction point attributes (0x%x != 0x410)!\n", dwret); + bret = RemoveDirectoryW(target_path); + ok(bret, "Failed to delete junction point target!\n"); + bret = CreateDirectoryW(target_path, NULL); + ok(bret, "Failed to create junction point target directory.\n"); + cleanup: /* Cleanup */ pRtlFreeUnicodeString( &nameW ); -- 1.9.5