Support for junction points/reparse points.

This commit is contained in:
Erich E. Hoover 2014-01-16 21:15:21 -07:00
parent 6274002623
commit 40aaeb61f8
10 changed files with 892 additions and 1 deletions

View File

@ -0,0 +1,310 @@
From 4d377524363a3e33e2df29c38fe16542b6cd0aa1 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 20:56:49 -0700
Subject: ntdll: Add support for junction point creation.
---
dlls/ntdll/file.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/tests/file.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++
include/ntifs.h | 52 ++++++++++++++++++++++++++
3 files changed, 235 insertions(+)
create mode 100644 include/ntifs.h
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index d2efcc1..7164d1e 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -89,12 +89,14 @@
#include "winioctl.h"
#include "ddk/ntddk.h"
#include "ddk/ntddser.h"
+#include "ntifs.h"
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
mode_t FILE_umask = 0;
+#define WINE_TEMPLINK P_tmpdir"/winelink.XXXXXX"
#define SECSPERDAY 86400
#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
@@ -1470,6 +1472,76 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
}
+/*
+ * Retrieve the unix name corresponding to a file handle, remove that directory, and then symlink the
+ * requested directory to the location of the old directory.
+ */
+NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
+{
+ int dest_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
+ int offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
+ WCHAR *dest = &buffer->MountPointReparseBuffer.PathBuffer[offset];
+ char tmplink[] = WINE_TEMPLINK;
+ ANSI_STRING unix_src, unix_dest;
+ BOOL dest_allocated = FALSE;
+ int dest_fd, needs_close;
+ UNICODE_STRING nt_dest;
+ NTSTATUS status;
+
+ if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
+ return status;
+
+ if ((status = server_get_unix_name( handle, &unix_src )))
+ goto cleanup;
+
+ nt_dest.Buffer = dest;
+ nt_dest.Length = dest_len;
+ if ((status = wine_nt_to_unix_file_name( &nt_dest, &unix_dest, FILE_OPEN, FALSE )))
+ goto cleanup;
+ dest_allocated = TRUE;
+
+ TRACE("Linking %s to %s\n", unix_src.Buffer, unix_dest.Buffer);
+
+ /* Produce the link in a temporary location */
+ while(1)
+ {
+ int fd;
+
+ memcpy( tmplink, WINE_TEMPLINK, sizeof(tmplink) );
+ fd = mkstemps( tmplink, 0 );
+ if (fd == -1) break;
+ if (!unlink( tmplink ))
+ {
+ if (!symlink( unix_dest.Buffer, tmplink ))
+ break;
+ }
+ close(fd);
+ }
+ /* Atomically move the link into position */
+ if (rename( tmplink, unix_src.Buffer ))
+ {
+ unlink( tmplink );
+ FIXME("Atomic replace of directory with symbolic link unsupported on this system, may result in race condition.\n");
+ if (rmdir( unix_src.Buffer ) < 0)
+ {
+ status = FILE_GetNtStatus();
+ goto cleanup;
+ }
+ if (symlink( unix_dest.Buffer, unix_src.Buffer ) < 0)
+ {
+ status = FILE_GetNtStatus();
+ goto cleanup;
+ }
+ }
+ status = STATUS_SUCCESS;
+
+cleanup:
+ if (dest_allocated) RtlFreeAnsiString( &unix_dest );
+ if (needs_close) close( dest_fd );
+ return status;
+}
+
+
/**************************************************************************
* NtFsControlFile [NTDLL.@]
* ZwFsControlFile [NTDLL.@]
@@ -1617,6 +1689,23 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
}
break;
}
+
+ case FSCTL_SET_REPARSE_POINT:
+ {
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
+
+ switch(buffer->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ status = FILE_CreateSymlink( handle, buffer );
+ break;
+ default:
+ FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ break;
+ }
case FSCTL_PIPE_LISTEN:
case FSCTL_PIPE_WAIT:
default:
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 695f034..127681a 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -37,6 +37,7 @@
#include "winternl.h"
#include "winuser.h"
#include "winioctl.h"
+#include "ntifs.h"
#ifndef IO_COMPLETION_ALL_ACCESS
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
@@ -2672,6 +2673,98 @@ todo_wine
CloseHandle(hfile);
}
+static INT build_reparse_buffer(WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer)
+{
+ REPARSE_DATA_BUFFER *buffer;
+ INT buffer_len, string_len;
+ WCHAR *dest;
+
+ string_len = (lstrlenW(filename)+1)*sizeof(WCHAR);
+ buffer_len = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[1]) + string_len;
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
+ buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ buffer->ReparseDataLength = sizeof(buffer->MountPointReparseBuffer) + string_len;
+ buffer->MountPointReparseBuffer.SubstituteNameLength = string_len - sizeof(WCHAR);
+ buffer->MountPointReparseBuffer.PrintNameOffset = string_len;
+ dest = &buffer->MountPointReparseBuffer.PathBuffer[0];
+ memcpy(dest, filename, string_len);
+ *pbuffer = buffer;
+ return buffer_len;
+}
+
+static void test_junction_points(void)
+{
+ 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];
+ static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
+ static const WCHAR fooW[] = {'f','o','o',0};
+ static WCHAR volW[] = {'c',':','\\',0};
+ static const WCHAR dotW[] = {'.',0};
+ REPARSE_DATA_BUFFER *buffer = NULL;
+ DWORD dwret, dwLen, dwFlags;
+ UNICODE_STRING nameW;
+ HANDLE hJunction;
+ INT buffer_len;
+ BOOL bret;
+
+ /* Create a temporary folder for the junction point tests */
+ GetTempFileNameW(dotW, fooW, 0, path);
+ DeleteFileW(path);
+ if (!CreateDirectoryW(path, NULL))
+ {
+ win_skip("Unable to create a temporary junction point directory.\n");
+ return;
+ }
+
+ /* Check that the volume this folder is located on supports junction points */
+ pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL);
+ volW[0] = nameW.Buffer[4];
+ pRtlFreeUnicodeString( &nameW );
+ GetVolumeInformationW(volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0);
+ if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
+ {
+ skip("File system does not support junction points.\n");
+ RemoveDirectoryW(path);
+ return;
+ }
+
+ /* Create the folder to be replaced by a junction point */
+ lstrcpyW(junction_path, path);
+ lstrcatW(junction_path, junctionW);
+ bret = CreateDirectoryW(junction_path, NULL);
+ ok(bret, "Failed to create junction point directory.\n");
+
+ /* Create a destination folder for the junction point to target */
+ lstrcpyW(target_path, path);
+ lstrcatW(target_path, targetW);
+ bret = CreateDirectoryW(target_path, NULL);
+ ok(bret, "Failed to create junction point target directory.\n");
+ pRtlDosPathNameToNtPathName_U(target_path, &nameW, NULL, NULL);
+
+ /* Create the junction point */
+ hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
+ if (hJunction == INVALID_HANDLE_VALUE)
+ {
+ win_skip("Failed to open junction point directory handle (0x%x).\n", GetLastError());
+ goto cleanup;
+ }
+ buffer_len = build_reparse_buffer(nameW.Buffer, &buffer);
+ bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
+ CloseHandle(hJunction);
+
+cleanup:
+ /* Cleanup */
+ pRtlFreeUnicodeString( &nameW );
+ HeapFree(GetProcessHeap(), 0, buffer);
+ bret = RemoveDirectoryW(junction_path);
+ ok(bret, "Failed to remove temporary junction point directory!\n");
+ bret = RemoveDirectoryW(target_path);
+ ok(bret, "Failed to remove temporary target directory!\n");
+ RemoveDirectoryW(path);
+}
+
START_TEST(file)
{
HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
@@ -2725,4 +2818,5 @@ START_TEST(file)
test_file_disposition_information();
test_query_volume_information_file();
test_query_attribute_information_file();
+ test_junction_points();
}
diff --git a/include/ntifs.h b/include/ntifs.h
new file mode 100644
index 0000000..db07c28
--- /dev/null
+++ b/include/ntifs.h
@@ -0,0 +1,52 @@
+/*
+ * Win32 definitions for Windows NT
+ *
+ * Copyright 2012 Erich Hoover
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_NTIFS_H
+#define __WINE_NTIFS_H
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
+
+#endif /* __WINE_NTIFS_H */
--
1.7.9.5

View File

@ -0,0 +1,128 @@
From c191da3e2cf2136800fbf0658ced3a3534280806 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 20:57:57 -0700
Subject: ntdll: Add support for reading junction points.
---
dlls/ntdll/file.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/tests/file.c | 14 ++++++++++-
2 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 7164d1e..56e0ef6 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1542,6 +1542,60 @@ 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)
+{
+ ANSI_STRING unix_src, unix_dest;
+ BOOL dest_allocated = FALSE;
+ int dest_fd, needs_close;
+ UNICODE_STRING nt_dest;
+ NTSTATUS status;
+ VOID *dest_name;
+ ssize_t ret;
+
+ if ((status = server_get_unix_fd( handle, FILE_ANY_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
+ return status;
+
+ if ((status = server_get_unix_name( handle, &unix_src )))
+ goto cleanup;
+
+ unix_dest.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, PATH_MAX );
+ unix_dest.MaximumLength = PATH_MAX;
+ dest_allocated = TRUE;
+ ret = readlink( unix_src.Buffer, unix_dest.Buffer, unix_dest.MaximumLength );
+ if (ret < 0)
+ {
+ status = FILE_GetNtStatus();
+ goto cleanup;
+ }
+ unix_dest.Length = ret;
+
+ if ((status = wine_unix_to_nt_file_name( &unix_dest, &nt_dest )))
+ goto cleanup;
+
+ if (nt_dest.Length > buffer->MountPointReparseBuffer.SubstituteNameLength)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto cleanup;
+ }
+
+ buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ buffer->MountPointReparseBuffer.SubstituteNameLength = nt_dest.Length;
+ buffer->MountPointReparseBuffer.SubstituteNameOffset = 0;
+ dest_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset];
+ memcpy( dest_name, nt_dest.Buffer, nt_dest.Length );
+ status = STATUS_SUCCESS;
+
+cleanup:
+ if (dest_allocated) RtlFreeAnsiString( &unix_dest );
+ if (needs_close) close( dest_fd );
+ return status;
+}
+
+
/**************************************************************************
* NtFsControlFile [NTDLL.@]
* ZwFsControlFile [NTDLL.@]
@@ -1690,6 +1744,15 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
break;
}
+ case FSCTL_GET_REPARSE_POINT:
+ {
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
+ DWORD max_length = out_size-FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[1]);
+
+ buffer->MountPointReparseBuffer.SubstituteNameLength = max_length;
+ status = FILE_GetSymlink( handle, buffer );
+ break;
+ }
case FSCTL_SET_REPARSE_POINT:
{
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 127681a..595276a 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2702,9 +2702,10 @@ static void test_junction_points(void)
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
+ INT buffer_len, string_len;
UNICODE_STRING nameW;
HANDLE hJunction;
- INT buffer_len;
+ WCHAR *dest;
BOOL bret;
/* Create a temporary folder for the junction point tests */
@@ -2752,6 +2753,17 @@ static void test_junction_points(void)
buffer_len = build_reparse_buffer(nameW.Buffer, &buffer);
bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
+
+ /* Read back the junction point */
+ HeapFree(GetProcessHeap(), 0, buffer);
+ buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
+ bret = DeviceIoControl(hJunction, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buffer, buffer_len, &dwret, 0);
+ string_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
+ dest = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ ok(bret, "Failed to read junction point!\n");
+ ok((memcmp(dest, nameW.Buffer, string_len) == 0), "Junction point destination does not match ('%s' != '%s')!\n",
+ wine_dbgstr_w(dest), wine_dbgstr_w(nameW.Buffer));
CloseHandle(hJunction);
cleanup:
--
1.7.9.5

View File

@ -0,0 +1,159 @@
From 9a592e9dd065355f785e997fb72ee5a586665dc0 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:00:21 -0700
Subject: ntdll: Add support for deleting junction points.
---
dlls/ntdll/file.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/tests/file.c | 23 +++++++++++++++++++++
include/ntifs.h | 11 ++++++++++
3 files changed, 85 insertions(+)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 56e0ef6..396bdf9 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1596,6 +1596,41 @@ 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;
+
+ if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
+ return status;
+
+ if ((status = server_get_unix_name( handle, &unix_name )))
+ goto cleanup;
+
+ 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:
+ if (needs_close) close( dest_fd );
+ return status;
+}
+
+
/**************************************************************************
* NtFsControlFile [NTDLL.@]
* ZwFsControlFile [NTDLL.@]
@@ -1744,6 +1779,22 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
break;
}
+ case FSCTL_DELETE_REPARSE_POINT:
+ {
+ REPARSE_GUID_DATA_BUFFER *buffer = (REPARSE_GUID_DATA_BUFFER *)in_buffer;
+
+ switch(buffer->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ status = FILE_RemoveSymlink( handle, buffer );
+ 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 = (REPARSE_DATA_BUFFER *)out_buffer;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 595276a..d3c6cf0 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2697,12 +2697,15 @@ static void test_junction_points(void)
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];
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
+ FILE_BASIC_INFORMATION old_attrib, new_attrib;
static const WCHAR fooW[] = {'f','o','o',0};
static WCHAR volW[] = {'c',':','\\',0};
+ REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
INT buffer_len, string_len;
+ IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE hJunction;
WCHAR *dest;
@@ -2750,6 +2753,8 @@ static void test_junction_points(void)
win_skip("Failed to open junction point directory handle (0x%x).\n", GetLastError());
goto cleanup;
}
+ dwret = NtQueryInformationFile(hJunction, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
+ ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%x).\n", dwret);
buffer_len = build_reparse_buffer(nameW.Buffer, &buffer);
bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
@@ -2764,6 +2769,24 @@ static void test_junction_points(void)
ok(bret, "Failed to read junction point!\n");
ok((memcmp(dest, nameW.Buffer, string_len) == 0), "Junction point destination does not match ('%s' != '%s')!\n",
wine_dbgstr_w(dest), wine_dbgstr_w(nameW.Buffer));
+
+ /* Delete the junction point */
+ memset(&old_attrib, 0x00, sizeof(old_attrib));
+ old_attrib.LastAccessTime.QuadPart = 0x200deadcafebeef;
+ dwret = NtSetInformationFile(hJunction, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
+ ok(dwret == STATUS_SUCCESS, "Failed to set junction point folder's attributes (0x%x).\n", dwret);
+ memset(&guid_buffer, 0x00, sizeof(guid_buffer));
+ guid_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ bret = DeviceIoControl(hJunction, FSCTL_DELETE_REPARSE_POINT, (LPVOID)&guid_buffer,
+ REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to delete junction point! (0x%x)\n", GetLastError());
+ memset(&new_attrib, 0x00, sizeof(new_attrib));
+ dwret = NtQueryInformationFile(hJunction, &iosb, &new_attrib, sizeof(new_attrib), FileBasicInformation);
+ ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%x).\n", dwret);
+ /* conversion bug: we see 0x1c9c380deadbee6 on Wine */
+ todo_wine ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
+ "Junction point folder's access time does not match (0x%llx != 0x%llx).\n",
+ new_attrib.LastAccessTime.QuadPart, old_attrib.LastAccessTime.QuadPart);
CloseHandle(hJunction);
cleanup:
diff --git a/include/ntifs.h b/include/ntifs.h
index db07c28..cb8638b 100644
--- a/include/ntifs.h
+++ b/include/ntifs.h
@@ -47,6 +47,17 @@ typedef struct _REPARSE_DATA_BUFFER {
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+typedef struct _REPARSE_GUID_DATA_BUFFER {
+ DWORD ReparseTag;
+ WORD ReparseDataLength;
+ WORD Reserved;
+ GUID ReparseGuid;
+ struct {
+ BYTE DataBuffer[1];
+ } GenericReparseBuffer;
+} REPARSE_GUID_DATA_BUFFER, *PREPARSE_GUID_DATA_BUFFER;
+
#define IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
+#define REPARSE_GUID_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer)
#endif /* __WINE_NTIFS_H */
--
1.7.9.5

View File

@ -0,0 +1,57 @@
From f35e4dc0c96de3e9fbeb4ff1bdefc9db7c7d56d8 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:01:25 -0700
Subject: ntdll: Advertise that a file is a junction point.
---
dlls/ntdll/file.c | 7 ++++++-
dlls/ntdll/tests/file.c | 5 +++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 396bdf9..b4e06d1 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1988,10 +1988,11 @@ NTSTATUS fill_stat_info( const struct stat *st, void *ptr, FILE_INFORMATION_CLAS
get_file_times( st, &info->LastWriteTime, &info->ChangeTime,
&info->LastAccessTime, &info->CreationTime );
- if (S_ISDIR(st->st_mode)) info->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+ if (st->st_mode & S_IFDIR) info->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
else info->FileAttributes = FILE_ATTRIBUTE_ARCHIVE;
if (!(st->st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
+ if ((st->st_mode & S_IFLNK) == S_IFLNK) info->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
}
break;
case FileStandardInformation:
@@ -2657,6 +2658,10 @@ NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC
status = STATUS_INVALID_INFO_CLASS;
else
{
+ struct stat lst;
+
+ if (lstat( unix_name.Buffer, &lst ) != -1)
+ st.st_mode |= (lst.st_mode & S_IFLNK);
status = fill_stat_info( &st, info, FileBasicInformation );
if (DIR_is_hidden_file( attr->ObjectName ))
info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index d3c6cf0..c75ba47 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2759,6 +2759,11 @@ static void test_junction_points(void)
bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
+ /* Check the file attributes of the junction point */
+ dwret = GetFileAttributesW(junction_path);
+ ok(dwret != (DWORD)~0, "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 */
HeapFree(GetProcessHeap(), 0, buffer);
buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
--
1.7.9.5

View File

@ -0,0 +1,103 @@
From e6a876330230784a2b2be6588b94e5555de169da Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:02:11 -0700
Subject: kernel32,ntdll: Add support for deleting junction points with
RemoveDirectory.
---
dlls/kernel32/path.c | 12 ++++++++++--
dlls/ntdll/tests/file.c | 34 +++++++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
index 09fb04b..c328cc0 100644
--- a/dlls/kernel32/path.c
+++ b/dlls/kernel32/path.c
@@ -1587,6 +1587,7 @@ BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRI
*/
BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
{
+ FILE_BASIC_INFORMATION info;
OBJECT_ATTRIBUTES attr;
UNICODE_STRING nt_name;
ANSI_STRING unix_name;
@@ -1614,15 +1615,22 @@ BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
if (status == STATUS_SUCCESS)
status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
- RtlFreeUnicodeString( &nt_name );
if (status != STATUS_SUCCESS)
{
+ RtlFreeUnicodeString( &nt_name );
SetLastError( RtlNtStatusToDosError(status) );
return FALSE;
}
- if (!(ret = (rmdir( unix_name.Buffer ) != -1))) FILE_SetDosError();
+ status = NtQueryAttributesFile( &attr, &info );
+ RtlFreeUnicodeString( &nt_name );
+ if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ ret = (unlink( unix_name.Buffer ) != -1);
+ else
+ ret = (rmdir( unix_name.Buffer ) != -1);
+ if (!ret) FILE_SetDosError();
+
RtlFreeAnsiString( &unix_name );
NtClose( handle );
return ret;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index c75ba47..f94a61c 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2703,7 +2703,7 @@ static void test_junction_points(void)
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
- DWORD dwret, dwLen, dwFlags;
+ DWORD dwret, dwLen, dwFlags, err;
INT buffer_len, string_len;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
@@ -2794,6 +2794,38 @@ static void test_junction_points(void)
new_attrib.LastAccessTime.QuadPart, old_attrib.LastAccessTime.QuadPart);
CloseHandle(hJunction);
+ /* Check deleting a junction point as if it were a directory */
+ HeapFree(GetProcessHeap(), 0, buffer);
+ 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);
+ bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
+ CloseHandle(hJunction);
+ bret = RemoveDirectoryW(junction_path);
+ ok(bret, "Failed to delete junction point as directory!\n");
+ dwret = GetFileAttributesW(junction_path);
+ ok(dwret == (DWORD)~0, "Junction point still exists (attributes: 0x%x)!\n", dwret);
+
+ /* Check deleting a junction point as if it were a file */
+ HeapFree(GetProcessHeap(), 0, buffer);
+ 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);
+ bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
+ CloseHandle(hJunction);
+ bret = DeleteFileW(junction_path);
+ ok(!bret, "Succeeded in deleting junction point as file!\n");
+ err = GetLastError();
+ ok(err == ERROR_ACCESS_DENIED, "Expected last error 0x%x for DeleteFile on junction point (actually 0x%x)!\n",
+ ERROR_ACCESS_DENIED, err);
+ dwret = GetFileAttributesW(junction_path);
+ ok(dwret != (DWORD)~0, "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);
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString( &nameW );
--
1.7.9.5

View File

@ -0,0 +1,26 @@
From cc928f1a52250242fd9e3dec8cd159216535f08f Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:03:47 -0700
Subject: kernel32: Advertise junction point support.
---
dlls/kernel32/volume.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c
index 1509d73..1e3ff7b 100644
--- a/dlls/kernel32/volume.c
+++ b/dlls/kernel32/volume.c
@@ -853,7 +853,8 @@ fill_fs_info: /* now fill in the information that depends on the file system ty
default:
if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
if (filename_len) *filename_len = 255;
- if (flags) *flags = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
+ if (flags) *flags = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS
+ | FILE_SUPPORTS_REPARSE_POINTS;
break;
}
ret = TRUE;
--
1.7.9.5

View File

@ -0,0 +1,34 @@
From f70c0d8ae00629148df5e16efb30960704b8bbf8 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:06:24 -0700
Subject: ntdll/tests: Add test for deleting junction point target.
---
dlls/ntdll/tests/file.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index f94a61c..ffb9ff2 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -2826,6 +2826,17 @@ static void test_junction_points(void)
ok(dwret != (DWORD)~0, "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 */,
+ "Unexpected junction point attributes (0x%x != 0x410)!\n", dwret);
+ bret = RemoveDirectoryW(target_path);
+ ok(bret, "Failed to delete junction point target!\n");
+ ok(dwret == 0x410 || broken(dwret == 0x430) /* win2k */,
+ "Unexpected junction point attributes (0x%x != 0x410)!\n", dwret);
+ bret = CreateDirectoryW(target_path, NULL);
+ ok(bret, "Failed to create junction point target directory.\n");
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString( &nameW );
--
1.7.9.5

View File

@ -0,0 +1,69 @@
From f8bf15e30d5e0b9e30ceb644c07449b7782efb72 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:07:43 -0700
Subject: ntdll: Use relative paths for creating links.
---
dlls/ntdll/file.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index b4e06d1..6907b2b 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1487,6 +1487,7 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
int dest_fd, needs_close;
UNICODE_STRING nt_dest;
NTSTATUS status;
+ char *p;
if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
return status;
@@ -1500,6 +1501,44 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
goto cleanup;
dest_allocated = TRUE;
+ p = strstr(unix_src.Buffer, "/dosdevices/");
+ if (p)
+ {
+ int count = -1; /* do not count the slash at the end of dosdevices or the last directory */
+
+ p += 11; /* strlen("/dosdevices") */
+ do
+ {
+ p++; /* skip the slash */
+ count++;
+ p = strchr(p, '/');
+ } while(p);
+ FIXME("found %d directories up.\n", count);
+ p = strstr(unix_dest.Buffer, "/dosdevices/");
+ if (p)
+ {
+ ANSI_STRING tmp;
+ int dest_len;
+ char *d;
+
+ p += 12; /* strlen("/dosdevices/") */
+ dest_len = unix_dest.Length - (p-unix_dest.Buffer) + 1;
+ tmp.Length = dest_len + 3*count; /* strlen("../") = 3 */
+ tmp.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, tmp.Length);
+ d = tmp.Buffer;
+ for(; count > 0; count--)
+ {
+ (d++)[0] = '.';
+ (d++)[0] = '.';
+ (d++)[0] = '/';
+ }
+ memcpy(d, p, dest_len);
+ RtlFreeAnsiString( &unix_dest );
+ unix_dest.Length = tmp.Length;
+ unix_dest.Buffer = tmp.Buffer;
+ }
+ }
+
TRACE("Linking %s to %s\n", unix_src.Buffer, unix_dest.Buffer);
/* Produce the link in a temporary location */
--
1.7.9.5

View File

@ -0,0 +1,4 @@
Revision: 1
Author: Erich E. Hoover
Title: Support for junction points/reparse points.

View File

@ -33,7 +33,7 @@ diff --git a/libs/wine/config.c b/libs/wine/config.c
index a273502..5fa0cd5 100644
--- a/libs/wine/config.c
+++ b/libs/wine/config.c
@@ -478,6 +478,34 @@ const char *wine_get_version(void)
@@ -478,6 +478,35 @@ const char *wine_get_version(void)
return PACKAGE_VERSION;
}
@ -50,6 +50,7 @@ index a273502..5fa0cd5 100644
+ { "cbe240e8-2c58-430a-b61c-7fbb9d0e1e11:1", "Sebastian Lackner", "Change return value of stub SetNamedPipeHandleState to TRUE." },
+ { "00273da7-72f8-4025-9e96-0c2bc95dacdb:2", "Maarten Lankhorst", "Winepulse patches extracted from https://launchpad.net/~mlankhorst/+archive/ppa/+files/wine1.7_1.7.10-0ubuntu1~saucy1.debian.tar.gz." },
+ { "29b2af38-7edd-11e3-a08d-0090f5c75ad5:1", "Erich E. Hoover", "Add support for security access parameters for named pipes." },
+ { "4cd13e94-7f2d-11e3-b5eb-0090f5c75ad5:1", "Erich E. Hoover", "Support for junction points/reparse points." },
+ { "5fb1f5c8-7f17-11e3-9b62-0090f5c75ad5:1", "Erich E. Hoover", "Implement TransmitFile." },
+ { "0b21d7ac-0387-4493-aa38-fbafe3e749f5:1", "Michael Müller", "Decrease minimum SetTimer interval from 15 to 5 ms." },
+ { "19835498-8d90-4673-867e-2376af4d7c76:1", "Sebastian Lackner", "Allow to set wined3d strictDrawOrdering via environment variable." },