mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-01-28 22:04:43 -08:00
Updated ntdll-Junction_Points patchset
This commit is contained in:
parent
bfd8af12aa
commit
36e84f2951
@ -1 +1,2 @@
|
||||
Fixes: [44948] kernel32: Implement CreateSymbolicLink
|
||||
Disabled: True
|
||||
|
@ -1,34 +1,55 @@
|
||||
From b68ba9df8489f913125e1aaaaf1203986f1ff346 Mon Sep 17 00:00:00 2001
|
||||
From 0d2ea784da8e7b644da39d00852b7470cc3b634e 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: [PATCH] ntdll: Add support for junction point creation.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/ddk/ntifs.h | 26 ++++++++++++++
|
||||
3 files changed, 211 insertions(+)
|
||||
configure.ac | 1 +
|
||||
dlls/ntdll/file.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 101 +++++++++++++++++++++++++++++++++++++++++
|
||||
include/Makefile.in | 1 +
|
||||
include/ntifs.h | 42 ++++++++++++++++++
|
||||
include/wine/port.h | 9 ++++
|
||||
libs/port/Makefile.in | 1 +
|
||||
libs/port/renameat2.c | 44 ++++++++++++++++++
|
||||
8 files changed, 315 insertions(+)
|
||||
create mode 100644 include/ntifs.h
|
||||
create mode 100644 libs/port/renameat2.c
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index cf896c1..3275061 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -2219,6 +2219,7 @@ AC_CHECK_FUNCS(\
|
||||
pwrite \
|
||||
readdir \
|
||||
readlink \
|
||||
+ renameat2 \
|
||||
sched_yield \
|
||||
select \
|
||||
setproctitle \
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index c1de955..c1075cf 100644
|
||||
index c1de955..6337cc8 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -108,12 +108,14 @@
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
+#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
@@ -108,6 +109,7 @@
|
||||
#include "winioctl.h"
|
||||
#include "ddk/ntddk.h"
|
||||
#include "ddk/ntddser.h"
|
||||
+#include "ddk/ntifs.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)
|
||||
|
||||
@@ -1746,6 +1748,76 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
|
||||
@@ -1746,6 +1748,102 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
|
||||
}
|
||||
|
||||
|
||||
@ -38,22 +59,24 @@ index c1de955..c1075cf 100644
|
||||
+ */
|
||||
+NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
+{
|
||||
+ BOOL src_allocated = FALSE, dest_allocated = FALSE, tempdir_created = FALSE;
|
||||
+ int dest_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
+ int offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
|
||||
+ WCHAR *dest = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
+ char tmplink[] = WINE_TEMPLINK;
|
||||
+ char tmpdir[PATH_MAX], tmplink[PATH_MAX];
|
||||
+ ANSI_STRING unix_src, unix_dest;
|
||||
+ BOOL dest_allocated = FALSE;
|
||||
+ char magic_dest[PATH_MAX];
|
||||
+ int dest_fd, needs_close;
|
||||
+ UNICODE_STRING nt_dest;
|
||||
+ NTSTATUS status;
|
||||
+ int i;
|
||||
+
|
||||
+ 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;
|
||||
+
|
||||
+ src_allocated = TRUE;
|
||||
+ nt_dest.Buffer = dest;
|
||||
+ nt_dest.Length = dest_len;
|
||||
+ if ((status = wine_nt_to_unix_file_name( &nt_dest, &unix_dest, FILE_OPEN, FALSE )))
|
||||
@ -62,41 +85,65 @@ index c1de955..c1075cf 100644
|
||||
+
|
||||
+ TRACE("Linking %s to %s\n", unix_src.Buffer, unix_dest.Buffer);
|
||||
+
|
||||
+ /* Produce the link in a temporary location */
|
||||
+ while(1)
|
||||
+ /* Encode the reparse tag into the symlink */
|
||||
+ strcpy( magic_dest, "/" );
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ int fd;
|
||||
+ if ((buffer->ReparseTag >> i) & 1)
|
||||
+ strcat( magic_dest, "." );
|
||||
+ strcat( magic_dest, "/" );
|
||||
+ }
|
||||
+ strcat( magic_dest, unix_dest.Buffer );
|
||||
+
|
||||
+ 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);
|
||||
+ /* Produce the link in a temporary location in the same folder */
|
||||
+ strcpy( tmpdir, unix_src.Buffer );
|
||||
+ dirname( tmpdir) ;
|
||||
+ strcat( tmpdir, "/.winelink.XXXXXX" );
|
||||
+ if (mkdtemp( tmpdir ) == NULL)
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ tempdir_created = TRUE;
|
||||
+ strcpy( tmplink, tmpdir );
|
||||
+ strcat( tmplink, "/tmplink" );
|
||||
+ if (symlink( magic_dest, tmplink ))
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ /* Atomically move the link into position */
|
||||
+ if (rename( tmplink, unix_src.Buffer ))
|
||||
+ if (!renameat2( -1, tmplink, -1, unix_src.Buffer, RENAME_EXCHANGE ))
|
||||
+ {
|
||||
+ 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)
|
||||
+ /* success: link and folder have switched locations */
|
||||
+ rmdir( tmplink ); /* remove the folder (at link location) */
|
||||
+ }
|
||||
+ else if (errno == ENOSYS)
|
||||
+ {
|
||||
+ FIXME( "Atomic exchange of directory with symbolic link unsupported on this system, "
|
||||
+ "using unsafe exchange instead.\n" );
|
||||
+ if (rmdir( unix_src.Buffer ))
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (symlink( unix_dest.Buffer, unix_src.Buffer ) < 0)
|
||||
+ if (rename( tmplink, unix_src.Buffer ))
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ goto cleanup; /* not moved, orignal file/folder at destination is orphaned */
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ status = STATUS_SUCCESS;
|
||||
+
|
||||
+cleanup:
|
||||
+ if (tempdir_created) rmdir( tmpdir );
|
||||
+ if (dest_allocated) RtlFreeAnsiString( &unix_dest );
|
||||
+ if (src_allocated) RtlFreeAnsiString( &unix_src );
|
||||
+ if (needs_close) close( dest_fd );
|
||||
+ return status;
|
||||
+}
|
||||
@ -105,13 +152,7 @@ index c1de955..c1075cf 100644
|
||||
/**************************************************************************
|
||||
* NtFsControlFile [NTDLL.@]
|
||||
* ZwFsControlFile [NTDLL.@]
|
||||
@@ -1825,11 +1897,30 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
}
|
||||
break;
|
||||
}
|
||||
+
|
||||
case FSCTL_SET_SPARSE:
|
||||
TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
|
||||
@@ -1830,6 +1928,24 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
io->Information = 0;
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
@ -137,44 +178,51 @@ index c1de955..c1075cf 100644
|
||||
return server_ioctl_file( handle, event, apc, apc_context, io, code,
|
||||
in_buffer, in_size, out_buffer, out_size );
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 74adf4c..ed3ddea 100644
|
||||
index 74adf4c..1eedfaa 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "winuser.h"
|
||||
#include "winioctl.h"
|
||||
#include "winnls.h"
|
||||
+#include "ddk/ntifs.h"
|
||||
+#include "ntifs.h"
|
||||
|
||||
#ifndef IO_COMPLETION_ALL_ACCESS
|
||||
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
|
||||
@@ -4982,6 +4983,98 @@ static void test_query_ea(void)
|
||||
#undef EA_BUFFER_SIZE
|
||||
@@ -5050,6 +5051,105 @@ static void test_file_readonly_access(void)
|
||||
DeleteFileW(path);
|
||||
}
|
||||
|
||||
+static INT build_reparse_buffer(WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer)
|
||||
+static INT build_reparse_buffer(const WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer)
|
||||
+{
|
||||
+ static INT header_size = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer);
|
||||
+ INT buffer_size, struct_size, data_size, string_len, prefix_len;
|
||||
+ WCHAR *subst_dest, *print_dest;
|
||||
+ 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);
|
||||
+ struct_size = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]);
|
||||
+ prefix_len = strlen("\\??\\");
|
||||
+ string_len = lstrlenW(&filename[prefix_len]);
|
||||
+ data_size = (prefix_len + 2 * string_len + 2) * sizeof(WCHAR);
|
||||
+ buffer_size = struct_size + data_size;
|
||||
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size);
|
||||
+ 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);
|
||||
+ buffer->ReparseDataLength = struct_size - header_size + data_size;
|
||||
+ buffer->MountPointReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
+ buffer->MountPointReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
+ buffer->MountPointReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
+ subst_dest = &buffer->MountPointReparseBuffer.PathBuffer[0];
|
||||
+ print_dest = &buffer->MountPointReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
+ lstrcpyW(subst_dest, filename);
|
||||
+ lstrcpyW(print_dest, &filename[prefix_len]);
|
||||
+ *pbuffer = buffer;
|
||||
+ return buffer_len;
|
||||
+ return buffer_size;
|
||||
+}
|
||||
+
|
||||
+static void test_junction_points(void)
|
||||
+static void test_reparse_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 reparseW[] = {'\\','r','e','p','a','r','s','e',0};
|
||||
+ WCHAR path[MAX_PATH], reparse_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};
|
||||
@ -182,8 +230,8 @@ index 74adf4c..ed3ddea 100644
|
||||
+ REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
+ DWORD dwret, dwLen, dwFlags;
|
||||
+ UNICODE_STRING nameW;
|
||||
+ HANDLE hJunction;
|
||||
+ INT buffer_len;
|
||||
+ HANDLE handle;
|
||||
+ BOOL bret;
|
||||
+
|
||||
+ /* Create a temporary folder for the junction point tests */
|
||||
@ -202,15 +250,15 @@ index 74adf4c..ed3ddea 100644
|
||||
+ GetVolumeInformationW(volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0);
|
||||
+ if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
|
||||
+ {
|
||||
+ skip("File system does not support junction points.\n");
|
||||
+ skip("File system does not support reparse 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);
|
||||
+ lstrcpyW(reparse_path, path);
|
||||
+ lstrcatW(reparse_path, reparseW);
|
||||
+ bret = CreateDirectoryW(reparse_path, NULL);
|
||||
+ ok(bret, "Failed to create junction point directory.\n");
|
||||
+
|
||||
+ /* Create a destination folder for the junction point to target */
|
||||
@ -221,46 +269,81 @@ index 74adf4c..ed3ddea 100644
|
||||
+ 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)
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ if (handle == 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);
|
||||
+ bret = DeviceIoControl(handle, 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);
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+cleanup:
|
||||
+ /* Cleanup */
|
||||
+ pRtlFreeUnicodeString( &nameW );
|
||||
+ pRtlFreeUnicodeString(&nameW);
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ bret = RemoveDirectoryW(junction_path);
|
||||
+ ok(bret, "Failed to remove temporary junction point directory!\n");
|
||||
+ bret = RemoveDirectoryW(reparse_path);
|
||||
+ todo_wine ok(bret, "Failed to remove temporary reparse point directory!\n");
|
||||
+ bret = RemoveDirectoryW(target_path);
|
||||
+ ok(bret, "Failed to remove temporary target directory!\n");
|
||||
+ RemoveDirectoryW(path);
|
||||
+}
|
||||
+
|
||||
static void test_file_readonly_access(void)
|
||||
START_TEST(file)
|
||||
{
|
||||
static const DWORD default_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
@@ -5122,4 +5215,5 @@ START_TEST(file)
|
||||
HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
|
||||
@@ -5120,6 +5220,7 @@ START_TEST(file)
|
||||
test_query_volume_information_file();
|
||||
test_query_attribute_information_file();
|
||||
test_ioctl();
|
||||
+ test_reparse_points();
|
||||
test_flush_buffers_file();
|
||||
test_query_ea();
|
||||
+ test_junction_points();
|
||||
}
|
||||
diff --git a/include/ddk/ntifs.h b/include/ddk/ntifs.h
|
||||
index abe357f..3cba319 100644
|
||||
--- a/include/ddk/ntifs.h
|
||||
+++ b/include/ddk/ntifs.h
|
||||
@@ -133,4 +133,30 @@ BOOLEAN WINAPI FsRtlIsNameInExpression(PUNICODE_STRING, PUNICODE_STRING, BOOLEAN
|
||||
NTSTATUS WINAPI ObQueryNameString(PVOID,POBJECT_NAME_INFORMATION,ULONG,PULONG);
|
||||
void WINAPI PsRevertToSelf(void);
|
||||
|
||||
diff --git a/include/Makefile.in b/include/Makefile.in
|
||||
index 31a811d..eeeff7b 100644
|
||||
--- a/include/Makefile.in
|
||||
+++ b/include/Makefile.in
|
||||
@@ -482,6 +482,7 @@ SOURCES = \
|
||||
ntddvdeo.h \
|
||||
ntdef.h \
|
||||
ntdsapi.h \
|
||||
+ ntifs.h \
|
||||
ntlsa.h \
|
||||
ntquery.h \
|
||||
ntsecapi.h \
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
new file mode 100644
|
||||
index 0000000..21d42e1
|
||||
--- /dev/null
|
||||
+++ b/include/ntifs.h
|
||||
@@ -0,0 +1,42 @@
|
||||
+/*
|
||||
+ * Win32 definitions for Windows NT
|
||||
+ *
|
||||
+ * Copyright 2012 Erich E. 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;
|
||||
@ -271,14 +354,6 @@ index abe357f..3cba319 100644
|
||||
+ 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 {
|
||||
@ -287,7 +362,89 @@ index abe357f..3cba319 100644
|
||||
+ };
|
||||
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
+
|
||||
+#endif /* __WINE_NTIFS_H */
|
||||
diff --git a/include/wine/port.h b/include/wine/port.h
|
||||
index 2b8a069..4bbf999 100644
|
||||
--- a/include/wine/port.h
|
||||
+++ b/include/wine/port.h
|
||||
@@ -317,6 +317,15 @@ double rint(double x);
|
||||
float rintf(float x);
|
||||
#endif
|
||||
|
||||
+#ifndef RENAME_EXCHANGE
|
||||
+#define RENAME_EXCHANGE (1 << 1)
|
||||
+#endif /* RENAME_EXCHANGE */
|
||||
+
|
||||
+#ifndef HAVE_RENAMEAT2
|
||||
+int renameat2( int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
|
||||
+ unsigned int flags );
|
||||
+#endif /* HAVE_RENAMEAT2 */
|
||||
+
|
||||
#ifndef HAVE_STATVFS
|
||||
int statvfs( const char *path, struct statvfs *buf );
|
||||
#endif
|
||||
diff --git a/libs/port/Makefile.in b/libs/port/Makefile.in
|
||||
index b975418..790fd96 100644
|
||||
--- a/libs/port/Makefile.in
|
||||
+++ b/libs/port/Makefile.in
|
||||
@@ -97,6 +97,7 @@ C_SRCS = \
|
||||
pread.c \
|
||||
pwrite.c \
|
||||
readlink.c \
|
||||
+ renameat2.c \
|
||||
rint.c \
|
||||
sortkey.c \
|
||||
spawn.c \
|
||||
diff --git a/libs/port/renameat2.c b/libs/port/renameat2.c
|
||||
new file mode 100644
|
||||
index 0000000..39b4053
|
||||
--- /dev/null
|
||||
+++ b/libs/port/renameat2.c
|
||||
@@ -0,0 +1,44 @@
|
||||
+/*
|
||||
+ * renameat2 function
|
||||
+ *
|
||||
+ * Copyright 2015 Erich E. 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
|
||||
+ */
|
||||
+
|
||||
+#include "config.h"
|
||||
+#include "wine/port.h"
|
||||
+
|
||||
+#ifdef HAVE_SYS_SYSCALL_H
|
||||
+# include <sys/syscall.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+#ifndef HAVE_RENAMEAT2
|
||||
+int renameat2( int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
|
||||
+ unsigned int flags )
|
||||
+{
|
||||
+ if (flags == 0)
|
||||
+ return renameat( olddirfd, oldpath, newdirfd, newpath );
|
||||
+#ifdef __NR_renameat2
|
||||
+ return syscall( __NR_renameat2, olddirfd, oldpath, newdirfd, newpath, flags );
|
||||
+#else
|
||||
+ errno = ENOSYS;
|
||||
+ return -1;
|
||||
+#endif
|
||||
+}
|
||||
+#endif /* HAVE_RENAMEAT2 */
|
||||
--
|
||||
1.9.1
|
||||
|
||||
|
@ -1,34 +1,39 @@
|
||||
From 4489ca1cdd1eeb62b129e4f83278da14af653468 Mon Sep 17 00:00:00 2001
|
||||
From 47cc80d8a94523f36a19906b8d647f3e58e346ef 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.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 14 ++++++++++-
|
||||
2 files changed, 76 insertions(+), 1 deletion(-)
|
||||
dlls/ntdll/file.c | 106 ++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 14 +++++-
|
||||
2 files changed, 119 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index d8bb01b..6e73547 100644
|
||||
index 5f2da14398..ee561d9268 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1727,6 +1727,60 @@ cleanup:
|
||||
@@ -1746,6 +1746,106 @@ 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.
|
||||
+ * 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)
|
||||
+NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_size)
|
||||
+{
|
||||
+ ANSI_STRING unix_src, unix_dest;
|
||||
+ VOID *subst_name, *print_name;
|
||||
+ BOOL dest_allocated = FALSE;
|
||||
+ int dest_fd, needs_close;
|
||||
+ UNICODE_STRING nt_dest;
|
||||
+ DWORD max_length;
|
||||
+ NTSTATUS status;
|
||||
+ VOID *dest_name;
|
||||
+ INT prefix_len;
|
||||
+ ssize_t ret;
|
||||
+ char *p;
|
||||
+ int i;
|
||||
+
|
||||
+ if ((status = server_get_unix_fd( handle, FILE_ANY_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
|
||||
+ return status;
|
||||
@ -47,20 +52,62 @@ index d8bb01b..6e73547 100644
|
||||
+ }
|
||||
+ unix_dest.Length = ret;
|
||||
+
|
||||
+ /* Decode the reparse tag from the symlink */
|
||||
+ p = unix_dest.Buffer;
|
||||
+ if (*p++ != '/')
|
||||
+ {
|
||||
+ status = STATUS_NOT_IMPLEMENTED;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ buffer->ReparseTag = 0;
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+ int val;
|
||||
+
|
||||
+ if (c == '/')
|
||||
+ val = 0;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ val = 1;
|
||||
+ else
|
||||
+ {
|
||||
+ status = STATUS_NOT_IMPLEMENTED;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ buffer->ReparseTag |= (val << i);
|
||||
+ }
|
||||
+ unix_dest.Length -= (p - unix_dest.Buffer);
|
||||
+ memmove(unix_dest.Buffer, p, unix_dest.Length);
|
||||
+
|
||||
+ if ((status = wine_unix_to_nt_file_name( &unix_dest, &nt_dest )))
|
||||
+ goto cleanup;
|
||||
+
|
||||
+ if (nt_dest.Length > buffer->MountPointReparseBuffer.SubstituteNameLength)
|
||||
+ prefix_len = strlen("\\??\\");
|
||||
+ switch(buffer->ReparseTag)
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[1]);
|
||||
+ buffer->MountPointReparseBuffer.SubstituteNameOffset = 0;
|
||||
+ buffer->MountPointReparseBuffer.SubstituteNameLength = nt_dest.Length;
|
||||
+ subst_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
|
||||
+ buffer->MountPointReparseBuffer.PrintNameOffset = nt_dest.Length + sizeof(WCHAR);
|
||||
+ buffer->MountPointReparseBuffer.PrintNameLength = nt_dest.Length - prefix_len*sizeof(WCHAR);
|
||||
+ print_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
|
||||
+ break;
|
||||
+ default:
|
||||
+ /* unrecognized (regular) files should probably be treated as symlinks */
|
||||
+ WARN("unrecognized symbolic link\n");
|
||||
+ status = STATUS_NOT_IMPLEMENTED;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (nt_dest.Length > max_length)
|
||||
+ {
|
||||
+ 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 );
|
||||
+ memcpy( subst_name, nt_dest.Buffer, nt_dest.Length );
|
||||
+ memcpy( print_name, &nt_dest.Buffer[prefix_len], nt_dest.Length - prefix_len*sizeof(WCHAR) );
|
||||
+ status = STATUS_SUCCESS;
|
||||
+
|
||||
+cleanup:
|
||||
@ -73,56 +120,53 @@ index d8bb01b..6e73547 100644
|
||||
/**************************************************************************
|
||||
* NtFsControlFile [NTDLL.@]
|
||||
* ZwFsControlFile [NTDLL.@]
|
||||
@@ -1882,6 +1936,15 @@ NTSTATUS WINAPI SYSCALL(NtFsControlFile)(HANDLE handle, HANDLE event, PIO_APC_RO
|
||||
@@ -1831,6 +1931,12 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
status = STATUS_SUCCESS;
|
||||
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 );
|
||||
+ status = FILE_GetSymlink( handle, buffer, out_size );
|
||||
+ 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 71e10c2..e07e6a8 100644
|
||||
index 322dadefe3..cdb608d305 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4303,9 +4303,10 @@ static void test_junction_points(void)
|
||||
@@ -4868,9 +4868,10 @@ static void test_reparse_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;
|
||||
HANDLE handle;
|
||||
+ WCHAR *dest;
|
||||
BOOL bret;
|
||||
|
||||
/* Create a temporary folder for the junction point tests */
|
||||
@@ -4353,6 +4354,17 @@ static void test_junction_points(void)
|
||||
@@ -4918,6 +4919,17 @@ static void test_reparse_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);
|
||||
bret = DeviceIoControl(handle, 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);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buffer, buffer_len, &dwret, 0);
|
||||
+ ok(bret, "Failed to read junction point!\n");
|
||||
+ 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);
|
||||
CloseHandle(handle);
|
||||
|
||||
cleanup:
|
||||
--
|
||||
2.6.1
|
||||
2.17.1
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
From 1fe09b6fd1dd6fe7520932c2c226728301d5d099 Mon Sep 17 00:00:00 2001
|
||||
From 272ed659e67adc42778e428ac4b74d2844dc9ad5 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: [PATCH] ntdll: Add support for deleting junction points.
|
||||
Subject: ntdll: Add support for deleting junction points.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 22 +++++++++++++++++++++
|
||||
include/ddk/ntifs.h | 12 ++++++++++++
|
||||
3 files changed, 85 insertions(+)
|
||||
dlls/ntdll/file.c | 51 +++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 23 ++++++++++++++++++-
|
||||
include/ntifs.h | 12 ++++++++++
|
||||
3 files changed, 85 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 66cc16a..9b8d4c8 100644
|
||||
index ee561d9268..2effe7efb0 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1775,6 +1775,41 @@ cleanup:
|
||||
@@ -1846,6 +1846,41 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +56,7 @@ index 66cc16a..9b8d4c8 100644
|
||||
/**************************************************************************
|
||||
* NtFsControlFile [NTDLL.@]
|
||||
* ZwFsControlFile [NTDLL.@]
|
||||
@@ -1861,6 +1896,22 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
@@ -1931,6 +1966,22 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
|
||||
@ -79,12 +80,12 @@ index 66cc16a..9b8d4c8 100644
|
||||
{
|
||||
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 94d5a04..a92961d 100644
|
||||
index cdb608d305..bc6961bc61 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4565,12 +4565,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];
|
||||
@@ -4863,12 +4863,15 @@ static void test_reparse_points(void)
|
||||
static const WCHAR reparseW[] = {'\\','r','e','p','a','r','s','e',0};
|
||||
WCHAR path[MAX_PATH], reparse_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};
|
||||
@ -96,46 +97,54 @@ index 94d5a04..a92961d 100644
|
||||
INT buffer_len, string_len;
|
||||
+ IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING nameW;
|
||||
HANDLE hJunction;
|
||||
HANDLE handle;
|
||||
WCHAR *dest;
|
||||
@@ -4618,6 +4621,8 @@ static void test_junction_points(void)
|
||||
@@ -4916,6 +4919,8 @@ static void test_reparse_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);
|
||||
+ dwret = NtQueryInformationFile(handle, &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);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
@@ -4632,6 +4637,23 @@ static void test_junction_points(void)
|
||||
ok(bret, "Failed to read junction point!\n");
|
||||
@@ -4930,6 +4935,22 @@ static void test_reparse_points(void)
|
||||
dest = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
|
||||
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);
|
||||
+ dwret = NtSetInformationFile(handle, &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,
|
||||
+ bret = DeviceIoControl(handle, 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);
|
||||
+ dwret = NtQueryInformationFile(handle, &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.\n");
|
||||
CloseHandle(hJunction);
|
||||
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ "Junction point folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
cleanup:
|
||||
diff --git a/include/ddk/ntifs.h b/include/ddk/ntifs.h
|
||||
index ccbd2a7..679d8ba 100644
|
||||
--- a/include/ddk/ntifs.h
|
||||
+++ b/include/ddk/ntifs.h
|
||||
@@ -158,4 +158,16 @@ typedef struct _REPARSE_DATA_BUFFER {
|
||||
@@ -4937,7 +4958,7 @@ cleanup:
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
- todo_wine ok(bret, "Failed to remove temporary reparse point directory!\n");
|
||||
+ ok(bret, "Failed to remove temporary reparse point directory!\n");
|
||||
bret = RemoveDirectoryW(target_path);
|
||||
ok(bret, "Failed to remove temporary target directory!\n");
|
||||
RemoveDirectoryW(path);
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
index 21d42e1732..4539b89d58 100644
|
||||
--- a/include/ntifs.h
|
||||
+++ b/include/ntifs.h
|
||||
@@ -39,4 +39,16 @@ typedef struct _REPARSE_DATA_BUFFER {
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
|
||||
@ -151,7 +160,7 @@ index ccbd2a7..679d8ba 100644
|
||||
+
|
||||
+#define REPARSE_GUID_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer)
|
||||
+
|
||||
#endif
|
||||
#endif /* __WINE_NTIFS_H */
|
||||
--
|
||||
2.7.4
|
||||
2.17.1
|
||||
|
||||
|
@ -1,22 +1,23 @@
|
||||
From 4950278da6011ac509313ad2cdbea7301423a91d Mon Sep 17 00:00:00 2001
|
||||
From 719d62a3f338279f466d12a628a9e6ec78f65b00 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: Add a test for junction point advertisement.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 5 +++++
|
||||
dlls/ntdll/tests/file.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index f84f6ea..965ca62 100644
|
||||
index bc6961bc61..e1f216370d 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -2770,6 +2770,11 @@ static void test_junction_points(void)
|
||||
bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
@@ -4925,6 +4925,11 @@ static void test_reparse_points(void)
|
||||
bret = DeviceIoControl(handle, 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);
|
||||
+ dwret = GetFileAttributesW(reparse_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);
|
||||
+
|
||||
@ -24,5 +25,5 @@ index f84f6ea..965ca62 100644
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
|
||||
--
|
||||
1.7.9.5
|
||||
2.17.1
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
From b4d6d566795a8fd35176414297a58dc6596900eb Mon Sep 17 00:00:00 2001
|
||||
From a8eb436342b0f261ba09c0a4bb1a87f0a85b4d85 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.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernel32/path.c | 13 +++++++++++--
|
||||
dlls/kernel32/path.c | 20 +++++++++++++-------
|
||||
dlls/ntdll/tests/file.c | 34 +++++++++++++++++++++++++++++++++-
|
||||
2 files changed, 44 insertions(+), 3 deletions(-)
|
||||
2 files changed, 46 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
|
||||
index 620401d..11711b3 100644
|
||||
index cf1c768970..6369225b3e 100644
|
||||
--- a/dlls/kernel32/path.c
|
||||
+++ b/dlls/kernel32/path.c
|
||||
@@ -1630,6 +1630,7 @@ BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRI
|
||||
@@ -1696,6 +1696,7 @@ BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRI
|
||||
*/
|
||||
BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
|
||||
{
|
||||
@ -21,37 +22,40 @@ index 620401d..11711b3 100644
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
UNICODE_STRING nt_name;
|
||||
ANSI_STRING unix_name;
|
||||
@@ -1663,15 +1664,23 @@ BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
|
||||
@@ -1729,16 +1730,21 @@ BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
|
||||
}
|
||||
|
||||
status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
|
||||
- RtlFreeUnicodeString( &nt_name );
|
||||
if (status != STATUS_SUCCESS)
|
||||
- if (status != STATUS_SUCCESS)
|
||||
+ if (status == STATUS_SUCCESS)
|
||||
{
|
||||
SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ RtlFreeUnicodeString( &nt_name );
|
||||
NtClose( handle );
|
||||
return FALSE;
|
||||
- SetLastError( RtlNtStatusToDosError(status) );
|
||||
- NtClose( handle );
|
||||
- return FALSE;
|
||||
+ status = NtQueryAttributesFile( &attr, &info );
|
||||
+ if (status == STATUS_SUCCESS && (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 );
|
||||
}
|
||||
+ else
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ RtlFreeUnicodeString( &nt_name );
|
||||
|
||||
- if (!(ret = (rmdir( unix_name.Buffer ) != -1))) FILE_SetDosError();
|
||||
+ status = NtQueryAttributesFile( &attr, &info );
|
||||
+ if (status == STATUS_SUCCESS && (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();
|
||||
+
|
||||
+ RtlFreeUnicodeString( &nt_name );
|
||||
RtlFreeAnsiString( &unix_name );
|
||||
- RtlFreeAnsiString( &unix_name );
|
||||
NtClose( handle );
|
||||
return ret;
|
||||
}
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 174d48a..f7b7d48 100644
|
||||
index e1f216370d..71e7dd56f3 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4271,7 +4271,7 @@ static void test_junction_points(void)
|
||||
@@ -4869,7 +4869,7 @@ static void test_reparse_points(void)
|
||||
REPARSE_GUID_DATA_BUFFER guid_buffer;
|
||||
static const WCHAR dotW[] = {'.',0};
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
@ -60,45 +64,45 @@ index 174d48a..f7b7d48 100644
|
||||
INT buffer_len, string_len;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING nameW;
|
||||
@@ -4361,6 +4361,38 @@ static void test_junction_points(void)
|
||||
"Junction point folder's access time does not match.\n");
|
||||
CloseHandle(hJunction);
|
||||
@@ -4958,6 +4958,38 @@ static void test_reparse_points(void)
|
||||
"Junction point folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* 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,
|
||||
+ handle = CreateFileW(reparse_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);
|
||||
+ bret = DeviceIoControl(handle, 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);
|
||||
+ CloseHandle(handle);
|
||||
+ bret = RemoveDirectoryW(reparse_path);
|
||||
+ ok(bret, "Failed to delete junction point as directory!\n");
|
||||
+ dwret = GetFileAttributesW(junction_path);
|
||||
+ dwret = GetFileAttributesW(reparse_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);
|
||||
+ bret = CreateDirectoryW(reparse_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);
|
||||
+ handle = CreateFileW(reparse_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);
|
||||
+ bret = DeviceIoControl(handle, 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);
|
||||
+ CloseHandle(handle);
|
||||
+ bret = DeleteFileW(reparse_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);
|
||||
+ dwret = GetFileAttributesW(reparse_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 );
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
--
|
||||
2.8.0
|
||||
2.17.1
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
From 7f97bca6c4153b373afbc621916d339d44e9241d Mon Sep 17 00:00:00 2001
|
||||
From 4fe08afd847dc6c1c89eb90f647faafe0be2f4a8 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.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernel32/volume.c | 3 ++-
|
||||
dlls/ntdll/tests/file.c | 5 ++---
|
||||
2 files changed, 4 insertions(+), 4 deletions(-)
|
||||
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 4f01c99..4f11e09 100644
|
||||
index 12084369cb..b8ab8896b9 100644
|
||||
--- a/dlls/kernel32/volume.c
|
||||
+++ b/dlls/kernel32/volume.c
|
||||
@@ -854,7 +854,8 @@ fill_fs_info: /* now fill in the information that depends on the file system ty
|
||||
@@ -824,7 +824,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;
|
||||
@ -22,22 +22,6 @@ index 4f01c99..4f11e09 100644
|
||||
break;
|
||||
}
|
||||
ret = TRUE;
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index f7b7d48..3091283 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4356,9 +4356,8 @@ static void test_junction_points(void)
|
||||
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.\n");
|
||||
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ "Junction point folder's access time does not match.\n");
|
||||
CloseHandle(hJunction);
|
||||
|
||||
/* Check deleting a junction point as if it were a directory */
|
||||
--
|
||||
2.7.1
|
||||
2.17.1
|
||||
|
||||
|
@ -0,0 +1,286 @@
|
||||
From 084864a69084f9e01e53ee289681a342d1ec0a05 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: Add support for absolute symlink creation.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 35 +++++++++++--
|
||||
dlls/ntdll/tests/file.c | 109 +++++++++++++++++++++++++++++++++++-----
|
||||
include/ntifs.h | 10 ++++
|
||||
3 files changed, 139 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 2effe7efb0..00e3d7aa31 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1657,17 +1657,33 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
|
||||
NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
{
|
||||
BOOL src_allocated = FALSE, dest_allocated = FALSE, tempdir_created = FALSE;
|
||||
- int dest_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
- int offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
|
||||
- WCHAR *dest = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
char tmpdir[PATH_MAX], tmplink[PATH_MAX];
|
||||
ANSI_STRING unix_src, unix_dest;
|
||||
char magic_dest[PATH_MAX];
|
||||
int dest_fd, needs_close;
|
||||
UNICODE_STRING nt_dest;
|
||||
+ int dest_len, offset;
|
||||
NTSTATUS status;
|
||||
+ struct stat st;
|
||||
+ WCHAR *dest;
|
||||
int i;
|
||||
|
||||
+ switch(buffer->ReparseTag)
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ dest_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
+ offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
|
||||
+ dest = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
+ break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ dest_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
+ offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset;
|
||||
+ dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
+ break;
|
||||
+ default:
|
||||
+ return STATUS_NOT_IMPLEMENTED;
|
||||
+ }
|
||||
+
|
||||
if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
|
||||
return status;
|
||||
|
||||
@@ -1690,6 +1706,18 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
strcat( magic_dest, "." );
|
||||
strcat( magic_dest, "/" );
|
||||
}
|
||||
+ /* Encode the type (file or directory) if NT symlink */
|
||||
+ if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ {
|
||||
+ if (fstat( dest_fd, &st ) == -1)
|
||||
+ {
|
||||
+ status = FILE_GetNtStatus();
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (S_ISDIR(st.st_mode))
|
||||
+ strcat( magic_dest, "." );
|
||||
+ strcat( magic_dest, "/" );
|
||||
+ }
|
||||
strcat( magic_dest, unix_dest.Buffer );
|
||||
|
||||
/* Produce the link in a temporary location in the same folder */
|
||||
@@ -1995,6 +2023,7 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
switch(buffer->ReparseTag)
|
||||
{
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
status = FILE_CreateSymlink( handle, buffer );
|
||||
break;
|
||||
default:
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 71e7dd56f3..bdac50cbd6 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4832,26 +4832,50 @@ static void test_file_readonly_access(void)
|
||||
DeleteFileW(path);
|
||||
}
|
||||
|
||||
-static INT build_reparse_buffer(const WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer)
|
||||
+static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, REPARSE_DATA_BUFFER **pbuffer)
|
||||
{
|
||||
static INT header_size = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer);
|
||||
INT buffer_size, struct_size, data_size, string_len, prefix_len;
|
||||
WCHAR *subst_dest, *print_dest;
|
||||
REPARSE_DATA_BUFFER *buffer;
|
||||
|
||||
- struct_size = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]);
|
||||
+ switch(tag)
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ struct_size = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]);
|
||||
+ break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ struct_size = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
prefix_len = strlen("\\??\\");
|
||||
string_len = lstrlenW(&filename[prefix_len]);
|
||||
data_size = (prefix_len + 2 * string_len + 2) * sizeof(WCHAR);
|
||||
buffer_size = struct_size + data_size;
|
||||
buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size);
|
||||
- buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||||
+ buffer->ReparseTag = tag;
|
||||
buffer->ReparseDataLength = struct_size - header_size + data_size;
|
||||
- buffer->MountPointReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
- buffer->MountPointReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
- buffer->MountPointReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
- subst_dest = &buffer->MountPointReparseBuffer.PathBuffer[0];
|
||||
- print_dest = &buffer->MountPointReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
+ switch(tag)
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ buffer->MountPointReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
+ buffer->MountPointReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
+ buffer->MountPointReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
+ subst_dest = &buffer->MountPointReparseBuffer.PathBuffer[0];
|
||||
+ print_dest = &buffer->MountPointReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
+ break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
+ subst_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ print_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
+ break;
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
lstrcpyW(subst_dest, filename);
|
||||
lstrcpyW(print_dest, &filename[prefix_len]);
|
||||
*pbuffer = buffer;
|
||||
@@ -4871,10 +4895,12 @@ static void test_reparse_points(void)
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
INT buffer_len, string_len;
|
||||
+ HANDLE handle, token;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING nameW;
|
||||
- HANDLE handle;
|
||||
+ TOKEN_PRIVILEGES tp;
|
||||
WCHAR *dest;
|
||||
+ LUID luid;
|
||||
BOOL bret;
|
||||
|
||||
/* Create a temporary folder for the junction point tests */
|
||||
@@ -4921,7 +4947,7 @@ static void test_reparse_points(void)
|
||||
}
|
||||
dwret = NtQueryInformationFile(handle, &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);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
|
||||
@@ -4962,7 +4988,7 @@ static void test_reparse_points(void)
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
handle = CreateFileW(reparse_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);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
@@ -4977,7 +5003,7 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to create junction point target directory.\n");
|
||||
handle = CreateFileW(reparse_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);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
@@ -4990,6 +5016,65 @@ static void test_reparse_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(reparse_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");
|
||||
+
|
||||
+ /* Establish permissions for symlink creation */
|
||||
+ bret = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);
|
||||
+ ok(bret, "OpenProcessToken failed: %u\n", GetLastError());
|
||||
+ bret = LookupPrivilegeValueA(NULL, "SeCreateSymbolicLinkPrivilege", &luid);
|
||||
+ todo_wine ok(bret || broken(!bret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE) /* winxp */,
|
||||
+ "LookupPrivilegeValue failed: %u\n", GetLastError());
|
||||
+ if (bret)
|
||||
+ {
|
||||
+ tp.PrivilegeCount = 1;
|
||||
+ tp.Privileges[0].Luid = luid;
|
||||
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
+ bret = AdjustTokenPrivileges(token, FALSE, &tp, 0, NULL, NULL);
|
||||
+ ok(bret, "AdjustTokenPrivileges failed: %u\n", GetLastError());
|
||||
+ if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
||||
+ {
|
||||
+ win_skip("Insufficient permissions to perform symlink tests.\n");
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Delete the junction point directory and create a blank slate for symlink tests */
|
||||
+ bret = RemoveDirectoryW(reparse_path);
|
||||
+ ok(bret, "Failed to delete junction point!\n");
|
||||
+ bret = CreateDirectoryW(reparse_path, NULL);
|
||||
+ ok(bret, "Failed to create junction point directory.\n");
|
||||
+ dwret = GetFileAttributesW(reparse_path);
|
||||
+ ok(dwret != (DWORD)~0, "Path doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
|
||||
+
|
||||
+ /* Create the directory symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ if (handle == INVALID_HANDLE_VALUE)
|
||||
+ {
|
||||
+ win_skip("Failed to open symlink directory handle (0x%x).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
+ ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, &buffer);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
+ CloseHandle(handle);
|
||||
+ ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
+
|
||||
+ /* Check the file attributes of the symlink */
|
||||
+ dwret = GetFileAttributesW(reparse_path);
|
||||
+ ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: %d)\n", dwret);
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
index 4539b89d58..ab3273d3f8 100644
|
||||
--- a/include/ntifs.h
|
||||
+++ b/include/ntifs.h
|
||||
@@ -21,11 +21,20 @@
|
||||
#ifndef __WINE_NTIFS_H
|
||||
#define __WINE_NTIFS_H
|
||||
|
||||
+#include <pshpack2.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;
|
||||
@@ -38,6 +47,7 @@ typedef struct _REPARSE_DATA_BUFFER {
|
||||
} GenericReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
+#include <poppack.h>
|
||||
|
||||
typedef struct _REPARSE_GUID_DATA_BUFFER {
|
||||
DWORD ReparseTag;
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,32 +0,0 @@
|
||||
From e0cefbdf61c85f044d3eaf20e546be542867417e 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 | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 3112081..be6b496 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -2836,6 +2836,15 @@ 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");
|
||||
+ bret = CreateDirectoryW(target_path, NULL);
|
||||
+ ok(bret, "Failed to create junction point target directory.\n");
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString( &nameW );
|
||||
--
|
||||
1.7.9.5
|
||||
|
@ -0,0 +1,92 @@
|
||||
From a482fa218dbe1284c8a08fba84c9f36dec4d3eba Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 12:55:20 -0600
|
||||
Subject: ntdll: Add support for reading absolute symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 22 ++++++++++++++++++++++
|
||||
dlls/ntdll/tests/file.c | 13 ++++++++++++-
|
||||
2 files changed, 34 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 00e3d7aa31..ee8ac6bd10 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1787,6 +1787,7 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
UNICODE_STRING nt_dest;
|
||||
DWORD max_length;
|
||||
NTSTATUS status;
|
||||
+ ULONG flags = 0;
|
||||
INT prefix_len;
|
||||
ssize_t ret;
|
||||
char *p;
|
||||
@@ -1833,6 +1834,17 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
}
|
||||
buffer->ReparseTag |= (val << i);
|
||||
}
|
||||
+ /* skip past the directory/file flag */
|
||||
+ if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+
|
||||
+ if ((c != '/' && c != '.') || (c == '.' && *p++ != '/'))
|
||||
+ {
|
||||
+ status = STATUS_NOT_IMPLEMENTED;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ }
|
||||
unix_dest.Length -= (p - unix_dest.Buffer);
|
||||
memmove(unix_dest.Buffer, p, unix_dest.Length);
|
||||
|
||||
@@ -1851,6 +1863,16 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
buffer->MountPointReparseBuffer.PrintNameLength = nt_dest.Length - prefix_len*sizeof(WCHAR);
|
||||
print_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
|
||||
break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), SymbolicLinkReparseBuffer.PathBuffer[1]);
|
||||
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
|
||||
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = nt_dest.Length;
|
||||
+ subst_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameOffset = nt_dest.Length + sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameLength = nt_dest.Length - prefix_len*sizeof(WCHAR);
|
||||
+ print_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
|
||||
+ buffer->SymbolicLinkReparseBuffer.Flags = flags;
|
||||
+ break;
|
||||
default:
|
||||
/* unrecognized (regular) files should probably be treated as symlinks */
|
||||
WARN("unrecognized symbolic link\n");
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index bdac50cbd6..ef69d12f6c 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5067,7 +5067,6 @@ static void test_reparse_points(void)
|
||||
ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
- CloseHandle(handle);
|
||||
ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
|
||||
/* Check the file attributes of the symlink */
|
||||
@@ -5075,6 +5074,18 @@ static void test_reparse_points(void)
|
||||
ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: %d)\n", dwret);
|
||||
|
||||
+ /* Read back the symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
|
||||
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buffer, buffer_len, &dwret, 0);
|
||||
+ string_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
+ dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
|
||||
+ ok(bret, "Failed to read symlink!\n");
|
||||
+ ok((memcmp(dest, nameW.Buffer, string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
|
||||
+ wine_dbgstr_w(dest), wine_dbgstr_w(nameW.Buffer));
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,53 @@
|
||||
From fdee240e3485e3fb993b811f2bd47142b166fd8c Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 13:02:22 -0600
|
||||
Subject: ntdll: Add support for deleting symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 1 +
|
||||
dlls/ntdll/tests/file.c | 16 ++++++++++++++++
|
||||
2 files changed, 17 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index ee8ac6bd10..834fd8eacc 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -2023,6 +2023,7 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
|
||||
switch(buffer->ReparseTag)
|
||||
{
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
status = FILE_RemoveSymlink( handle, buffer );
|
||||
break;
|
||||
default:
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index ef69d12f6c..de06e625d6 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5084,6 +5084,22 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to read symlink!\n");
|
||||
ok((memcmp(dest, nameW.Buffer, string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
|
||||
wine_dbgstr_w(dest), wine_dbgstr_w(nameW.Buffer));
|
||||
+
|
||||
+ /* Delete the symlink */
|
||||
+ memset(&old_attrib, 0x00, sizeof(old_attrib));
|
||||
+ old_attrib.LastAccessTime.QuadPart = 0x200deadcafebeef;
|
||||
+ dwret = NtSetInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
+ ok(dwret == STATUS_SUCCESS, "Failed to set symlink folder's attributes (0x%x).\n", dwret);
|
||||
+ memset(&guid_buffer, 0x00, sizeof(guid_buffer));
|
||||
+ guid_buffer.ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||||
+ bret = DeviceIoControl(handle, FSCTL_DELETE_REPARSE_POINT, (LPVOID)&guid_buffer,
|
||||
+ REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &dwret, 0);
|
||||
+ ok(bret, "Failed to delete symlink! (0x%x)\n", GetLastError());
|
||||
+ memset(&new_attrib, 0x00, sizeof(new_attrib));
|
||||
+ dwret = NtQueryInformationFile(handle, &iosb, &new_attrib, sizeof(new_attrib), FileBasicInformation);
|
||||
+ ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ "Symlink folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
cleanup:
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,232 @@
|
||||
From 2251bc21739a0876f42085127d861e11df2cc953 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 11 Apr 2019 12:16:49 -0600
|
||||
Subject: ntdll: Add support for relative symlink creation.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 57 ++++++++++++++++++++++++++++++++++++-----
|
||||
dlls/ntdll/tests/file.c | 30 +++++++++++++++++-----
|
||||
include/ntifs.h | 2 ++
|
||||
3 files changed, 76 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 834fd8eacc..4b9688c594 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1656,16 +1656,19 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
|
||||
*/
|
||||
NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
{
|
||||
- BOOL src_allocated = FALSE, dest_allocated = FALSE, tempdir_created = FALSE;
|
||||
+ BOOL src_allocated = FALSE, path_allocated = FALSE, dest_allocated = FALSE;
|
||||
+ BOOL nt_dest_allocated = FALSE, tempdir_created = FALSE;
|
||||
+ ANSI_STRING unix_src, unix_dest, unix_path;
|
||||
char tmpdir[PATH_MAX], tmplink[PATH_MAX];
|
||||
- ANSI_STRING unix_src, unix_dest;
|
||||
char magic_dest[PATH_MAX];
|
||||
int dest_fd, needs_close;
|
||||
+ int relative_offset = 0;
|
||||
UNICODE_STRING nt_dest;
|
||||
int dest_len, offset;
|
||||
NTSTATUS status;
|
||||
struct stat st;
|
||||
WCHAR *dest;
|
||||
+ ULONG flags;
|
||||
int i;
|
||||
|
||||
switch(buffer->ReparseTag)
|
||||
@@ -1674,11 +1677,13 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
dest_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
|
||||
dest = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
+ flags = 0;
|
||||
break;
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
dest_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset;
|
||||
dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
+ flags = buffer->SymbolicLinkReparseBuffer.Flags;
|
||||
break;
|
||||
default:
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
@@ -1690,16 +1695,52 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
if ((status = server_get_unix_name( handle, &unix_src )))
|
||||
goto cleanup;
|
||||
src_allocated = TRUE;
|
||||
- nt_dest.Buffer = dest;
|
||||
- nt_dest.Length = dest_len;
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
+ {
|
||||
+ UNICODE_STRING nt_path;
|
||||
+
|
||||
+ unix_path.MaximumLength = strlen(unix_src.Buffer) + 2;
|
||||
+ unix_path.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_path.MaximumLength );
|
||||
+ path_allocated = TRUE;
|
||||
+ strcpy( unix_path.Buffer, unix_src.Buffer );
|
||||
+ dirname( unix_path.Buffer );
|
||||
+ strcat( unix_path.Buffer, "/");
|
||||
+ unix_path.Length = strlen( unix_path.Buffer );
|
||||
+ if ((status = wine_unix_to_nt_file_name( &unix_path, &nt_path )))
|
||||
+ goto cleanup;
|
||||
+ nt_dest.MaximumLength = dest_len + (strlenW( nt_path.Buffer ) + 1) * sizeof(WCHAR);
|
||||
+ nt_dest.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, nt_dest.MaximumLength );
|
||||
+ strcpyW( nt_dest.Buffer, nt_path.Buffer );
|
||||
+ RtlFreeUnicodeString( &nt_path );
|
||||
+ memcpy( &nt_dest.Buffer[strlenW(nt_dest.Buffer)], dest, dest_len + sizeof(WCHAR));
|
||||
+ nt_dest.Length = strlenW( nt_dest.Buffer ) * sizeof(WCHAR);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ RtlCreateUnicodeString( &nt_dest, dest );
|
||||
+ nt_dest.Length = dest_len;
|
||||
+ }
|
||||
+ nt_dest_allocated = TRUE;
|
||||
if ((status = wine_nt_to_unix_file_name( &nt_dest, &unix_dest, FILE_OPEN, FALSE )))
|
||||
goto cleanup;
|
||||
dest_allocated = TRUE;
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
+ {
|
||||
+ relative_offset = strlen(unix_path.Buffer);
|
||||
+ if (strncmp( unix_path.Buffer, unix_dest.Buffer, relative_offset ) != 0)
|
||||
+ {
|
||||
+ status = STATUS_IO_REPARSE_DATA_INVALID;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- TRACE("Linking %s to %s\n", unix_src.Buffer, unix_dest.Buffer);
|
||||
+ TRACE("Linking %s to %s\n", unix_src.Buffer, &unix_dest.Buffer[relative_offset]);
|
||||
|
||||
/* Encode the reparse tag into the symlink */
|
||||
- strcpy( magic_dest, "/" );
|
||||
+ strcpy( magic_dest, "" );
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
+ strcat( magic_dest, "." );
|
||||
+ strcat( magic_dest, "/" );
|
||||
for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
{
|
||||
if ((buffer->ReparseTag >> i) & 1)
|
||||
@@ -1718,7 +1759,7 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
strcat( magic_dest, "." );
|
||||
strcat( magic_dest, "/" );
|
||||
}
|
||||
- strcat( magic_dest, unix_dest.Buffer );
|
||||
+ strcat( magic_dest, &unix_dest.Buffer[relative_offset] );
|
||||
|
||||
/* Produce the link in a temporary location in the same folder */
|
||||
strcpy( tmpdir, unix_src.Buffer );
|
||||
@@ -1767,7 +1808,9 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
|
||||
cleanup:
|
||||
if (tempdir_created) rmdir( tmpdir );
|
||||
+ if (path_allocated) RtlFreeAnsiString( &unix_path );
|
||||
if (dest_allocated) RtlFreeAnsiString( &unix_dest );
|
||||
+ if (nt_dest_allocated) RtlFreeUnicodeString( &nt_dest );
|
||||
if (src_allocated) RtlFreeAnsiString( &unix_src );
|
||||
if (needs_close) close( dest_fd );
|
||||
return status;
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index de06e625d6..4a6a28e879 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -4832,7 +4832,8 @@ static void test_file_readonly_access(void)
|
||||
DeleteFileW(path);
|
||||
}
|
||||
|
||||
-static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, REPARSE_DATA_BUFFER **pbuffer)
|
||||
+static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, ULONG flags,
|
||||
+ REPARSE_DATA_BUFFER **pbuffer)
|
||||
{
|
||||
static INT header_size = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer);
|
||||
INT buffer_size, struct_size, data_size, string_len, prefix_len;
|
||||
@@ -4850,7 +4851,7 @@ static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, REPARSE_DATA_B
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
- prefix_len = strlen("\\??\\");
|
||||
+ prefix_len = (flags == SYMLINK_FLAG_RELATIVE) ? 0 : strlen("\\??\\");
|
||||
string_len = lstrlenW(&filename[prefix_len]);
|
||||
data_size = (prefix_len + 2 * string_len + 2) * sizeof(WCHAR);
|
||||
buffer_size = struct_size + data_size;
|
||||
@@ -4870,6 +4871,7 @@ static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, REPARSE_DATA_B
|
||||
buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
buffer->SymbolicLinkReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
buffer->SymbolicLinkReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.Flags = flags;
|
||||
subst_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
print_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
break;
|
||||
@@ -4947,7 +4949,7 @@ static void test_reparse_points(void)
|
||||
}
|
||||
dwret = NtQueryInformationFile(handle, &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, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, 0, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
|
||||
@@ -4988,7 +4990,7 @@ static void test_reparse_points(void)
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
handle = CreateFileW(reparse_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, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, 0, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
@@ -5003,7 +5005,7 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to create junction point target directory.\n");
|
||||
handle = CreateFileW(reparse_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, IO_REPARSE_TAG_MOUNT_POINT, &buffer);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_MOUNT_POINT, 0, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
@@ -5065,7 +5067,7 @@ static void test_reparse_points(void)
|
||||
}
|
||||
dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
- buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, &buffer);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, 0, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
|
||||
@@ -5102,6 +5104,22 @@ static void test_reparse_points(void)
|
||||
"Symlink folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* Create a relative directory symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ if (handle == INVALID_HANDLE_VALUE)
|
||||
+ {
|
||||
+ win_skip("Failed to open symlink directory handle (0x%x).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
+ ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
+ buffer_len = build_reparse_buffer(targetW, IO_REPARSE_TAG_SYMLINK, SYMLINK_FLAG_RELATIVE, &buffer);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
+ CloseHandle(handle);
|
||||
+ ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
index ab3273d3f8..0d02225bc4 100644
|
||||
--- a/include/ntifs.h
|
||||
+++ b/include/ntifs.h
|
||||
@@ -61,4 +61,6 @@ typedef struct _REPARSE_GUID_DATA_BUFFER {
|
||||
|
||||
#define REPARSE_GUID_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer)
|
||||
|
||||
+#define SYMLINK_FLAG_RELATIVE 0x00000001
|
||||
+
|
||||
#endif /* __WINE_NTIFS_H */
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,110 @@
|
||||
From 2a5bd75df6dcbbc28b821e0e70b022f32d6de424 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 11 Apr 2019 12:31:16 -0600
|
||||
Subject: ntdll: Add support for reading relative symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 42 ++++++++++++++++++++++++++++++++++++++++-
|
||||
dlls/ntdll/tests/file.c | 13 ++++++++++++-
|
||||
2 files changed, 53 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 4b9688c594..0eb522bf1d 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1828,6 +1828,7 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
BOOL dest_allocated = FALSE;
|
||||
int dest_fd, needs_close;
|
||||
UNICODE_STRING nt_dest;
|
||||
+ int path_len = 0;
|
||||
DWORD max_length;
|
||||
NTSTATUS status;
|
||||
ULONG flags = 0;
|
||||
@@ -1855,6 +1856,11 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
|
||||
/* Decode the reparse tag from the symlink */
|
||||
p = unix_dest.Buffer;
|
||||
+ if (*p == '.')
|
||||
+ {
|
||||
+ flags = SYMLINK_FLAG_RELATIVE;
|
||||
+ p++;
|
||||
+ }
|
||||
if (*p++ != '/')
|
||||
{
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
@@ -1891,10 +1897,44 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
unix_dest.Length -= (p - unix_dest.Buffer);
|
||||
memmove(unix_dest.Buffer, p, unix_dest.Length);
|
||||
|
||||
+ /* convert the relative path into an absolute path */
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
+ {
|
||||
+ int offset = unix_src.Length + 2;
|
||||
+ memcpy( &unix_dest.Buffer[offset], unix_dest.Buffer, unix_dest.Length );
|
||||
+ unix_dest.Buffer[offset+unix_dest.Length] = 0;
|
||||
+ memcpy( unix_dest.Buffer, unix_src.Buffer, unix_src.Length );
|
||||
+ unix_dest.Buffer[unix_src.Length] = 0;
|
||||
+ dirname( unix_dest.Buffer );
|
||||
+ strcat( unix_dest.Buffer, "/" );
|
||||
+ path_len = strlen( unix_dest.Buffer );
|
||||
+ memmove( &unix_dest.Buffer[path_len], &unix_dest.Buffer[offset], unix_dest.Length + 1 );
|
||||
+ unix_dest.Length = strlen( unix_dest.Buffer );
|
||||
+ }
|
||||
if ((status = wine_unix_to_nt_file_name( &unix_dest, &nt_dest )))
|
||||
goto cleanup;
|
||||
+ /* remove the relative path from the NT path */
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
+ {
|
||||
+ UNICODE_STRING nt_path;
|
||||
+ int relative_offset;
|
||||
+
|
||||
+ unix_dest.Length = path_len;
|
||||
+ if ((status = wine_unix_to_nt_file_name( &unix_dest, &nt_path )))
|
||||
+ goto cleanup;
|
||||
+ relative_offset = strlenW( nt_path.Buffer );
|
||||
+ if (strncmpW( nt_path.Buffer, nt_dest.Buffer, relative_offset ) != 0)
|
||||
+ {
|
||||
+ RtlFreeUnicodeString( &nt_path );
|
||||
+ status = STATUS_IO_REPARSE_DATA_INVALID;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ RtlFreeUnicodeString( &nt_path );
|
||||
+ nt_dest.Length = strlenW( &nt_dest.Buffer[relative_offset] ) * sizeof(WCHAR);
|
||||
+ memmove( nt_dest.Buffer, &nt_dest.Buffer[relative_offset], nt_dest.Length + sizeof(WCHAR) );
|
||||
+ }
|
||||
|
||||
- prefix_len = strlen("\\??\\");
|
||||
+ prefix_len = (flags == SYMLINK_FLAG_RELATIVE) ? 0 : strlen("\\??\\");
|
||||
switch(buffer->ReparseTag)
|
||||
{
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 4a6a28e879..c13ee84bf0 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5117,9 +5117,20 @@ static void test_reparse_points(void)
|
||||
ok(dwret == STATUS_SUCCESS, "Failed to get symlink folder's attributes (0x%x).\n", dwret);
|
||||
buffer_len = build_reparse_buffer(targetW, IO_REPARSE_TAG_SYMLINK, SYMLINK_FLAG_RELATIVE, &buffer);
|
||||
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
- CloseHandle(handle);
|
||||
ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
|
||||
+ /* Read back the relative symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
|
||||
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buffer, buffer_len, &dwret, 0);
|
||||
+ ok(bret, "Failed to read relative symlink!\n");
|
||||
+ string_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
+ dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
|
||||
+ ok((memcmp(dest, targetW, string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
|
||||
+ wine_dbgstr_w(dest), wine_dbgstr_w(targetW));
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,94 @@
|
||||
From 43b9fbb15b535717e2b8cb082330efd179acf321 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 11 Apr 2019 17:57:53 -0600
|
||||
Subject: ntdll: Add support for file symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 11 ++++++++---
|
||||
dlls/ntdll/tests/file.c | 33 +++++++++++++++++++++++++++++++++
|
||||
2 files changed, 41 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 0eb522bf1d..7c1a711fc3 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -1665,6 +1665,7 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
int relative_offset = 0;
|
||||
UNICODE_STRING nt_dest;
|
||||
int dest_len, offset;
|
||||
+ BOOL is_dir = TRUE;
|
||||
NTSTATUS status;
|
||||
struct stat st;
|
||||
WCHAR *dest;
|
||||
@@ -1755,7 +1756,8 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
status = FILE_GetNtStatus();
|
||||
goto cleanup;
|
||||
}
|
||||
- if (S_ISDIR(st.st_mode))
|
||||
+ is_dir = S_ISDIR(st.st_mode);
|
||||
+ if (is_dir)
|
||||
strcat( magic_dest, "." );
|
||||
strcat( magic_dest, "/" );
|
||||
}
|
||||
@@ -1781,8 +1783,11 @@ NTSTATUS FILE_CreateSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
/* Atomically move the link into position */
|
||||
if (!renameat2( -1, tmplink, -1, unix_src.Buffer, RENAME_EXCHANGE ))
|
||||
{
|
||||
- /* success: link and folder have switched locations */
|
||||
- rmdir( tmplink ); /* remove the folder (at link location) */
|
||||
+ /* success: link and folder/file have switched locations */
|
||||
+ if (is_dir)
|
||||
+ rmdir( tmplink ); /* remove the folder (at link location) */
|
||||
+ else
|
||||
+ unlink( tmplink ); /* remove the file (at link location) */
|
||||
}
|
||||
else if (errno == ENOSYS)
|
||||
{
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index c13ee84bf0..f40dc1f51b 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5050,6 +5050,39 @@ static void test_reparse_points(void)
|
||||
/* Delete the junction point directory and create a blank slate for symlink tests */
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
ok(bret, "Failed to delete junction point!\n");
|
||||
+
|
||||
+ /* Create the file symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ if (handle == INVALID_HANDLE_VALUE)
|
||||
+ {
|
||||
+ win_skip("Failed to open symlink directory handle (0x%x).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
+ ok(dwret == STATUS_SUCCESS, "Failed to get symlink file's attributes (0x%x).\n", dwret);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, 0, &buffer);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
+ ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+ /* Check deleting a file symlink as if it were a directory */
|
||||
+ bret = RemoveDirectoryW(reparse_path);
|
||||
+ todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
+ err = GetLastError();
|
||||
+ todo_wine ok(err == ERROR_DIRECTORY,
|
||||
+ "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
+ ERROR_DIRECTORY, err);
|
||||
+ dwret = GetFileAttributesW(reparse_path);
|
||||
+ todo_wine ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
|
||||
+
|
||||
+ /* Delete the symlink as a file */
|
||||
+ bret = DeleteFileW(reparse_path);
|
||||
+ todo_wine ok(bret, "Failed to delete symlink as a file!\n");
|
||||
+
|
||||
+ /* Create a blank slate for directory symlink tests */
|
||||
bret = CreateDirectoryW(reparse_path, NULL);
|
||||
ok(bret, "Failed to create junction point directory.\n");
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,223 @@
|
||||
From 48fd8c19b8a0403c297c5bd0eade9cd5ef446305 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 30 Mar 2019 12:00:51 -0600
|
||||
Subject: [PATCH] ntdll: Correctly report file symbolic links as files.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/file.c | 110 +++++++++++++++++++++++++++++++++---------------
|
||||
dlls/ntdll/tests/file.c | 8 ++--
|
||||
2 files changed, 79 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
|
||||
index 4e5bf46..6cb351b 100644
|
||||
--- a/dlls/ntdll/file.c
|
||||
+++ b/dlls/ntdll/file.c
|
||||
@@ -140,6 +140,9 @@ static inline int get_file_xattr( char *hexattr, int attrlen )
|
||||
return 0;
|
||||
}
|
||||
|
||||
+NTSTATUS FILE_DecodeSymlink(const char *unix_src, char *unix_dest, USHORT *unix_dest_len,
|
||||
+ DWORD *tag, ULONG *flags, BOOL *is_dir);
|
||||
+
|
||||
/* fetch the attributes of a file */
|
||||
static inline ULONG get_file_attributes( const struct stat *st )
|
||||
{
|
||||
@@ -214,10 +217,14 @@ int get_file_info( const char *path, struct stat *st, ULONG *attr )
|
||||
if (ret == -1) return ret;
|
||||
if (S_ISLNK( st->st_mode ))
|
||||
{
|
||||
+ BOOL is_dir;
|
||||
+
|
||||
ret = stat( path, st );
|
||||
if (ret == -1) return ret;
|
||||
/* is a symbolic link and a directory, consider these "reparse points" */
|
||||
if (S_ISDIR( st->st_mode )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
+ if (FILE_DecodeSymlink( path, NULL, NULL, NULL, NULL, &is_dir) == STATUS_SUCCESS)
|
||||
+ st->st_mode = (st->st_mode & ~S_IFMT) | (is_dir ? S_IFDIR : S_IFREG);
|
||||
}
|
||||
*attr |= get_file_attributes( st );
|
||||
/* retrieve any stored DOS attributes */
|
||||
@@ -1920,48 +1927,34 @@ 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 out_size)
|
||||
+NTSTATUS FILE_DecodeSymlink(const char *unix_src, char *unix_dest, USHORT *unix_dest_len,
|
||||
+ DWORD *tag, ULONG *flags, BOOL *is_dir)
|
||||
{
|
||||
- ANSI_STRING unix_src, unix_dest;
|
||||
- VOID *subst_name, *print_name;
|
||||
- BOOL dest_allocated = FALSE;
|
||||
- int dest_fd, needs_close;
|
||||
- UNICODE_STRING nt_dest;
|
||||
- int path_len = 0;
|
||||
- DWORD max_length;
|
||||
+ USHORT len = MAX_PATH;
|
||||
+ DWORD reparse_tag;
|
||||
NTSTATUS status;
|
||||
- ULONG flags = 0;
|
||||
- INT prefix_len;
|
||||
+ BOOL dir_flag;
|
||||
+ char *p, *tmp;
|
||||
ssize_t ret;
|
||||
- char *p;
|
||||
int i;
|
||||
|
||||
- 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 (unix_dest_len) len = *unix_dest_len;
|
||||
+ if (!unix_dest)
|
||||
+ tmp = RtlAllocateHeap( GetProcessHeap(), 0, len );
|
||||
+ else
|
||||
+ tmp = unix_dest;
|
||||
+ ret = readlink( unix_src, tmp, len );
|
||||
if (ret < 0)
|
||||
{
|
||||
status = FILE_GetNtStatus();
|
||||
goto cleanup;
|
||||
}
|
||||
- unix_dest.Length = ret;
|
||||
-
|
||||
+ len = ret;
|
||||
/* Decode the reparse tag from the symlink */
|
||||
- p = unix_dest.Buffer;
|
||||
+ p = tmp;
|
||||
if (*p == '.')
|
||||
{
|
||||
- flags = SYMLINK_FLAG_RELATIVE;
|
||||
+ if (flags) *flags = SYMLINK_FLAG_RELATIVE;
|
||||
p++;
|
||||
}
|
||||
if (*p++ != '/')
|
||||
@@ -1969,7 +1962,7 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
- buffer->ReparseTag = 0;
|
||||
+ reparse_tag = 0;
|
||||
for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
{
|
||||
char c = *p++;
|
||||
@@ -1984,21 +1977,68 @@ NTSTATUS FILE_GetSymlink(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG out_s
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
- buffer->ReparseTag |= (val << i);
|
||||
+ reparse_tag |= (val << i);
|
||||
}
|
||||
/* skip past the directory/file flag */
|
||||
- if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ if (reparse_tag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
char c = *p++;
|
||||
|
||||
- if ((c != '/' && c != '.') || (c == '.' && *p++ != '/'))
|
||||
+ if (c == '/')
|
||||
+ dir_flag = FALSE;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ dir_flag = TRUE;
|
||||
+ else
|
||||
{
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
- unix_dest.Length -= (p - unix_dest.Buffer);
|
||||
- memmove(unix_dest.Buffer, p, unix_dest.Length);
|
||||
+ else
|
||||
+ dir_flag = TRUE;
|
||||
+ len -= (p - tmp);
|
||||
+ if (tag) *tag = reparse_tag;
|
||||
+ if (is_dir) *is_dir = dir_flag;
|
||||
+ if (unix_dest) memmove(unix_dest, p, len);
|
||||
+ if (unix_dest_len) *unix_dest_len = len;
|
||||
+ status = STATUS_SUCCESS;
|
||||
+
|
||||
+cleanup:
|
||||
+ if (!unix_dest) RtlFreeHeap( GetProcessHeap(), 0, tmp );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * 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 out_size)
|
||||
+{
|
||||
+ ANSI_STRING unix_src, unix_dest;
|
||||
+ VOID *subst_name, *print_name;
|
||||
+ BOOL dest_allocated = FALSE;
|
||||
+ int dest_fd, needs_close;
|
||||
+ UNICODE_STRING nt_dest;
|
||||
+ int path_len = 0;
|
||||
+ DWORD max_length;
|
||||
+ NTSTATUS status;
|
||||
+ ULONG flags = 0;
|
||||
+ INT prefix_len;
|
||||
+
|
||||
+ 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.MaximumLength = PATH_MAX;
|
||||
+ unix_dest.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_dest.MaximumLength );
|
||||
+ unix_dest.Length = unix_dest.MaximumLength;
|
||||
+ dest_allocated = TRUE;
|
||||
+ if ((status = FILE_DecodeSymlink( unix_src.Buffer, unix_dest.Buffer, &unix_dest.Length,
|
||||
+ &buffer->ReparseTag, &flags, NULL )))
|
||||
+ goto cleanup;
|
||||
|
||||
/* convert the relative path into an absolute path */
|
||||
if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 3f47c69..cecce37 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5288,13 +5288,13 @@ static void test_reparse_points(void)
|
||||
|
||||
/* Check deleting a file symlink as if it were a directory */
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
- todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
+ ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
err = GetLastError();
|
||||
todo_wine ok(err == ERROR_DIRECTORY,
|
||||
"Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
ERROR_DIRECTORY, err);
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
- todo_wine ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
|
||||
|
||||
/* Delete the symlink as a file */
|
||||
@@ -5303,10 +5303,10 @@ static void test_reparse_points(void)
|
||||
|
||||
/* Create a blank slate for directory symlink tests */
|
||||
bret = CreateDirectoryW(reparse_path, NULL);
|
||||
- ok(bret, "Failed to create junction point directory.\n");
|
||||
+ todo_wine ok(bret, "Failed to create junction point directory.\n");
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
ok(dwret != (DWORD)~0, "Path doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
- ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
|
||||
+ todo_wine ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
|
||||
|
||||
/* Create the directory symlink */
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
--
|
||||
1.9.1
|
||||
|
@ -0,0 +1,48 @@
|
||||
From e57c2a438bf63fcab5686c8c7dbd4379dda1a49b Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 30 Mar 2019 12:01:50 -0600
|
||||
Subject: kernel32: Set error code when attempting to delete file symlinks as
|
||||
directories.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernel32/path.c | 5 ++++-
|
||||
dlls/ntdll/tests/file.c | 6 +++---
|
||||
2 files changed, 7 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
|
||||
index 6369225b3e..832e77bfc0 100644
|
||||
--- a/dlls/kernel32/path.c
|
||||
+++ b/dlls/kernel32/path.c
|
||||
@@ -1738,7 +1738,10 @@ BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
|
||||
ret = (unlink( unix_name.Buffer ) != -1);
|
||||
else
|
||||
ret = (rmdir( unix_name.Buffer ) != -1);
|
||||
- if (!ret) FILE_SetDosError();
|
||||
+ if (status == STATUS_SUCCESS && (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
||||
+ !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
+ SetLastError( ERROR_DIRECTORY );
|
||||
+ else if (!ret) FILE_SetDosError();
|
||||
RtlFreeAnsiString( &unix_name );
|
||||
}
|
||||
else
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index f23bf8644c..a1323ab4f6 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5071,9 +5071,9 @@ static void test_reparse_points(void)
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
err = GetLastError();
|
||||
- todo_wine ok(err == ERROR_DIRECTORY,
|
||||
- "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
- ERROR_DIRECTORY, err);
|
||||
+ ok(err == ERROR_DIRECTORY,
|
||||
+ "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
+ ERROR_DIRECTORY, err);
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,143 @@
|
||||
From 108acd429c44d9a06bab68ad66890ee6756264cc Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 30 Mar 2019 13:41:07 -0600
|
||||
Subject: [PATCH] server: Properly handle file symlink deletion.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 6 ++---
|
||||
server/fd.c | 64 +++++++++++++++++++++++++++++++++++++++++++++----
|
||||
2 files changed, 62 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 8800ab0..92ac697 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5299,14 +5299,14 @@ static void test_reparse_points(void)
|
||||
|
||||
/* Delete the symlink as a file */
|
||||
bret = DeleteFileW(reparse_path);
|
||||
- todo_wine ok(bret, "Failed to delete symlink as a file!\n");
|
||||
+ ok(bret, "Failed to delete symlink as a file!\n");
|
||||
|
||||
/* Create a blank slate for directory symlink tests */
|
||||
bret = CreateDirectoryW(reparse_path, NULL);
|
||||
- todo_wine ok(bret, "Failed to create junction point directory.\n");
|
||||
+ ok(bret, "Failed to create junction point directory.\n");
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
ok(dwret != (DWORD)~0, "Path doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
- todo_wine ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
|
||||
+ ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
|
||||
|
||||
/* Create the directory symlink */
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index 6c4cf61..58843d7 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -1075,7 +1075,7 @@ static void inode_destroy( struct object *obj )
|
||||
{
|
||||
/* make sure it is still the same file */
|
||||
struct stat st;
|
||||
- if (!stat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino)
|
||||
+ if (!lstat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino)
|
||||
{
|
||||
if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name );
|
||||
else unlink( fd->unix_name );
|
||||
@@ -1769,6 +1769,53 @@ char *dup_fd_name( struct fd *root, const char *name )
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static void decode_symlink(char *name, int *is_dir)
|
||||
+{
|
||||
+ char link[MAX_PATH], *p;
|
||||
+ ULONG reparse_tag;
|
||||
+ int len, i;
|
||||
+
|
||||
+ len = readlink( name, link, sizeof(link) );
|
||||
+ link[len] = 0;
|
||||
+ p = link;
|
||||
+ /* skip past relative/absolute indication */
|
||||
+ if (*p == '.')
|
||||
+ p++;
|
||||
+ if (*p++ != '/')
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+ /* decode the reparse tag */
|
||||
+ reparse_tag = 0;
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+ int val;
|
||||
+
|
||||
+ if (c == '/')
|
||||
+ val = 0;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ val = 1;
|
||||
+ else
|
||||
+ return;
|
||||
+ reparse_tag |= (val << i);
|
||||
+ }
|
||||
+ /* decode the directory/file flag */
|
||||
+ if (reparse_tag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+
|
||||
+ if (c == '/')
|
||||
+ *is_dir = FALSE;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ *is_dir = TRUE;
|
||||
+ else
|
||||
+ return;
|
||||
+ }
|
||||
+ else
|
||||
+ *is_dir = TRUE;
|
||||
+}
|
||||
+
|
||||
/* open() wrapper that returns a struct fd with no fd user set */
|
||||
struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, unsigned int access,
|
||||
unsigned int sharing, unsigned int options )
|
||||
@@ -1876,14 +1923,15 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode,
|
||||
closed_fd->unix_name = fd->unix_name;
|
||||
|
||||
if (do_chmod) chmod( name, *mode );
|
||||
- fstat( fd->unix_fd, &st );
|
||||
+ fstatat( -1, fd->unix_name, &st, AT_SYMLINK_NOFOLLOW );
|
||||
*mode = st.st_mode;
|
||||
|
||||
/* only bother with an inode for normal files and directories */
|
||||
- if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
|
||||
+ if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))
|
||||
{
|
||||
unsigned int err;
|
||||
struct inode *inode = get_inode( st.st_dev, st.st_ino, fd->unix_fd );
|
||||
+ int is_link = S_ISLNK(st.st_mode), is_dir;
|
||||
|
||||
if (!inode)
|
||||
{
|
||||
@@ -1898,13 +1946,19 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode,
|
||||
list_add_head( &inode->open, &fd->inode_entry );
|
||||
closed_fd = NULL;
|
||||
|
||||
+ /* decode symlink type */
|
||||
+ fstat( fd->unix_fd, &st );
|
||||
+ is_dir = S_ISDIR(st.st_mode);
|
||||
+ if (is_link)
|
||||
+ decode_symlink(fd->unix_name, &is_dir);
|
||||
+
|
||||
/* check directory options */
|
||||
- if ((options & FILE_DIRECTORY_FILE) && !S_ISDIR(st.st_mode))
|
||||
+ if ((options & FILE_DIRECTORY_FILE) && !is_dir)
|
||||
{
|
||||
set_error( STATUS_NOT_A_DIRECTORY );
|
||||
goto error;
|
||||
}
|
||||
- if ((options & FILE_NON_DIRECTORY_FILE) && S_ISDIR(st.st_mode))
|
||||
+ if ((options & FILE_NON_DIRECTORY_FILE) && is_dir)
|
||||
{
|
||||
set_error( STATUS_FILE_IS_A_DIRECTORY );
|
||||
goto error;
|
||||
--
|
||||
1.9.1
|
||||
|
@ -0,0 +1,288 @@
|
||||
From 0a6af701fbda3b10b709a33f811788d24552cb5f Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 16:02:05 -0600
|
||||
Subject: kernel32: Implement CreateSymbolicLink[A|W] with ntdll reparse
|
||||
points.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernel32/path.c | 124 +++++++++++++++++++++++++++++++++++--
|
||||
dlls/kernel32/tests/path.c | 94 ++++++++++++++++++++++++++++
|
||||
2 files changed, 214 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
|
||||
index 832e77bfc0..9fef494cfa 100644
|
||||
--- a/dlls/kernel32/path.c
|
||||
+++ b/dlls/kernel32/path.c
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winternl.h"
|
||||
+#include "winioctl.h"
|
||||
+#include "ntifs.h"
|
||||
|
||||
#include "kernel_private.h"
|
||||
#include "wine/unicode.h"
|
||||
@@ -2088,8 +2090,106 @@ WCHAR * CDECL wine_get_dos_file_name( LPCSTR str )
|
||||
*/
|
||||
BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags)
|
||||
{
|
||||
- FIXME("(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags);
|
||||
- return TRUE;
|
||||
+ static INT struct_size = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer[0]);
|
||||
+ static INT header_size = offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer);
|
||||
+ INT buffer_size, data_size, string_len, prefix_len;
|
||||
+ WCHAR *subst_dest, *print_dest, *string;
|
||||
+ REPARSE_DATA_BUFFER *buffer;
|
||||
+ LPWSTR target_path = NULL;
|
||||
+ BOOL is_relative, is_dir;
|
||||
+ int target_path_len = 0;
|
||||
+ UNICODE_STRING nt_name;
|
||||
+ BOOLEAN bret = FALSE;
|
||||
+ NTSTATUS status;
|
||||
+ HANDLE hlink;
|
||||
+ DWORD dwret;
|
||||
+
|
||||
+ TRACE("(%s %s %d)\n", debugstr_w(link), debugstr_w(target), flags);
|
||||
+
|
||||
+ is_relative = (RtlDetermineDosPathNameType_U( target ) == RELATIVE_PATH);
|
||||
+ is_dir = (flags & SYMBOLIC_LINK_FLAG_DIRECTORY);
|
||||
+ if (is_dir && !CreateDirectoryW( link, NULL ))
|
||||
+ return FALSE;
|
||||
+ hlink = CreateFileW( link, GENERIC_READ | GENERIC_WRITE, 0, 0,
|
||||
+ is_dir ? OPEN_EXISTING : CREATE_NEW,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0 );
|
||||
+ if (hlink == INVALID_HANDLE_VALUE)
|
||||
+ goto cleanup;
|
||||
+ if (is_relative)
|
||||
+ {
|
||||
+ UNICODE_STRING nt_path;
|
||||
+ int len;
|
||||
+
|
||||
+ status = RtlDosPathNameToNtPathName_U_WithStatus( link, &nt_path, NULL, NULL );
|
||||
+ if (status != STATUS_SUCCESS)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ /* obtain the path of the link */
|
||||
+ for (; nt_path.Length > 0; nt_path.Length -= sizeof(WCHAR))
|
||||
+ {
|
||||
+ WCHAR c = nt_path.Buffer[nt_path.Length/sizeof(WCHAR)];
|
||||
+ if (c == '/' || c == '\\')
|
||||
+ {
|
||||
+ nt_path.Length += sizeof(WCHAR);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ /* append the target to the link path */
|
||||
+ target_path_len = nt_path.Length / sizeof(WCHAR);
|
||||
+ len = target_path_len + (strlenW( target ) + 1);
|
||||
+ target_path = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(WCHAR) );
|
||||
+ lstrcpynW( target_path, nt_path.Buffer, nt_path.Length );
|
||||
+ target_path[nt_path.Length/sizeof(WCHAR)] = 0;
|
||||
+ lstrcatW( target_path, target );
|
||||
+ RtlFreeUnicodeString( &nt_path );
|
||||
+ }
|
||||
+ else
|
||||
+ target_path = (LPWSTR)target;
|
||||
+ status = RtlDosPathNameToNtPathName_U_WithStatus( target_path, &nt_name, NULL, NULL );
|
||||
+ if (status != STATUS_SUCCESS)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (is_relative && strncmpW( target_path, nt_name.Buffer, target_path_len ) != 0)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ prefix_len = is_relative ? 0 : strlen("\\??\\");
|
||||
+ string = &nt_name.Buffer[target_path_len];
|
||||
+ string_len = lstrlenW( &string[prefix_len] );
|
||||
+ data_size = (prefix_len + 2 * string_len + 2) * sizeof(WCHAR);
|
||||
+ buffer_size = struct_size + data_size;
|
||||
+ buffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size );
|
||||
+ buffer->ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||||
+ buffer->ReparseDataLength = struct_size - header_size + data_size;
|
||||
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = (prefix_len + string_len) * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameOffset = (prefix_len + string_len + 1) * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.PrintNameLength = string_len * sizeof(WCHAR);
|
||||
+ buffer->SymbolicLinkReparseBuffer.Flags = is_relative ? SYMLINK_FLAG_RELATIVE : 0;
|
||||
+ subst_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ print_dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[prefix_len + string_len + 1];
|
||||
+ lstrcpyW( subst_dest, string );
|
||||
+ lstrcpyW( print_dest, &string[prefix_len] );
|
||||
+ RtlFreeUnicodeString( &nt_name );
|
||||
+ bret = DeviceIoControl( hlink, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_size, NULL, 0,
|
||||
+ &dwret, 0 );
|
||||
+ CloseHandle( hlink );
|
||||
+ HeapFree( GetProcessHeap(), 0, buffer );
|
||||
+
|
||||
+cleanup:
|
||||
+ if (!bret)
|
||||
+ {
|
||||
+ if (is_dir)
|
||||
+ RemoveDirectoryW( link );
|
||||
+ else
|
||||
+ DeleteFileW( link );
|
||||
+ }
|
||||
+ if (is_relative) HeapFree( GetProcessHeap(), 0, target_path );
|
||||
+ return bret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
@@ -2097,8 +2197,24 @@ BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags)
|
||||
*/
|
||||
BOOLEAN WINAPI CreateSymbolicLinkA(LPCSTR link, LPCSTR target, DWORD flags)
|
||||
{
|
||||
- FIXME("(%s %s %d): stub\n", debugstr_a(link), debugstr_a(target), flags);
|
||||
- return TRUE;
|
||||
+ WCHAR *targetW, *linkW;
|
||||
+ BOOL ret;
|
||||
+
|
||||
+ TRACE("(%s %s %d)\n", debugstr_a(link), debugstr_a(target), flags);
|
||||
+
|
||||
+ if (!(linkW = FILE_name_AtoW( link, TRUE )))
|
||||
+ {
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+ if (!(targetW = FILE_name_AtoW( target, TRUE )))
|
||||
+ {
|
||||
+ HeapFree( GetProcessHeap(), 0, linkW );
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+ ret = CreateSymbolicLinkW( linkW, targetW, flags );
|
||||
+ HeapFree( GetProcessHeap(), 0, linkW );
|
||||
+ HeapFree( GetProcessHeap(), 0, targetW );
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
diff --git a/dlls/kernel32/tests/path.c b/dlls/kernel32/tests/path.c
|
||||
index 0a03225120..076333b3b1 100644
|
||||
--- a/dlls/kernel32/tests/path.c
|
||||
+++ b/dlls/kernel32/tests/path.c
|
||||
@@ -81,6 +81,9 @@ static void (WINAPI *pReleaseActCtx)(HANDLE);
|
||||
static BOOL (WINAPI *pCheckNameLegalDOS8Dot3W)(const WCHAR *, char *, DWORD, BOOL *, BOOL *);
|
||||
static BOOL (WINAPI *pCheckNameLegalDOS8Dot3A)(const char *, char *, DWORD, BOOL *, BOOL *);
|
||||
|
||||
+/* Present in Vista+ */
|
||||
+static BOOL (WINAPI *pCreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD);
|
||||
+
|
||||
/* a structure to deal with wine todos somewhat cleanly */
|
||||
typedef struct {
|
||||
DWORD shortlen;
|
||||
@@ -2179,6 +2182,7 @@ static void init_pointers(void)
|
||||
MAKEFUNC(ReleaseActCtx);
|
||||
MAKEFUNC(CheckNameLegalDOS8Dot3W);
|
||||
MAKEFUNC(CheckNameLegalDOS8Dot3A);
|
||||
+ MAKEFUNC(CreateSymbolicLinkW);
|
||||
#undef MAKEFUNC
|
||||
}
|
||||
|
||||
@@ -2435,6 +2439,95 @@ static void test_SetSearchPathMode(void)
|
||||
RemoveDirectoryA( dir );
|
||||
}
|
||||
|
||||
+static void test_CreateSymbolicLink(void)
|
||||
+{
|
||||
+ static const WCHAR target_fileW[] = {'t','a','r','g','e','t','_','f','i','l','e',0};
|
||||
+ static const WCHAR target_dirW[] = {'t','a','r','g','e','t','_','d','i','r',0};
|
||||
+ static const WCHAR linkW[] = {'l','i','n','k',0};
|
||||
+ static const WCHAR fooW[] = {'f','o','o',0};
|
||||
+ static WCHAR volW[] = {'c',':','\\',0};
|
||||
+ static const WCHAR dotW[] = {'.',0};
|
||||
+ WCHAR path[MAX_PATH], old_path[MAX_PATH], tmp[MAX_PATH];
|
||||
+ DWORD dwLen, dwFlags;
|
||||
+ TOKEN_PRIVILEGES tp;
|
||||
+ HANDLE token;
|
||||
+ LUID luid;
|
||||
+ BOOL bret;
|
||||
+ HANDLE h;
|
||||
+
|
||||
+ if (!pCreateSymbolicLinkW)
|
||||
+ {
|
||||
+ win_skip( "CreateSymbolicLink isn't available\n" );
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a temporary folder for the symlink tests */
|
||||
+ GetTempFileNameW( dotW, fooW, 0, path );
|
||||
+ DeleteFileW( path );
|
||||
+ if (!CreateDirectoryW( path, NULL ))
|
||||
+ {
|
||||
+ win_skip("Unable to create a temporary junction point directory.\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ GetCurrentDirectoryW( sizeof(old_path)/sizeof(WCHAR), old_path );
|
||||
+ SetCurrentDirectoryW( path );
|
||||
+
|
||||
+ /* Check that the volume this folder is located on supports reparse points */
|
||||
+ GetFullPathNameW( path, sizeof(tmp)/sizeof(WCHAR), tmp, NULL );
|
||||
+ volW[0] = tmp[0];
|
||||
+ GetVolumeInformationW( volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0 );
|
||||
+ if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
|
||||
+ {
|
||||
+ skip("File system does not support junction points.\n");
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* Establish permissions for symlink creation */
|
||||
+ bret = OpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &token );
|
||||
+ ok(bret, "OpenProcessToken failed: %u\n", GetLastError());
|
||||
+ bret = LookupPrivilegeValueA( NULL, "SeCreateSymbolicLinkPrivilege", &luid );
|
||||
+ todo_wine ok(bret || broken(!bret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE) /* winxp */,
|
||||
+ "LookupPrivilegeValue failed: %u\n", GetLastError());
|
||||
+ if (bret)
|
||||
+ {
|
||||
+ tp.PrivilegeCount = 1;
|
||||
+ tp.Privileges[0].Luid = luid;
|
||||
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
+ bret = AdjustTokenPrivileges( token, FALSE, &tp, 0, NULL, NULL );
|
||||
+ ok(bret, "AdjustTokenPrivileges failed: %u\n", GetLastError());
|
||||
+ }
|
||||
+ if ((!bret && GetLastError() != ERROR_NO_SUCH_PRIVILEGE) || GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
||||
+ {
|
||||
+ win_skip("Insufficient permissions to perform symlink tests.\n");
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* Create a destination folder and file for symlinks to target */
|
||||
+ bret = CreateDirectoryW( target_dirW, NULL );
|
||||
+ ok(bret, "Failed to create symlink target directory.\n");
|
||||
+ h = CreateFileW( target_fileW, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
+ ok(h != INVALID_HANDLE_VALUE, "Failed to create symlink target file.\n");
|
||||
+ CloseHandle( h );
|
||||
+
|
||||
+ /* Create a directory symbolic link */
|
||||
+ bret = CreateSymbolicLinkW( linkW, target_dirW, SYMBOLIC_LINK_FLAG_DIRECTORY );
|
||||
+ ok(bret, "Failed to create directory symbolic link! (0x%x)\n", GetLastError());
|
||||
+ bret = RemoveDirectoryW( linkW );
|
||||
+ ok(bret, "Failed to remove directory symbolic link! (0x%x)\n", GetLastError());
|
||||
+
|
||||
+ /* Create a file symbolic link */
|
||||
+ bret = CreateSymbolicLinkW( linkW, target_fileW, 0x0 );
|
||||
+ ok(bret, "Failed to create file symbolic link! (0x%x)\n", GetLastError());
|
||||
+ bret = DeleteFileW( linkW );
|
||||
+ ok(bret, "Failed to remove file symbolic link! (0x%x)\n", GetLastError());
|
||||
+
|
||||
+cleanup:
|
||||
+ DeleteFileW( target_fileW );
|
||||
+ RemoveDirectoryW( target_dirW );
|
||||
+ SetCurrentDirectoryW( old_path );
|
||||
+ RemoveDirectoryW( path );
|
||||
+}
|
||||
+
|
||||
START_TEST(path)
|
||||
{
|
||||
CHAR origdir[MAX_PATH],curdir[MAX_PATH], curDrive, otherDrive;
|
||||
@@ -2469,4 +2562,5 @@ START_TEST(path)
|
||||
test_GetFullPathNameW();
|
||||
test_CheckNameLegalDOS8Dot3();
|
||||
test_SetSearchPathMode();
|
||||
+ test_CreateSymbolicLink();
|
||||
}
|
||||
--
|
||||
2.17.1
|
||||
|
@ -160,7 +160,6 @@ patch_enable_all ()
|
||||
enable_iphlpapi_System_Ping="$1"
|
||||
enable_iphlpapi_TCP_Table="$1"
|
||||
enable_kernel32_CopyFileEx="$1"
|
||||
enable_kernel32_CreateSymbolicLink="$1"
|
||||
enable_kernel32_Debugger="$1"
|
||||
enable_kernel32_FindFirstFile="$1"
|
||||
enable_kernel32_Job_Tests="$1"
|
||||
@ -620,9 +619,6 @@ patch_enable ()
|
||||
kernel32-CopyFileEx)
|
||||
enable_kernel32_CopyFileEx="$2"
|
||||
;;
|
||||
kernel32-CreateSymbolicLink)
|
||||
enable_kernel32_CreateSymbolicLink="$2"
|
||||
;;
|
||||
kernel32-Debugger)
|
||||
enable_kernel32_Debugger="$2"
|
||||
;;
|
||||
@ -4222,21 +4218,6 @@ if test "$enable_kernel32_CopyFileEx" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset kernel32-CreateSymbolicLink
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#44948] kernel32: Implement CreateSymbolicLink
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * dlls/kernel32/path.c, dlls/msvcp120/tests/msvcp120.c
|
||||
# |
|
||||
if test "$enable_kernel32_CreateSymbolicLink" -eq 1; then
|
||||
patch_apply kernel32-CreateSymbolicLink/0001-kernel32-Implement-CreateSymbolicLink.patch
|
||||
(
|
||||
printf '%s\n' '+ { "Gijs Vermeulen", "kernel32: Implement CreateSymbolicLink.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset kernel32-Debugger
|
||||
# |
|
||||
# | Modified files:
|
||||
@ -4776,7 +4757,8 @@ fi
|
||||
# | * [#12401] Support for Junction Points
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * dlls/kernel32/path.c, dlls/kernel32/volume.c, dlls/ntdll/file.c, dlls/ntdll/tests/file.c, include/ddk/ntifs.h
|
||||
# | * configure.ac, dlls/kernel32/path.c, dlls/kernel32/tests/path.c, dlls/kernel32/volume.c, dlls/ntdll/file.c,
|
||||
# | dlls/ntdll/tests/file.c, include/Makefile.in, include/wine/port.h, libs/port/Makefile.in, server/fd.c
|
||||
# |
|
||||
if test "$enable_ntdll_Junction_Points" -eq 1; then
|
||||
patch_apply ntdll-Junction_Points/0001-ntdll-Add-support-for-junction-point-creation.patch
|
||||
@ -4785,7 +4767,16 @@ if test "$enable_ntdll_Junction_Points" -eq 1; then
|
||||
patch_apply ntdll-Junction_Points/0004-ntdll-Add-a-test-for-junction-point-advertisement.patch
|
||||
patch_apply ntdll-Junction_Points/0005-kernel32-ntdll-Add-support-for-deleting-junction-poi.patch
|
||||
patch_apply ntdll-Junction_Points/0006-kernel32-Advertise-junction-point-support.patch
|
||||
patch_apply ntdll-Junction_Points/0007-ntdll-tests-Add-test-for-deleting-junction-point-tar.patch
|
||||
patch_apply ntdll-Junction_Points/0007-ntdll-Add-support-for-absolute-symlink-creation.patch
|
||||
patch_apply ntdll-Junction_Points/0008-ntdll-Add-support-for-reading-absolute-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0009-ntdll-Add-support-for-deleting-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0010-ntdll-Add-support-for-relative-symlink-creation.patch
|
||||
patch_apply ntdll-Junction_Points/0011-ntdll-Add-support-for-reading-relative-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0012-ntdll-Add-support-for-file-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0013-ntdll-Correctly-report-file-symbolic-links-as-files.patch
|
||||
patch_apply ntdll-Junction_Points/0014-kernel32-Set-error-code-when-attempting-to-delete-fi.patch
|
||||
patch_apply ntdll-Junction_Points/0015-server-Properly-handle-file-symlink-deletion.patch
|
||||
patch_apply ntdll-Junction_Points/0016-kernel32-Implement-CreateSymbolicLink-A-W-with-ntdll.patch
|
||||
(
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for junction point creation.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for reading junction points.", 1 },';
|
||||
@ -4793,7 +4784,16 @@ if test "$enable_ntdll_Junction_Points" -eq 1; then
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add a test for junction point advertisement.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "kernel32,ntdll: Add support for deleting junction points with RemoveDirectory.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "kernel32: Advertise junction point support.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll/tests: Add test for deleting junction point target.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for absolute symlink creation.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for reading absolute symlinks.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for deleting symlinks.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for relative symlink creation.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for reading relative symlinks.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Add support for file symlinks.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "ntdll: Correctly report file symbolic links as files.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "kernel32: Set error code when attempting to delete file symlinks as directories.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "server: Properly handle file symlink deletion.", 1 },';
|
||||
printf '%s\n' '+ { "Erich E. Hoover", "kernel32: Implement CreateSymbolicLink[A|W] with ntdll reparse points.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user