From: Daniel Lehman Subject: [PATCH 4/5] ntdll: Add support for reading junction points. Message-Id: <87e11467a69b47a2a1e77eb3e6e63bf9@RED-INF-MXMB-P4.esri.com> Date: Thu, 16 Nov 2017 00:09:56 +0000 From 842928eb447187923a7da5acba2c8dd8d8a96964 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Thu, 16 Jan 2014 20:57:57 -0700 Subject: [PATCH 4/5] ntdll: Add support for reading junction points. Signed-off-by: Daniel Lehman --- dlls/ntdll/file.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/tests/file.c | 53 ++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 17bdf1d..e2a2085 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -1738,6 +1738,87 @@ cleanup: } +/* + * Retrieve the unix name corresponding to a file handle and use that to find the destination of the symlink + * corresponding to that file handle. + */ +NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG bufsize) +{ + ANSI_STRING unix_src, unix_dest; + UNICODE_STRING nt_dest; + int fd, needs_close; + WCHAR *dest_name; + NTSTATUS status; + ULONG max_size; + struct stat st; + ssize_t len; + + if ((status = server_get_unix_fd( handle, FILE_ANY_ACCESS, &fd, &needs_close, NULL, NULL ))) + return status; + if (needs_close) close(fd); + + if ((status = server_get_unix_name( handle, &unix_src ))) + return status; + + nt_dest.Buffer = NULL; + unix_dest.Buffer = NULL; + if (lstat( unix_src.Buffer, &st ) < 0) + { + status = FILE_GetNtStatus(); + goto cleanup; + } + + len = st.st_size; + unix_dest.MaximumLength = len + 1; + unix_dest.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_dest.MaximumLength ); + if (!unix_dest.Buffer) + { + status = STATUS_NO_MEMORY; + goto cleanup; + } + + len = readlink( unix_src.Buffer, unix_dest.Buffer, len ); + if (len < 0) + { + status = FILE_GetNtStatus(); + goto cleanup; + } + unix_dest.Buffer[len] = 0; + unix_dest.Length = len; + + if ((status = wine_unix_to_nt_file_name( &unix_dest, &nt_dest ))) + goto cleanup; + + /* +2 for \0 for SubstituteName and PrintName */ + max_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, u.MountPointReparseBuffer.PathBuffer[nt_dest.Length/sizeof(WCHAR)+2]); + if (max_size > bufsize) + { + status = STATUS_BUFFER_TOO_SMALL; + goto cleanup; + } + + buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + buffer->ReparseDataLength = max_size - REPARSE_DATA_BUFFER_HEADER_SIZE; + buffer->Reserved = 0; + buffer->u.MountPointReparseBuffer.SubstituteNameLength = nt_dest.Length; + buffer->u.MountPointReparseBuffer.SubstituteNameOffset = 0; + buffer->u.MountPointReparseBuffer.PrintNameOffset = nt_dest.Length + sizeof(WCHAR); + buffer->u.MountPointReparseBuffer.PrintNameLength = 0; + + dest_name = buffer->u.MountPointReparseBuffer.PathBuffer; + memcpy( dest_name, nt_dest.Buffer, nt_dest.Length ); + dest_name[nt_dest.Length/sizeof(WCHAR)] = 0; + dest_name[nt_dest.Length/sizeof(WCHAR)+1] = 0; + status = STATUS_SUCCESS; + +cleanup: + RtlFreeUnicodeString( &nt_dest ); + RtlFreeAnsiString( &unix_dest ); + RtlFreeAnsiString( &unix_src ); + return status; +} + + /************************************************************************** * NtFsControlFile [NTDLL.@] * ZwFsControlFile [NTDLL.@] @@ -1832,6 +1913,27 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc io->Information = 0; status = STATUS_SUCCESS; break; + case FSCTL_GET_REPARSE_POINT: + { + REPARSE_DATA_BUFFER *buffer = out_buffer; + + if (!buffer) + { + status = STATUS_INVALID_USER_BUFFER; + break; + } + + if (out_size < REPARSE_DATA_BUFFER_HEADER_SIZE) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + status = FILE_GetSymlink( handle, buffer, out_size ); + if (status == STATUS_SUCCESS) + io->Information = buffer->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE; + break; + } case FSCTL_SET_REPARSE_POINT: { REPARSE_DATA_BUFFER *buffer = in_buffer; diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 072d8cc..8647aa1 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4273,7 +4273,7 @@ static INT build_reparse_buffer(WCHAR *filename, REPARSE_DATA_BUFFER *buffer) static void test_junction_points(void) { - static BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + static BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE*2]; REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)buf; static const WCHAR junctionW[] = {'\\','j','u','n','c','t','i','o','n',0}; WCHAR path[MAX_PATH], junction_path[MAX_PATH], target_path[MAX_PATH]; @@ -4281,12 +4281,15 @@ static void test_junction_points(void) static const WCHAR fooW[] = {'f','o','o',0}; static WCHAR volW[] = {'c',':','\\',0}; static const WCHAR dotW[] = {'.',0}; - DWORD dwLen, dwFlags; + DWORD dwret, dwLen, dwFlags; UNICODE_STRING nameW; IO_STATUS_BLOCK iosb; NTSTATUS status; HANDLE hJunction; + DWORD soff, slen; + DWORD poff, plen; INT buffer_len; + WCHAR *dest; BOOL bret; /* Create a temporary folder for the junction point tests */ @@ -4359,8 +4362,54 @@ static void test_junction_points(void) 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); + + /* Check the file attributes of the junction point */ + 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: %d)\n", dwret); + + /* Read back the junction point */ + memset(&iosb, 0xff, sizeof(iosb)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_GET_REPARSE_POINT, NULL, 0, NULL, 0); + ok(status == STATUS_INVALID_USER_BUFFER, "expected %x, got %x\n", STATUS_INVALID_USER_BUFFER, status); + ok(iosb.Information == ~0, "expected ~0, got %lx\n", iosb.Information); + + memset(buf, 0xcc, sizeof(buf)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, REPARSE_DATA_BUFFER_HEADER_SIZE); + ok(status == STATUS_BUFFER_TOO_SMALL, "expected %x, got %x\n", STATUS_BUFFER_TOO_SMALL, status); + ok(iosb.Information == ~0, "expected ~0, got %lx\n", iosb.Information); + ok(buf[0] == 0xcc, "expected cc, got %x\n", buf[0]); + + /* Windows does not actually test buffer size against MAXIMUM_REPARSE_DATA_BUFFER_SIZE */ + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE * 2); + ok(status == STATUS_SUCCESS, "expected 0, got %x\n", status); + + SetLastError(0xdeadbeef); + memset(buf, 0xcc, sizeof(buf)); + memset(&iosb, 0xff, sizeof(iosb)); + status = pNtFsControlFile(hJunction, NULL, NULL, NULL, &iosb, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + ok(status == STATUS_SUCCESS, "expected 0, got %x\n", status); CloseHandle(hJunction); + poff = buffer->u.MountPointReparseBuffer.PrintNameOffset; + plen = buffer->u.MountPointReparseBuffer.PrintNameLength; + soff = buffer->u.MountPointReparseBuffer.SubstituteNameOffset; + slen = buffer->u.MountPointReparseBuffer.SubstituteNameLength; + dest = buffer->u.MountPointReparseBuffer.PathBuffer; + + ok(buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT, "expected %x, got %x\n", IO_REPARSE_TAG_MOUNT_POINT, buffer->ReparseTag); + ok(!buffer->Reserved, "expected 0, got %x\n", buffer->Reserved); + ok(!plen, "expected 0, got %d\n", plen); + ok(!soff, "expected 0, got %d\n", soff); + ok(poff == slen+2, "expected %d, got %d\n", slen+2, poff); + ok(nameW.Length == slen, "expected %d, got %d\n", nameW.Length, slen); + ok(!lstrcmpW(nameW.Buffer, dest), "expected %s, got %s\n", wine_dbgstr_w(nameW.Buffer), wine_dbgstr_w(dest)); + /* +2 is \0 for Print and Substitute */ + buffer_len = FIELD_OFFSET(REPARSE_DATA_BUFFER, u.MountPointReparseBuffer.PathBuffer[slen/sizeof(WCHAR) + 2]); + ok(iosb.Information == buffer_len, "expected %u, got %lu\n", buffer_len, iosb.Information); + buffer_len -= REPARSE_DATA_BUFFER_HEADER_SIZE; + ok(buffer->ReparseDataLength == buffer_len, "expected %d, got %d\n", buffer_len, buffer->ReparseDataLength); + cleanup: /* Cleanup */ pRtlFreeUnicodeString( &nameW ); -- 1.9.5