Updated ntdll-Junction_Points patchset

This commit is contained in:
Alistair Leslie-Hughes 2019-04-16 09:43:52 +10:00
parent bfd8af12aa
commit 36e84f2951
19 changed files with 2020 additions and 283 deletions

View File

@ -1 +1,2 @@
Fixes: [44948] kernel32: Implement CreateSymbolicLink
Disabled: True

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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