ntdll-Junction_Points: Updates from Erich E. Hoover.

Includes a fundamental rewrite of the way reparse points are stored.
This commit is contained in:
Zebediah Figura 2022-09-11 19:36:38 -05:00
parent 533337e25e
commit 1ed196f0ef
59 changed files with 3005 additions and 3985 deletions

View File

@ -1,4 +1,4 @@
From a791c331b23e717a5f6c0397e4c290e8e8abd2f2 Mon Sep 17 00:00:00 2001
From 9829e3c307e8019a3a2b9204d1133833863da5f1 Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Sat, 7 Jul 2018 12:57:47 +0200
Subject: [PATCH] server: Create eventfd descriptors for pseudo-fd objects and
@ -11,10 +11,10 @@ Subject: [PATCH] server: Create eventfd descriptors for pseudo-fd objects and
3 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/server/fd.c b/server/fd.c
index 880a5037925..c6db8d13265 100644
index 6a1b89b0e54..8ba704344cf 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -102,6 +102,7 @@
@@ -97,6 +97,7 @@
#include "handle.h"
#include "process.h"
#include "request.h"
@ -22,7 +22,7 @@ index 880a5037925..c6db8d13265 100644
#include "winternl.h"
#include "winioctl.h"
@@ -205,6 +206,7 @@ struct fd
@@ -198,6 +199,7 @@ struct fd
struct completion *completion; /* completion object attached to this fd */
apc_param_t comp_key; /* completion key to set in completion events */
unsigned int comp_flags; /* completion flags */
@ -30,8 +30,8 @@ index 880a5037925..c6db8d13265 100644
};
static void fd_dump( struct object *obj, int verbose );
@@ -1606,6 +1608,9 @@ static void fd_destroy( struct object *obj )
free( fd->unlink_name );
@@ -1668,6 +1670,9 @@ static void fd_destroy( struct object *obj )
if (fd->unix_fd != -1) close( fd->unix_fd );
free( fd->unix_name );
}
+
@ -40,7 +40,7 @@ index 880a5037925..c6db8d13265 100644
}
/* check if the desired access is possible without violating */
@@ -1723,6 +1728,7 @@ static struct fd *alloc_fd_object(void)
@@ -1784,6 +1789,7 @@ static struct fd *alloc_fd_object(void)
fd->poll_index = -1;
fd->completion = NULL;
fd->comp_flags = 0;
@ -48,7 +48,7 @@ index 880a5037925..c6db8d13265 100644
init_async_queue( &fd->read_q );
init_async_queue( &fd->write_q );
init_async_queue( &fd->wait_q );
@@ -1763,11 +1769,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
@@ -1823,11 +1829,15 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
fd->completion = NULL;
fd->comp_flags = 0;
fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
@ -64,7 +64,7 @@ index 880a5037925..c6db8d13265 100644
return fd;
}
@@ -2293,6 +2303,9 @@ void set_fd_signaled( struct fd *fd, int signaled )
@@ -2268,6 +2278,9 @@ void set_fd_signaled( struct fd *fd, int signaled )
if (fd->comp_flags & FILE_SKIP_SET_EVENT_ON_HANDLE) return;
fd->signaled = signaled;
if (signaled) wake_up( fd->user, 0 );
@ -74,7 +74,7 @@ index 880a5037925..c6db8d13265 100644
}
/* check if events are pending and if yes return which one(s) */
@@ -2318,6 +2331,15 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry )
@@ -2293,6 +2306,15 @@ int default_fd_signaled( struct object *obj, struct wait_queue_entry *entry )
return ret;
}
@ -91,7 +91,7 @@ index 880a5037925..c6db8d13265 100644
{
int events = 0;
diff --git a/server/file.h b/server/file.h
index 80f2191c050..224048a4292 100644
index 0ffe0e2c8dc..b5b1e2a1077 100644
--- a/server/file.h
+++ b/server/file.h
@@ -106,6 +106,7 @@ extern char *dup_fd_name( struct fd *root, const char *name );
@ -103,7 +103,7 @@ index 80f2191c050..224048a4292 100644
extern void default_poll_event( struct fd *fd, int event );
extern void fd_cancel_async( struct fd *fd, struct async *async );
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 6b4401810dc..27f4497bfe2 100644
index b8ec17a787a..e01b28f725a 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -168,7 +168,7 @@ static const struct object_ops pipe_server_ops =
@ -125,5 +125,5 @@ index 6b4401810dc..27f4497bfe2 100644
no_signal, /* signal */
pipe_end_get_fd, /* get_fd */
--
2.33.0
2.35.1

View File

@ -1,421 +0,0 @@
From dfe3430017b0ed0f911598a07bc7ccf8de574077 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 20:56:49 -0700
Subject: ntdll: Add support for junction point creation.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
configure.ac | 2 +
dlls/ntdll/tests/file.c | 101 +++++++++++++++++++++++++
dlls/ntdll/unix/file.c | 159 ++++++++++++++++++++++++++++++++++++++++
include/Makefile.in | 1 +
include/ntifs.h | 42 +++++++++++
5 files changed, 305 insertions(+)
create mode 100644 include/ntifs.h
diff --git a/configure.ac b/configure.ac
index bb26503dfd4..9e113d939ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2027,6 +2027,8 @@ AC_CHECK_FUNCS(\
prctl \
proc_pidinfo \
sched_yield \
+ renameat \
+ renameat2 \
setproctitle \
setprogname \
sigprocmask \
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 46037546e6e..7a01e947e13 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 "ntifs.h"
#ifndef IO_COMPLETION_ALL_ACCESS
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
@@ -5322,6 +5323,105 @@ static void test_mailslot_name(void)
CloseHandle( device );
}
+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;
+
+ 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 = 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_size;
+}
+
+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};
+ static const WCHAR fooW[] = {'f','o','o',0};
+ static WCHAR volW[] = {'c',':','\\',0};
+ static const WCHAR dotW[] = {'.',0};
+ REPARSE_DATA_BUFFER *buffer = NULL;
+ DWORD dwret, dwLen, dwFlags;
+ UNICODE_STRING nameW;
+ INT buffer_len;
+ HANDLE handle;
+ BOOL bret;
+
+ /* Create a temporary folder for the junction point tests */
+ GetTempFileNameW(dotW, fooW, 0, path);
+ DeleteFileW(path);
+ if (!CreateDirectoryW(path, NULL))
+ {
+ win_skip("Unable to create a temporary junction point directory.\n");
+ return;
+ }
+
+ /* Check that the volume this folder is located on supports junction points */
+ pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL);
+ volW[0] = nameW.Buffer[4];
+ pRtlFreeUnicodeString( &nameW );
+ GetVolumeInformationW(volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0);
+ if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
+ {
+ skip("File system does not support reparse points.\n");
+ RemoveDirectoryW(path);
+ return;
+ }
+
+ /* Create the folder to be replaced by a junction point */
+ 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 */
+ lstrcpyW(target_path, path);
+ lstrcatW(target_path, targetW);
+ bret = CreateDirectoryW(target_path, NULL);
+ ok(bret, "Failed to create junction point target directory.\n");
+ pRtlDosPathNameToNtPathName_U(target_path, &nameW, NULL, NULL);
+
+ /* Create the junction point */
+ 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(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);
+
+cleanup:
+ /* Cleanup */
+ pRtlFreeUnicodeString(&nameW);
+ HeapFree(GetProcessHeap(), 0, buffer);
+ 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);
+}
+
START_TEST(file)
{
HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
@@ -5394,5 +5494,6 @@ START_TEST(file)
test_ioctl();
test_query_ea();
test_flush_buffers_file();
+ test_reparse_points();
test_mailslot_name();
}
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 689b943a5b1..dd7ab59de67 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -35,6 +35,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <libgen.h>
#include <limits.h>
#include <unistd.h>
#ifdef HAVE_MNTENT_H
@@ -135,6 +136,7 @@
#include "wine/list.h"
#include "wine/debug.h"
#include "unix_private.h"
+#include "ntifs.h"
WINE_DEFAULT_DEBUG_CHANNEL(file);
WINE_DECLARE_DEBUG_CHANNEL(winediag);
@@ -146,6 +148,10 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
#undef EXT2_IOC_GETFLAGS
#undef EXT4_CASEFOLD_FL
+#ifndef RENAME_EXCHANGE
+#define RENAME_EXCHANGE (1 << 1)
+#endif
+
#ifdef linux
/* We want the real kernel dirent structure, not the libc one */
@@ -470,6 +476,32 @@ static int xattr_set( const char *path, const char *name, void *value, size_t si
#endif
}
+#ifndef HAVE_RENAMEAT
+int renameat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath )
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+#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 );
+#if defined(__NR_renameat2)
+ return syscall( __NR_renameat2, olddirfd, oldpath, newdirfd, newpath, flags );
+#elif defined(RENAME_SWAP)
+ return renameatx_np(olddirfd, oldpath, newdirfd, newpath,
+ (flags & RENAME_EXCHANGE ? RENAME_SWAP : 0));
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+#endif /* HAVE_RENAMEAT2 */
+
/* get space from the current directory data buffer, allocating a new one if necessary */
static void *get_dir_data_space( struct dir_data *data, unsigned int size )
{
@@ -5932,6 +5964,116 @@ static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer,
}
+/*
+ * Retrieve the unix name corresponding to a file handle, remove that directory, and then symlink
+ * the requested directory to the location of the old directory.
+ */
+NTSTATUS create_reparse_point(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], *d;
+ SIZE_T unix_dest_len = PATH_MAX;
+ char *unix_src, *unix_dest;
+ 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;
+ for (;;)
+ {
+ unix_dest = malloc( unix_dest_len );
+ if (!unix_dest)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ status = wine_nt_to_unix_file_name( &nt_dest, unix_dest, &unix_dest_len, FALSE );
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
+ free( unix_dest );
+ }
+ if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
+ goto cleanup;
+ dest_allocated = TRUE;
+
+ TRACE( "Linking %s to %s\n", unix_src, unix_dest );
+
+ /* Encode the reparse tag into the symlink */
+ strcpy( magic_dest, "/" );
+ for (i = 0; i < sizeof(ULONG)*8; i++)
+ {
+ if ((buffer->ReparseTag >> i) & 1)
+ strcat( magic_dest, "." );
+ strcat( magic_dest, "/" );
+ }
+ strcat( magic_dest, unix_dest );
+
+ /* Produce the link in a temporary location in the same folder */
+ strcpy( tmpdir, unix_src );
+ d = dirname( tmpdir);
+ if (d != tmpdir) strcpy( tmpdir, d );
+ strcat( tmpdir, "/.winelink.XXXXXX" );
+ if (mkdtemp( tmpdir ) == NULL)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ tempdir_created = TRUE;
+ strcpy( tmplink, tmpdir );
+ strcat( tmplink, "/tmplink" );
+ if (symlink( magic_dest, tmplink ))
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ /* Atomically move the link into position */
+ if (!renameat2( -1, tmplink, -1, unix_src, RENAME_EXCHANGE ))
+ {
+ /* 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 ))
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ if (rename( tmplink, unix_src ))
+ {
+ status = errno_to_status( errno );
+ goto cleanup; /* not moved, orignal file/folder at destination is orphaned */
+ }
+ }
+ else
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ status = STATUS_SUCCESS;
+
+cleanup:
+ if (tempdir_created) rmdir( tmpdir );
+ if (dest_allocated) free( unix_dest );
+ if (src_allocated) free( unix_src );
+ if (needs_close) close( dest_fd );
+ return status;
+}
+
+
/******************************************************************************
* NtFsControlFile (NTDLL.@)
*/
@@ -6014,6 +6156,23 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
break;
}
+ case FSCTL_SET_REPARSE_POINT:
+ {
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
+
+ switch(buffer->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ status = create_reparse_point( handle, buffer );
+ break;
+ default:
+ FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ break;
+ }
+
case FSCTL_SET_SPARSE:
TRACE("FSCTL_SET_SPARSE: Ignoring request\n");
io->Information = 0;
diff --git a/include/Makefile.in b/include/Makefile.in
index c99b0e1e1c1..973b5a52569 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -551,6 +551,7 @@ SOURCES = \
ntdef.h \
ntdsapi.h \
ntgdi.h \
+ ntifs.h \
ntlsa.h \
ntquery.h \
ntsecapi.h \
diff --git a/include/ntifs.h b/include/ntifs.h
new file mode 100644
index 00000000000..21d42e17325
--- /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;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+#endif /* __WINE_NTIFS_H */
--
2.17.1

View File

@ -1,206 +0,0 @@
From 2253ea0333a80d04657da9018023c28b036ecfa5 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/tests/file.c | 20 ++++++-
dlls/ntdll/unix/file.c | 129 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 7a01e947e13..3b7746650f3 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5354,14 +5354,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};
+ INT buffer_len, string_len, path_len, total_len;
static const WCHAR fooW[] = {'f','o','o',0};
static WCHAR volW[] = {'c',':','\\',0};
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
UNICODE_STRING nameW;
- INT buffer_len;
HANDLE handle;
+ WCHAR *dest;
BOOL bret;
/* Create a temporary folder for the junction point tests */
@@ -5409,6 +5410,23 @@ static void test_reparse_points(void)
buffer_len = build_reparse_buffer(nameW.Buffer, &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());
+
+ /* 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(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((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));
+ path_len = buffer->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+ path_len += buffer->MountPointReparseBuffer.PrintNameLength/sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len+1])
+ - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
+ ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
+ buffer->ReparseDataLength, total_len);
CloseHandle(handle);
cleanup:
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index dd7ab59de67..3b4b1cac833 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6074,6 +6074,127 @@ 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 get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *size)
+{
+ char *unix_src, unix_dest[PATH_MAX];
+ INT prefix_len, path_len, total_len;
+ VOID *subst_name, *print_name;
+ SIZE_T nt_dest_len = PATH_MAX;
+ BOOL dest_allocated = FALSE;
+ int dest_fd, needs_close;
+ ULONG out_size = *size;
+ int unix_dest_len;
+ DWORD max_length;
+ NTSTATUS status;
+ WCHAR *nt_dest;
+ 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;
+
+ ret = readlink( unix_src, unix_dest, sizeof(unix_dest) );
+ if (ret < 0)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ unix_dest_len = ret;
+
+ /* Decode the reparse tag from the symlink */
+ p = unix_dest;
+ 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_len -= (p - unix_dest);
+ memmove(unix_dest, p, unix_dest_len);
+ unix_dest[unix_dest_len] = 0;
+
+ for (;;)
+ {
+ nt_dest = malloc( nt_dest_len * sizeof(WCHAR) );
+ if (!nt_dest)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ status = wine_unix_to_nt_file_name( unix_dest, nt_dest, &nt_dest_len );
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
+ free( nt_dest );
+ }
+ dest_allocated = TRUE;
+ if (status != STATUS_SUCCESS)
+ goto cleanup;
+ nt_dest_len *= sizeof(WCHAR);
+
+ prefix_len = strlen("\\??\\");
+ switch(buffer->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[1]);
+ path_len = 0;
+ buffer->MountPointReparseBuffer.SubstituteNameOffset = path_len;
+ buffer->MountPointReparseBuffer.SubstituteNameLength = nt_dest_len;
+ path_len += nt_dest_len + sizeof(WCHAR);
+ subst_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ buffer->MountPointReparseBuffer.PrintNameOffset = path_len;
+ buffer->MountPointReparseBuffer.PrintNameLength = nt_dest_len - prefix_len*sizeof(WCHAR);
+ print_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
+ path_len += (nt_dest_len - prefix_len*sizeof(WCHAR)) + sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len/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_len > max_length)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto cleanup;
+ }
+
+ memcpy( subst_name, nt_dest, nt_dest_len );
+ memcpy( print_name, &nt_dest[prefix_len], nt_dest_len - prefix_len*sizeof(WCHAR) );
+ *size = total_len;
+ buffer->ReparseDataLength = total_len - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
+ status = STATUS_SUCCESS;
+
+cleanup:
+ if (dest_allocated) free( nt_dest );
+ if (needs_close) close( dest_fd );
+ return status;
+}
+
+
/******************************************************************************
* NtFsControlFile (NTDLL.@)
*/
@@ -6156,6 +6277,14 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
break;
}
+ case FSCTL_GET_REPARSE_POINT:
+ {
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
+ ULONG size = out_size;
+ status = get_reparse_point( handle, buffer, &size );
+ io->Information = size;
+ break;
+ }
case FSCTL_SET_REPARSE_POINT:
{
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
--
2.17.1

View File

@ -0,0 +1,293 @@
From ded223a1f564442c26860d9e708b963710ecea21 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: [PATCH] ntdll: Add support for reading reparse points.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 21 +++-
dlls/ntdll/unix/file.c | 209 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 228 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 5f6cb223951..33e49793319 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5359,14 +5359,14 @@ static void test_reparse_points(void)
static const WCHAR reparseW[] = {'\\','r','e','p','a','r','s','e',0};
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
static const WCHAR parentW[] = {'\\','.','.','\\',0};
+ INT buffer_len, string_len, path_len, total_len;
static const WCHAR fooW[] = {'f','o','o',0};
static WCHAR volW[] = {'c',':','\\',0};
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
+ WCHAR *dest, *long_path;
UNICODE_STRING nameW;
- WCHAR *long_path;
- INT buffer_len;
HANDLE handle;
BOOL bret;
@@ -5463,6 +5463,23 @@ static void test_reparse_points(void)
buffer_len = build_reparse_buffer(long_path, &buffer);
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(bret, "Failed to create junction point! (0x%lx)\n", GetLastError());
+
+ /* Read back the junction point */
+ HeapFree(GetProcessHeap(), 0, buffer);
+ buffer_len = sizeof(*buffer) + 2*32767;
+ 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 junction point! (last error=0x%lx)\n", GetLastError());
+ string_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
+ dest = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ ok((memcmp(dest, long_path, string_len) == 0), "Junction point destination does not match ('%s' != '%s')!\n",
+ wine_dbgstr_w(dest), wine_dbgstr_w(long_path));
+ path_len = buffer->MountPointReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+ path_len += buffer->MountPointReparseBuffer.PrintNameLength/sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len+1])
+ - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
+ ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
+ buffer->ReparseDataLength, total_len);
CloseHandle(handle);
cleanup:
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 3fb4ded846c..3a1489c35d0 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -318,6 +318,84 @@ static UINT encode_base64url( const char *bin, unsigned int len, char *base64 )
return n;
}
+static inline char decode_base64url_char( char c )
+{
+ if (c >= 'A' && c <= 'Z') return c - 'A';
+ if (c >= 'a' && c <= 'z') return c - 'a' + 26;
+ if (c >= '0' && c <= '9') return c - '0' + 52;
+ if (c == '-') return 62;
+ if (c == '_') return 63;
+ return 64;
+}
+
+/* decode a base64url (RFC 4648 §5) binary string
+ * 1) start with base64
+ * 2) replace '+' by '-' and replace '/' by '_'
+ * 3) do not add padding characters
+ * 4) do not add line separators
+ */
+static unsigned int decode_base64url( const char *base64, unsigned int len, char *buf )
+{
+ unsigned int i = 0;
+ char c0, c1, c2, c3;
+ const char *p = base64;
+
+ while (len > 4)
+ {
+ if ((c0 = decode_base64url_char( p[0] )) > 63) return 0;
+ if ((c1 = decode_base64url_char( p[1] )) > 63) return 0;
+ if ((c2 = decode_base64url_char( p[2] )) > 63) return 0;
+ if ((c3 = decode_base64url_char( p[3] )) > 63) return 0;
+
+ if (buf)
+ {
+ buf[i + 0] = (c0 << 2) | (c1 >> 4);
+ buf[i + 1] = (c1 << 4) | (c2 >> 2);
+ buf[i + 2] = (c2 << 6) | c3;
+ }
+ len -= 4;
+ i += 3;
+ p += 4;
+ }
+ if (len == 2)
+ {
+ if ((c0 = decode_base64url_char( p[0] )) > 63) return 0;
+ if ((c1 = decode_base64url_char( p[1] )) > 63) return 0;
+
+ if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
+ i++;
+ }
+ else if (len == 3)
+ {
+ if ((c0 = decode_base64url_char( p[0] )) > 63) return 0;
+ if ((c1 = decode_base64url_char( p[1] )) > 63) return 0;
+ if ((c2 = decode_base64url_char( p[2] )) > 63) return 0;
+
+ if (buf)
+ {
+ buf[i + 0] = (c0 << 2) | (c1 >> 4);
+ buf[i + 1] = (c1 << 4) | (c2 >> 2);
+ }
+ i += 2;
+ }
+ else
+ {
+ if ((c0 = decode_base64url_char( p[0] )) > 63) return 0;
+ if ((c1 = decode_base64url_char( p[1] )) > 63) return 0;
+ if ((c2 = decode_base64url_char( p[2] )) > 63) return 0;
+ if ((c3 = decode_base64url_char( p[3] )) > 63) return 0;
+
+ if (buf)
+ {
+ buf[i + 0] = (c0 << 2) | (c1 >> 4);
+ buf[i + 1] = (c1 << 4) | (c2 >> 2);
+ buf[i + 2] = (c2 << 6) | c3;
+ }
+ i += 3;
+ }
+ return i;
+}
+
/* create a directory and all the needed parent directories */
static int mkdir_p(const char *path, mode_t mode)
{
@@ -3606,6 +3684,129 @@ 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 get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *size)
+{
+ char link_dir[PATH_MAX], original_dir[PATH_MAX], *d;
+ int link_path_len, buffer_len, encoded_len;
+ char *unix_src, link_path[PATH_MAX];
+ REPARSE_DATA_BUFFER header;
+ ULONG out_size = *size;
+ char *encoded = NULL;
+ NTSTATUS status;
+ ssize_t ret;
+ int depth;
+ char *p;
+
+ if ((status = server_get_unix_name( handle, &unix_src )))
+ goto cleanup;
+
+ ret = readlink( unix_src, link_path, sizeof(link_path) );
+ if (ret < 0)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ link_path_len = ret;
+ link_path[link_path_len] = 0;
+ if (strncmp( link_path, ".REPARSE_POINT/", 15) != 0)
+ {
+ status = STATUS_NOT_IMPLEMENTED;
+ goto cleanup;
+ }
+ encoded_len = link_path_len;
+ encoded = malloc( encoded_len );
+ if (!encoded)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Copy the encoded data from the inital symlink */
+ encoded[0] = 0;
+ p = &link_path[15];
+ if ((p = strchr( p, '/' )) == NULL)
+ {
+ status = STATUS_IO_REPARSE_DATA_INVALID;
+ goto cleanup;
+ }
+ p++;
+ if (*(p++) == '.')
+ p++;
+ for (depth=0; p < link_path + link_path_len; p += NAME_MAX+1, depth++)
+ strncat( encoded, p, NAME_MAX );
+ encoded[strlen(encoded)-1] = 0; /* chunk id */
+ encoded[strlen(encoded)-1] = 0; /* final slash */
+
+ /* get the length of the full buffer so that we know when to stop collecting data */
+ decode_base64url( encoded, sizeof(header), (char*)&header );
+ buffer_len = header.ReparseDataLength+FIELD_OFFSET(typeof(header), GenericReparseBuffer);
+ *size = buffer_len;
+
+ if (buffer_len > out_size)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto cleanup;
+ }
+ encoded_len = (int)ceil(buffer_len*4/3.0) + 1;
+ encoded = realloc( encoded, encoded_len );
+ if (!encoded)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* change to the link folder so that we can build any necessary additional data */
+ getcwd( original_dir, PATH_MAX );
+ strcpy( link_dir, unix_src );
+ d = dirname( link_dir);
+ if (d != link_dir) strcpy( link_dir, d );
+ chdir( link_dir );
+
+ /* Copy the encoded data from the follow on symlinks */
+ while(strlen(encoded) < encoded_len-1)
+ {
+ strcpy( link_dir, link_path );
+ ret = readlink( link_dir, link_path, sizeof(link_path) );
+ if (ret < 0)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ link_path_len = ret;
+ link_path[link_path_len] = 0;
+
+ p = &link_path[3*depth];
+ for (depth=0; p < link_path + link_path_len; p += NAME_MAX+1, depth++)
+ strncat( encoded, p, NAME_MAX );
+ encoded[strlen(encoded)-1] = 0; /* chunk id */
+ encoded[strlen(encoded)-1] = 0; /* final slash */
+
+ link_dir[strlen(link_dir)-1] = 0;
+ chdir( link_dir );
+ }
+
+ /* revert to the original directory */
+ chdir( original_dir );
+
+ /* Decode the reparse buffer from the base64-encoded symlink data */
+ *size = decode_base64url( encoded, strlen(encoded), (char*)buffer );
+ status = STATUS_SUCCESS;
+ if (buffer_len != *size)
+ {
+ status = STATUS_IO_REPARSE_DATA_INVALID;
+ ERR("Size mismatch decoding reparse point buffer (%d != %d)\n", *size, buffer_len);
+ }
+
+cleanup:
+ free( encoded );
+ return status;
+}
+
+
/******************************************************************************
* lookup_unix_name
*
@@ -6353,6 +6554,14 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
break;
}
+ case FSCTL_GET_REPARSE_POINT:
+ {
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
+ ULONG size = out_size;
+ status = get_reparse_point( handle, buffer, &size );
+ io->Information = size;
+ break;
+ }
case FSCTL_SET_REPARSE_POINT:
{
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
--
2.35.1

View File

@ -1,22 +1,22 @@
From 0eece31261dab2d05c3d9e23138d87a0495f5cbb Mon Sep 17 00:00:00 2001
From 2a596f3d73eb9243960ed48c9b348c0e1461284e Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Thu, 16 Jan 2014 21:00:21 -0700
Subject: ntdll: Add support for deleting junction points.
Subject: [PATCH] ntdll: Add support for deleting reparse points.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 23 +++++++++-
dlls/ntdll/unix/file.c | 97 +++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/unix/file.c | 99 +++++++++++++++++++++++++++++++++++++++++
include/ntifs.h | 12 +++++
3 files changed, 131 insertions(+), 1 deletion(-)
3 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 3b7746650f3..0031f49693c 100644
index 33e49793319..54f7c7fc860 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5355,11 +5355,14 @@ static void test_reparse_points(void)
WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH];
@@ -5360,12 +5360,15 @@ static void test_reparse_points(void)
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
static const WCHAR parentW[] = {'\\','.','.','\\',0};
INT buffer_len, string_len, path_len, total_len;
+ FILE_BASIC_INFORMATION old_attrib, new_attrib;
static const WCHAR fooW[] = {'f','o','o',0};
@ -25,20 +25,21 @@ index 3b7746650f3..0031f49693c 100644
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
WCHAR *dest, *long_path;
+ IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE handle;
WCHAR *dest;
@@ -5407,6 +5410,8 @@ static void test_reparse_points(void)
win_skip("Failed to open junction point directory handle (0x%x).\n", GetLastError());
BOOL bret;
@@ -5460,6 +5463,8 @@ static void test_reparse_points(void)
win_skip("Failed to open junction point directory handle (0x%lx).\n", GetLastError());
goto cleanup;
}
+ 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);
+ ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%lx).\n", dwret);
buffer_len = build_reparse_buffer(long_path, &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());
@@ -5427,6 +5432,22 @@ static void test_reparse_points(void)
ok(bret, "Failed to create junction point! (0x%lx)\n", GetLastError());
@@ -5480,6 +5485,22 @@ static void test_reparse_points(void)
- FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
buffer->ReparseDataLength, total_len);
@ -47,22 +48,22 @@ index 3b7746650f3..0031f49693c 100644
+ 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 junction point folder's attributes (0x%x).\n", dwret);
+ ok(dwret == STATUS_SUCCESS, "Failed to set junction point folder's attributes (0x%lx).\n", dwret);
+ memset(&guid_buffer, 0x00, sizeof(guid_buffer));
+ guid_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ 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());
+ ok(bret, "Failed to delete junction point! (0x%lx)\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 junction point folder's attributes (0x%x).\n", dwret);
+ ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%lx).\n", dwret);
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
+ "Junction point folder's access time does not match.\n");
CloseHandle(handle);
cleanup:
@@ -5434,7 +5455,7 @@ cleanup:
pRtlFreeUnicodeString(&nameW);
@@ -5488,7 +5509,7 @@ cleanup:
HeapFree(GetProcessHeap(), 0, long_path);
HeapFree(GetProcessHeap(), 0, buffer);
bret = RemoveDirectoryW(reparse_path);
- todo_wine ok(bret, "Failed to remove temporary reparse point directory!\n");
@ -71,10 +72,10 @@ index 3b7746650f3..0031f49693c 100644
ok(bret, "Failed to remove temporary target directory!\n");
RemoveDirectoryW(path);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 3b4b1cac833..79cf0f2dbca 100644
index 3a1489c35d0..af390214624 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6195,6 +6195,87 @@ cleanup:
@@ -3807,6 +3807,99 @@ cleanup:
}
@ -84,9 +85,10 @@ index 3b4b1cac833..79cf0f2dbca 100644
+ */
+NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
+{
+ char tmpdir[PATH_MAX], tmpfile[PATH_MAX], *d;
+ char tmpdir[PATH_MAX], tmplink[PATH_MAX], *d;
+ BOOL tempdir_created = FALSE;
+ int dest_fd, needs_close;
+ BOOL is_dir = TRUE;
+ NTSTATUS status;
+ char *unix_name;
+ struct stat st;
@ -99,12 +101,13 @@ index 3b4b1cac833..79cf0f2dbca 100644
+
+ TRACE( "Deleting symlink %s\n", unix_name );
+
+ /* Produce the directory in a temporary location in the same folder */
+ /* Produce the file/directory in a temporary location in the same folder */
+ if (fstat( dest_fd, &st ) == -1)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ is_dir = S_ISDIR(st.st_mode);
+ strcpy( tmpdir, unix_name );
+ d = dirname( tmpdir);
+ if (d != tmpdir) strcpy( tmpdir, d );
@ -115,20 +118,30 @@ index 3b4b1cac833..79cf0f2dbca 100644
+ goto cleanup;
+ }
+ tempdir_created = TRUE;
+ strcpy( tmpfile, tmpdir );
+ strcat( tmpfile, "/tmpfile" );
+ if (mkdir( tmpfile, st.st_mode ))
+ strcpy( tmplink, tmpdir );
+ strcat( tmplink, "/tmplink" );
+ if (is_dir && mkdir( tmplink, st.st_mode ))
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ /* attemp to retain the ownership (if possible) */
+ lchown( tmpfile, st.st_uid, st.st_gid );
+ /* Atomically move the directory into position */
+ if (!renameat2( -1, tmpfile, -1, unix_name, RENAME_EXCHANGE ))
+ else if (!is_dir)
+ {
+ /* success: link and folder have switched locations */
+ unlink( tmpfile ); /* remove the link (at folder location) */
+ int fd = open( tmplink, O_CREAT|O_WRONLY|O_TRUNC, st.st_mode );
+ if (fd < 0)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ close( fd );
+ }
+ /* attemp to retain the ownership (if possible) */
+ lchown( tmplink, st.st_uid, st.st_gid );
+ /* Atomically move the directory into position */
+ if (!renameat2( -1, tmplink, -1, unix_name, RENAME_EXCHANGE ))
+ {
+ /* success: link and folder/file have switched locations */
+ unlink( tmplink ); /* remove the file (at link location) */
+ }
+ else if (errno == ENOSYS)
+ {
@ -139,7 +152,7 @@ index 3b4b1cac833..79cf0f2dbca 100644
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ if (rename( tmpfile, unix_name ))
+ if (rename( tmplink, unix_name ))
+ {
+ status = errno_to_status( errno );
+ goto cleanup; /* not moved, orignal file/folder at destination is orphaned */
@ -160,26 +173,16 @@ index 3b4b1cac833..79cf0f2dbca 100644
+
+
/******************************************************************************
* NtFsControlFile (NTDLL.@)
*/
@@ -6277,6 +6358,22 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
* lookup_unix_name
*
@@ -6554,6 +6647,12 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
break;
}
+ case FSCTL_DELETE_REPARSE_POINT:
+ {
+ REPARSE_GUID_DATA_BUFFER *buffer = (REPARSE_GUID_DATA_BUFFER *)in_buffer;
+
+ switch(buffer->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ status = remove_reparse_point( handle, buffer );
+ break;
+ default:
+ FIXME("stub: FSCTL_DELETE_REPARSE_POINT(%x)\n", buffer->ReparseTag);
+ status = STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+ status = remove_reparse_point( handle, buffer );
+ break;
+ }
case FSCTL_GET_REPARSE_POINT:
@ -207,5 +210,5 @@ index 21d42e17325..4539b89d583 100644
+
#endif /* __WINE_NTIFS_H */
--
2.17.1
2.35.1

View File

@ -1,29 +0,0 @@
From a8d8375757d1736dca6f4c7a185e766fbe513bd0 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 +++++
1 file changed, 5 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 0031f49693c..521857575d8 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5416,6 +5416,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(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);
+
/* Read back the junction point */
HeapFree(GetProcessHeap(), 0, buffer);
buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR);
--
2.17.1

View File

@ -0,0 +1,77 @@
From f93a10fd26b694aa0df49f032844f9ddd34c632d 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: [PATCH] ntdll: Add support for testing for reparse points with
GetFileAttributes.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 5 +++++
dlls/ntdll/unix/file.c | 25 ++++++++++++++++++++-----
2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 54f7c7fc860..7fa50ec59a6 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5469,6 +5469,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%lx)\n", GetLastError());
+ /* Check the file attributes of the junction point */
+ dwret = GetFileAttributesW(reparse_path);
+ ok(dwret != (DWORD)~0, "Reparse point doesn't exist (attributes: 0x%lx)!\n", dwret);
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a reparse point! (attributes: 0x%lx)\n", dwret);
+
/* Read back the junction point */
HeapFree(GetProcessHeap(), 0, buffer);
buffer_len = sizeof(*buffer) + 2*32767;
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index af390214624..201eeab7013 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -1788,10 +1788,20 @@ static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULON
*attr = 0;
ret = fstat( fd, st );
if (ret == -1) return ret;
- *attr |= get_file_attributes( st );
/* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st ))
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
+ if (S_ISLNK( st->st_mode ))
+ {
+ BOOL is_dir;
+
+ /* symbolic links (either junction points or NT symlinks) are "reparse points" */
+ *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
+ /* whether a reparse point is a file or a directory is stored inside the link target */
+ if (is_reparse_dir( fd, "", &is_dir ) == 0)
+ st->st_mode = (st->st_mode & ~S_IFMT) | (is_dir ? S_IFDIR : S_IFREG);
+ }
+ *attr |= get_file_attributes( st );
return ret;
}
@@ -1841,10 +1851,15 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
if (ret == -1) return ret;
if (S_ISLNK( st->st_mode ))
{
- 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;
+ BOOL is_dir;
+
+ /* return information about the destination (unless this is a dangling symlink) */
+ stat( path, st );
+ /* symbolic links (either junction points or NT symlinks) are "reparse points" */
+ *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
+ /* whether a reparse point is a file or a directory is stored inside the link target */
+ if (is_reparse_dir( AT_FDCWD, path, &is_dir ) == 0)
+ st->st_mode = (st->st_mode & ~S_IFMT) | (is_dir ? S_IFDIR : S_IFREG);
}
else if (S_ISDIR( st->st_mode ) && (parent_path = malloc( strlen(path) + 4 )))
{
--
2.35.1

View File

@ -1,206 +0,0 @@
From 6eb3781353711b7a31ef5cc391cf7431e3f77640 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: server: Add support for deleting junction points with
RemoveDirectory.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 34 +++++++++++++++++++++++++++++++++-
server/fd.c | 37 ++++++++++++++++++++++++++++---------
2 files changed, 61 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 521857575d8..c5b3b83802e 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5361,7 +5361,7 @@ static void test_reparse_points(void)
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
- DWORD dwret, dwLen, dwFlags;
+ DWORD dwret, dwLen, dwFlags, err;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE handle;
@@ -5455,6 +5455,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);
+ 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(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);
+ bret = RemoveDirectoryW(reparse_path);
+ ok(bret, "Failed to delete junction point as directory!\n");
+ 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(reparse_path, NULL);
+ 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);
+ 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);
+ 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(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);
diff --git a/server/fd.c b/server/fd.c
index c9a21186722..ce1c47d58a9 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -163,7 +163,8 @@ struct closed_fd
struct list entry; /* entry in inode closed list */
int unix_fd; /* the unix file descriptor */
int unlink; /* whether to unlink on close: -1 - implicit FILE_DELETE_ON_CLOSE, 1 - explicit disposition */
- char *unix_name; /* name to unlink on close, points to parent fd unix_name */
+ char *unlink_name; /* name to unlink on close, points to parent fd unix_name */
+ char *unix_name; /* name to real file path, points to parent fd unix_name */
};
struct fd
@@ -178,6 +179,7 @@ struct fd
unsigned int access; /* file access (FILE_READ_DATA etc.) */
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
unsigned int sharing; /* file sharing mode */
+ char *unlink_name; /* file name to unlink on close */
char *unix_name; /* unix file name */
WCHAR *nt_name; /* NT file name */
data_size_t nt_namelen; /* length of NT file name */
@@ -1125,6 +1127,7 @@ static void inode_close_pending( struct inode *inode, int keep_unlinks )
if (!keep_unlinks || !fd->unlink) /* get rid of it unless there's an unlink pending on that file */
{
list_remove( ptr );
+ free( fd->unlink_name );
free( fd->unix_name );
free( fd );
}
@@ -1159,12 +1162,13 @@ 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->unlink_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 );
+ if (S_ISDIR(st.st_mode)) rmdir( fd->unlink_name );
+ else unlink( fd->unlink_name );
}
}
+ free( fd->unlink_name );
free( fd->unix_name );
free( fd );
}
@@ -1585,6 +1589,7 @@ static void fd_destroy( struct object *obj )
else /* no inode, close it right away */
{
if (fd->unix_fd != -1) close( fd->unix_fd );
+ free( fd->unlink_name );
free( fd->unix_name );
}
}
@@ -1694,6 +1699,7 @@ static struct fd *alloc_fd_object(void)
fd->options = 0;
fd->sharing = 0;
fd->unix_fd = -1;
+ fd->unlink_name = NULL;
fd->unix_name = NULL;
fd->nt_name = NULL;
fd->nt_namelen = 0;
@@ -1731,6 +1737,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
fd->access = 0;
fd->options = options;
fd->sharing = 0;
+ fd->unlink_name = NULL;
fd->unix_name = NULL;
fd->nt_name = NULL;
fd->nt_namelen = 0;
@@ -1772,6 +1779,12 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha
fd->nt_namelen = orig->nt_namelen;
}
+ if (orig->unlink_name)
+ {
+ if (!(fd->unlink_name = mem_alloc( strlen(orig->unlink_name) + 1 ))) goto failed;
+ strcpy( fd->unlink_name, orig->unlink_name );
+ }
+
if (orig->inode)
{
struct closed_fd *closed = mem_alloc( sizeof(*closed) );
@@ -1784,6 +1797,7 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha
}
closed->unix_fd = fd->unix_fd;
closed->unlink = 0;
+ closed->unlink_name = fd->unlink_name;
closed->unix_name = fd->unix_name;
fd->closed = closed;
fd->inode = (struct inode *)grab_object( orig->inode );
@@ -1968,18 +1982,19 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
fd->unix_name = NULL;
if ((path = dup_fd_name( root, name )))
{
+ fd->unlink_name = path;
fd->unix_name = realpath( path, NULL );
- free( path );
}
closed_fd->unix_fd = fd->unix_fd;
closed_fd->unlink = 0;
+ closed_fd->unlink_name = fd->unlink_name;
closed_fd->unix_name = fd->unix_name;
- fstat( fd->unix_fd, &st );
+ lstat( fd->unlink_name, &st );
*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 );
@@ -1997,6 +2012,9 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
list_add_head( &inode->open, &fd->inode_entry );
closed_fd = NULL;
+ fstat( fd->unix_fd, &st );
+ *mode = st.st_mode;
+
/* check directory options */
if ((options & FILE_DIRECTORY_FILE) && !S_ISDIR(st.st_mode))
{
@@ -2644,10 +2662,11 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
free( fd->nt_name );
fd->nt_name = dup_nt_name( root, nt_name, &fd->nt_namelen );
+ free( fd->unlink_name );
free( fd->unix_name );
+ fd->closed->unlink_name = fd->unlink_name = name;
fd->closed->unix_name = fd->unix_name = realpath( name, NULL );
- free( name );
- if (!fd->unix_name)
+ if (!fd->unlink_name || !fd->unix_name)
set_error( STATUS_NO_MEMORY );
return;
--
2.17.1

View File

@ -0,0 +1,276 @@
From 4f3ab4bdb7daf7b38babb2ff4758c6633e703b7c 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: [PATCH] server: Implement FILE_OPEN_REPARSE_POINT option.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/kernelbase/file.c | 2 +
server/fd.c | 142 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 137 insertions(+), 7 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 8ae982294f6..2ab399ec8e2 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -735,6 +735,8 @@ static UINT get_nt_file_options( DWORD attributes )
options |= FILE_SEQUENTIAL_ONLY;
if (attributes & FILE_FLAG_WRITE_THROUGH)
options |= FILE_WRITE_THROUGH;
+ if (attributes & FILE_FLAG_OPEN_REPARSE_POINT)
+ options |= FILE_OPEN_REPARSE_POINT;
return options;
}
diff --git a/server/fd.c b/server/fd.c
index eaebe044f37..1ed975673a6 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -31,6 +31,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <libgen.h>
#include <poll.h>
#ifdef HAVE_LINUX_MAJOR_H
#include <linux/major.h>
@@ -101,6 +102,10 @@
#include "winioctl.h"
#include "ddk/wdm.h"
+#if !defined(O_SYMLINK) && defined(O_PATH)
+# define O_SYMLINK (O_NOFOLLOW | O_PATH)
+#endif
+
#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL_CREATE)
# include <sys/epoll.h>
# define USE_EPOLL
@@ -1140,6 +1145,59 @@ static void inode_dump( struct object *obj, int verbose )
fprintf( stderr, "\n" );
}
+/* recursively delete everything in a directory */
+static int rmdir_recursive( int dir_fd, const char *pathname )
+{
+ int ret = 0, tmp_fd;
+ struct dirent *p;
+ struct stat st;
+ DIR *d;
+
+ tmp_fd = openat( dir_fd, pathname, O_DIRECTORY|O_RDONLY|O_NONBLOCK|O_CLOEXEC );
+ d = fdopendir( tmp_fd );
+ if (!d)
+ {
+ close( tmp_fd );
+ return -1;
+ }
+
+ while (!ret && (p = readdir( d )))
+ {
+ if (!strcmp( p->d_name, "." ) || !strcmp( p->d_name, ".." ))
+ continue;
+ if (!fstatat( dirfd(d), p->d_name, &st, AT_SYMLINK_NOFOLLOW ))
+ {
+ if (S_ISDIR( st.st_mode ))
+ ret = rmdir_recursive( dirfd(d), p->d_name );
+ else
+ ret = unlinkat( dirfd(d), p->d_name, 0 );
+ }
+ }
+ closedir( d );
+ return unlinkat( dir_fd, pathname, AT_REMOVEDIR );
+}
+
+/* determine whether a reparse point is meant to be a directory or a file */
+static int is_reparse_dir( const char *path, int *is_dir )
+{
+ char link_path[PATH_MAX], *p;
+ int ret;
+
+ if ((ret = readlink( path, link_path, sizeof(link_path) )) < 0)
+ return ret;
+ /* confirm that this file is a reparse point */
+ if (strncmp( link_path, ".REPARSE_POINT/", 15) != 0)
+ return -1;
+ /* skip past the reparse point indicator and the filename */
+ p = &link_path[15];
+ if ((p = strchr( p, '/' )) == NULL)
+ return -1;
+ p++;
+ /* read the flag indicating whether this reparse point is a directory */
+ if (is_dir) *is_dir = (*p == '.');
+ return 0;
+}
+
static void inode_destroy( struct object *obj )
{
struct inode *inode = (struct inode *)obj;
@@ -1159,10 +1217,29 @@ 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)
{
+ int is_reparse_point = (is_reparse_dir( fd->unix_name, NULL ) == 0);
if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name );
else unlink( fd->unix_name );
+ /* remove reparse point metadata (if applicable) */
+ if (is_reparse_point)
+ {
+ char tmp[PATH_MAX], metadata_path[PATH_MAX], *p;
+
+ strcpy( tmp, fd->unix_name );
+ p = dirname( tmp );
+ if (p != tmp ) strcpy( tmp, p );
+ strcpy( metadata_path, tmp );
+ strcat( metadata_path, "/.REPARSE_POINT/" );
+ strcpy( tmp, fd->unix_name );
+ p = basename( tmp );
+ if (p != tmp) strcpy( tmp, p );
+ strcat( metadata_path, tmp );
+
+ rmdir_recursive( AT_FDCWD, metadata_path );
+ rmdir( dirname( metadata_path ) );
+ }
}
}
free( fd->unix_name );
@@ -1888,6 +1965,36 @@ void get_nt_name( struct fd *fd, struct unicode_str *name )
name->len = fd->nt_namelen;
}
+/* check whether a file is a symlink */
+int check_symlink( char *name )
+{
+ struct stat st;
+
+ lstat( name, &st );
+ return S_ISLNK( st.st_mode );
+}
+
+/* if flags does not contain O_SYMLINK then just use realpath */
+/* otherwise return the real path of the parent and append the filename of the symlink */
+char *normalize_path( const char *path, int flags )
+{
+ char tmp[PATH_MAX], resolved_path[PATH_MAX], *p;
+
+ if ((flags & O_SYMLINK) != O_SYMLINK)
+ return realpath( path, NULL );
+
+ strcpy( tmp, path );
+ p = dirname( tmp );
+ if (p != tmp ) strcpy( tmp, p );
+ realpath( tmp, resolved_path );
+ strcat( resolved_path, "/" );
+ strcpy( tmp, path );
+ p = basename( tmp );
+ if (p != tmp) strcpy( tmp, p );
+ strcat( resolved_path, tmp );
+ return strdup( resolved_path );
+}
+
/* open() wrapper that returns a struct fd with no fd user set */
struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_name,
int flags, mode_t *mode, unsigned int access,
@@ -1948,6 +2055,15 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
}
else rw_mode = O_RDONLY;
+ if ((path = dup_fd_name( root, name )))
+ {
+#if defined(O_SYMLINK)
+ if (check_symlink( path ) && (options & FILE_OPEN_REPARSE_POINT) && !(flags & O_CREAT))
+ flags |= O_SYMLINK;
+#endif
+ free( path );
+ }
+
if ((fd->unix_fd = open( name, rw_mode | (flags & ~O_TRUNC), *mode )) == -1)
{
/* if we tried to open a directory for write access, retry read-only */
@@ -1972,7 +2088,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
fd->unix_name = NULL;
if ((path = dup_fd_name( root, name )))
{
- fd->unix_name = realpath( path, NULL );
+ fd->unix_name = normalize_path( path, flags );
free( path );
}
@@ -1983,10 +2099,11 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
*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)
{
@@ -2001,13 +2118,17 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
list_add_head( &inode->open, &fd->inode_entry );
closed_fd = NULL;
+ is_dir = S_ISDIR(st.st_mode);
+ if (is_link)
+ is_reparse_dir(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;
@@ -2439,6 +2560,7 @@ static struct fd *get_handle_fd_obj( struct process *process, obj_handle_t handl
static int is_dir_empty( int fd )
{
+ int dir_fd;
DIR *dir;
int empty;
struct dirent *de;
@@ -2446,8 +2568,13 @@ static int is_dir_empty( int fd )
if ((fd = dup( fd )) == -1)
return -1;
- if (!(dir = fdopendir( fd )))
+ /* use openat() so that if 'fd' was opened with O_SYMLINK we can still check the contents */
+ dir_fd = openat( fd, ".", O_RDONLY | O_DIRECTORY | O_NONBLOCK );
+ if (dir_fd == -1)
+ return -1;
+ if (!(dir = fdopendir( dir_fd )))
{
+ close( dir_fd );
close( fd );
return -1;
}
@@ -2459,6 +2586,7 @@ static int is_dir_empty( int fd )
empty = 0;
}
closedir( dir );
+ close( dir_fd );
return empty;
}
@@ -2497,7 +2625,7 @@ static void set_fd_disposition( struct fd *fd, int unlink )
file_set_error();
return;
}
- if (S_ISREG( st.st_mode )) /* can't unlink files we don't have permission to write */
+ if (S_ISREG( st.st_mode ) || S_ISLNK( st.st_mode )) /* can't unlink files we don't have permission to write */
{
if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
{
--
2.35.1

View File

@ -0,0 +1,85 @@
From e5d8dc8bdb7f22a5d1a91b8599ace874c5e2316f Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Sat, 12 Dec 2020 17:35:21 -0700
Subject: [PATCH] kernelbase: Add support for deleting reparse points with
RemoveDirectory.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/kernelbase/file.c | 2 +-
dlls/ntdll/tests/file.c | 28 +++++++++++++++++++++++++++-
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 2ab399ec8e2..4f4040dd098 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -3493,7 +3493,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH RemoveDirectoryW( LPCWSTR path )
InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL );
status = NtOpenFile( &handle, DELETE | SYNCHRONIZE, &attr, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
+ FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
RtlFreeUnicodeString( &nt_name );
if (!status)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 7fa50ec59a6..fa8083b1c3b 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5363,11 +5363,11 @@ static void test_reparse_points(void)
FILE_BASIC_INFORMATION old_attrib, new_attrib;
static const WCHAR fooW[] = {'f','o','o',0};
static WCHAR volW[] = {'c',':','\\',0};
+ WCHAR *dest, *long_path, *abs_target;
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags;
- WCHAR *dest, *long_path;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE handle;
@@ -5454,6 +5454,7 @@ static void test_reparse_points(void)
/* use a sane (not obscenely long) target for the rest of testing */
pRtlFreeUnicodeString(&nameW);
pRtlDosPathNameToNtPathName_U(target_path, &nameW, NULL, NULL);
+ abs_target = nameW.Buffer;
/* Create the junction point */
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
@@ -5508,6 +5509,31 @@ 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);
+ 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(abs_target, &buffer);
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create junction point! (0x%lx)\n", GetLastError());
+ CloseHandle(handle);
+ bret = RemoveDirectoryW(reparse_path);
+ ok(bret, "Failed to delete junction point as directory!\n");
+ dwret = GetFileAttributesW(reparse_path);
+ ok(dwret == (DWORD)~0, "Junction point still exists (attributes: 0x%lx)!\n", dwret);
+
+ /* Check deleting a junction point as if it were a file */
+ HeapFree(GetProcessHeap(), 0, buffer);
+ bret = CreateDirectoryW(reparse_path, NULL);
+ 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(abs_target, &buffer);
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create junction point! (0x%lx)\n", GetLastError());
+ CloseHandle(handle);
+ /* TODO: use DeleteFile on reparse point */
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
--
2.35.1

View File

@ -0,0 +1,58 @@
From 6ba3c723cbc8c5a2f3aea407ac62a005bab9b015 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Sat, 6 Feb 2021 12:52:51 -0700
Subject: [PATCH] kernelbase: Add support for deleting reparse points with
DeleteFile.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/kernelbase/file.c | 3 ++-
dlls/ntdll/tests/file.c | 11 +++++++++--
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 4f4040dd098..5c9f0ebd485 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -993,7 +993,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileW( LPCWSTR path )
status = NtCreateFile(&hFile, SYNCHRONIZE | DELETE, &attr, &io, NULL, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0);
+ FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT,
+ NULL, 0);
if (status == STATUS_SUCCESS) status = NtClose(hFile);
RtlFreeUnicodeString( &nameW );
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index fa8083b1c3b..4d4b861e9fb 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5367,7 +5367,7 @@ static void test_reparse_points(void)
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
- DWORD dwret, dwLen, dwFlags;
+ DWORD dwret, dwLen, dwFlags, err;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
HANDLE handle;
@@ -5532,7 +5532,14 @@ 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%lx)\n", GetLastError());
CloseHandle(handle);
- /* TODO: use DeleteFile on reparse point */
+ 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%lx)!\n",
+ ERROR_ACCESS_DENIED, err);
+ dwret = GetFileAttributesW(reparse_path);
+ ok(dwret != (DWORD)~0, "Junction point doesn't exist (attributes: 0x%lx)!\n", dwret);
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: 0x%lx)\n", dwret);
cleanup:
/* Cleanup */
--
2.35.1

View File

@ -1,313 +0,0 @@
From 0d11bb3b7baf33f2b86f6e62e7a062bd22699895 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/tests/file.c | 117 ++++++++++++++++++++++++++++++++++------
dlls/ntdll/unix/file.c | 49 ++++++++++++-----
include/ntifs.h | 10 ++++
3 files changed, 146 insertions(+), 30 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 084398ba83d..81cc2ab5dab 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5323,26 +5323,50 @@ static void test_mailslot_name(void)
CloseHandle( device );
}
-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;
@@ -5362,10 +5386,12 @@ static void test_reparse_points(void)
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags, err;
+ 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 */
@@ -5417,7 +5443,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());
@@ -5464,7 +5490,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);
@@ -5479,7 +5505,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);
@@ -5492,14 +5518,73 @@ 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);
HeapFree(GetProcessHeap(), 0, buffer);
- bret = RemoveDirectoryW(reparse_path);
- ok(bret, "Failed to remove temporary reparse point directory!\n");
- bret = RemoveDirectoryW(target_path);
- ok(bret, "Failed to remove temporary target directory!\n");
+ RemoveDirectoryW(reparse_path);
+ DeleteFileW(reparse_path);
+ RemoveDirectoryW(target_path);
+ DeleteFileW(target_path);
RemoveDirectoryW(path);
}
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 79cf0f2dbca..4605d82ec81 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -5971,18 +5971,35 @@ static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer,
NTSTATUS create_reparse_point(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], *d;
SIZE_T unix_dest_len = PATH_MAX;
char *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:
+ FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
return status;
@@ -6017,6 +6034,20 @@ NTSTATUS create_reparse_point(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)
+ {
+ BOOL is_dir;
+ if (fstat( dest_fd, &st ) == -1)
+ {
+ status = errno_to_status( errno );
+ goto cleanup;
+ }
+ is_dir = S_ISDIR(st.st_mode);
+ if (is_dir)
+ strcat( magic_dest, "." );
+ strcat( magic_dest, "/" );
+ }
strcat( magic_dest, unix_dest );
/* Produce the link in a temporary location in the same folder */
@@ -6385,17 +6416,7 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
case FSCTL_SET_REPARSE_POINT:
{
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)in_buffer;
-
- switch(buffer->ReparseTag)
- {
- case IO_REPARSE_TAG_MOUNT_POINT:
- status = create_reparse_point( handle, buffer );
- break;
- default:
- FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
- status = STATUS_NOT_IMPLEMENTED;
- break;
- }
+ status = create_reparse_point( handle, buffer );
break;
}
diff --git a/include/ntifs.h b/include/ntifs.h
index 4539b89d583..ab3273d3f81 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,102 +0,0 @@
From 71060418f320eb0c78073b1b035bc9d905b778c6 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/tests/file.c | 19 ++++++++++++++++++-
dlls/ntdll/unix/file.c | 26 ++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 81cc2ab5dab..bf9d9677412 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5569,7 +5569,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 */
@@ -5577,6 +5576,24 @@ 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));
+ path_len = buffer->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+ path_len += buffer->SymbolicLinkReparseBuffer.PrintNameLength/sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len+1])
+ - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
+ ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
+ buffer->ReparseDataLength, total_len);
+ CloseHandle(handle);
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 4605d82ec81..00e92e83887 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6121,6 +6121,7 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
int unix_dest_len;
DWORD max_length;
NTSTATUS status;
+ ULONG flags = 0;
WCHAR *nt_dest;
ssize_t ret;
char *p;
@@ -6164,6 +6165,17 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
}
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_len -= (p - unix_dest);
memmove(unix_dest, p, unix_dest_len);
unix_dest[unix_dest_len] = 0;
@@ -6201,6 +6213,20 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
path_len += (nt_dest_len - prefix_len*sizeof(WCHAR)) + sizeof(WCHAR);
total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len/sizeof(WCHAR)]);
break;
+ case IO_REPARSE_TAG_SYMLINK:
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), SymbolicLinkReparseBuffer.PathBuffer[1]);
+ path_len = 0;
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset = path_len;
+ buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = nt_dest_len;
+ path_len += nt_dest_len + sizeof(WCHAR);
+ subst_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ buffer->SymbolicLinkReparseBuffer.PrintNameOffset = path_len;
+ buffer->SymbolicLinkReparseBuffer.PrintNameLength = nt_dest_len - prefix_len*sizeof(WCHAR);
+ print_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
+ path_len += (nt_dest_len - prefix_len*sizeof(WCHAR)) + sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[path_len/sizeof(WCHAR)]);
+ buffer->SymbolicLinkReparseBuffer.Flags = flags;
+ break;
default:
/* unrecognized (regular) files should probably be treated as symlinks */
WARN("unrecognized symbolic link\n");
--
2.17.1

View File

@ -0,0 +1,358 @@
From c6e4a8f7377c21227044833bb6bf8a4fa4e8c8a5 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Sat, 6 Feb 2021 12:52:51 -0700
Subject: [PATCH] ntdll: Add tests for NT symlink reparse points.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 238 +++++++++++++++++++++++++++++++++++++---
include/ntifs.h | 12 ++
2 files changed, 236 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 4d4b861e9fb..f9e179edc1d 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5327,26 +5327,52 @@ static void test_mailslot_name(void)
CloseHandle( device );
}
-static INT build_reparse_buffer(const WCHAR *filename, 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;
WCHAR *subst_dest, *print_dest;
REPARSE_DATA_BUFFER *buffer;
- struct_size = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[0]);
- prefix_len = strlen("\\??\\");
+ 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 = (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;
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);
+ buffer->SymbolicLinkReparseBuffer.Flags = flags;
+ 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;
@@ -5368,9 +5394,12 @@ static void test_reparse_points(void)
static const WCHAR dotW[] = {'.',0};
REPARSE_DATA_BUFFER *buffer = NULL;
DWORD dwret, dwLen, dwFlags, err;
+ WCHAR buf[] = {0,0,0,0};
+ HANDLE handle, token;
IO_STATUS_BLOCK iosb;
UNICODE_STRING nameW;
- HANDLE handle;
+ TOKEN_PRIVILEGES tp;
+ LUID luid;
BOOL bret;
/* Create a temporary folder for the junction point tests */
@@ -5435,7 +5464,7 @@ static void test_reparse_points(void)
win_skip("Failed to open junction point directory handle (0x%lx).\n", GetLastError());
goto cleanup;
}
- buffer_len = build_reparse_buffer(long_path, &buffer);
+ buffer_len = build_reparse_buffer(long_path, IO_REPARSE_TAG_MOUNT_POINT, 0, &buffer);
bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
ok(!bret && GetLastError()==ERROR_INVALID_REPARSE_DATA, "Unexpected error (0x%lx)\n", GetLastError());
HeapFree(GetProcessHeap(), 0, buffer);
@@ -5466,7 +5495,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%lx).\n", dwret);
- buffer_len = build_reparse_buffer(long_path, &buffer);
+ buffer_len = build_reparse_buffer(long_path, 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%lx)\n", GetLastError());
@@ -5513,7 +5542,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(abs_target, &buffer);
+ buffer_len = build_reparse_buffer(abs_target, 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%lx)\n", GetLastError());
CloseHandle(handle);
@@ -5528,7 +5557,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(abs_target, &buffer);
+ buffer_len = build_reparse_buffer(abs_target, 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%lx)\n", GetLastError());
CloseHandle(handle);
@@ -5541,6 +5570,187 @@ static void test_reparse_points(void)
ok(dwret != (DWORD)~0, "Junction point doesn't exist (attributes: 0x%lx)!\n", dwret);
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: 0x%lx)\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%lx != 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: %lx\n", GetLastError());
+ bret = LookupPrivilegeValueA(NULL, "SeCreateSymbolicLinkPrivilege", &luid);
+ todo_wine ok(bret || broken(!bret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE) /* winxp */,
+ "LookupPrivilegeValue failed: %lx\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: %lx\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 = RemoveDirectoryW(target_path);
+ ok(bret, "Failed to delete junction point target!\n");
+ handle = CreateFileW(target_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to create symlink target file.\n");
+ bret = WriteFile(handle, fooW, sizeof(fooW), &dwLen, NULL);
+ ok(bret, "Failed to write data to the symlink target file.\n");
+ ok(GetFileSize(handle, NULL) == sizeof(fooW), "target size is incorrect (%ld vs %d)\n",
+ GetFileSize(handle, NULL), (int)sizeof(fooW));
+ CloseHandle(handle);
+
+ /* 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);
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to create symlink file.\n");
+ dwret = NtQueryInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
+ ok(dwret == STATUS_SUCCESS, "Failed to get symlink file's attributes (0x%lx).\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%lx)\n", GetLastError());
+ CloseHandle(handle);
+
+ /* Check the size/data of the symlink target when opened with FILE_FLAG_OPEN_REPARSE_POINT */
+ handle = CreateFileW(target_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 file handle (0x%lx).\n", GetLastError());
+ goto cleanup;
+ }
+ ok(GetFileSize(handle, NULL) == sizeof(fooW), "symlink target size does not match (%ld != %d)\n",
+ GetFileSize(handle, NULL), (int)sizeof(fooW));
+ bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
+ ok(bret, "Failed to read data from the symlink target.\n");
+ ok(dwLen == sizeof(fooW), "Length of symlink target data does not match (%ld != %d).\n",
+ dwLen, (int)sizeof(fooW));
+ ok(!memcmp(fooW, &buf, sizeof(fooW)), "Symlink target data does not match (%s != %s).\n",
+ wine_dbgstr_wn(buf, dwLen), wine_dbgstr_w(fooW));
+ CloseHandle(handle);
+
+ /* Check deleting a file symlink as if it were a directory */
+ bret = RemoveDirectoryW(reparse_path);
+ ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
+ err = GetLastError();
+ ok(err == ERROR_DIRECTORY,
+ "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%lx)!\n",
+ ERROR_DIRECTORY, err);
+ dwret = GetFileAttributesW(reparse_path);
+ ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%lx)!\n", dwret);
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%lx)\n", dwret);
+
+ /* Delete the symlink as a file */
+ bret = DeleteFileW(reparse_path);
+ 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);
+ ok(dwret != (DWORD)~0, "Path doesn't exist (attributes: 0x%lx)!\n", dwret);
+ ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %lx)\n", dwret);
+ bret = DeleteFileW(target_path);
+ ok(bret, "Failed to delete symlink target!\n");
+ bret = CreateDirectoryW(target_path, NULL);
+ ok(bret, "Failed to create symlink target directory.\n");
+
+ /* 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%lx).\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%lx).\n", dwret);
+ buffer_len = build_reparse_buffer(abs_target, 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%lx)\n", GetLastError());
+
+ /* Check the file attributes of the symlink */
+ dwret = GetFileAttributesW(reparse_path);
+ ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%lx)!\n", dwret);
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: %lx)\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));
+ path_len = buffer->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+ path_len += buffer->SymbolicLinkReparseBuffer.PrintNameLength/sizeof(WCHAR);
+ total_len = FIELD_OFFSET(typeof(*buffer), SymbolicLinkReparseBuffer.PathBuffer[path_len+1])
+ - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
+ ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
+ buffer->ReparseDataLength, total_len);
+
+ /* 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%lx).\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%lx)\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%lx).\n", dwret);
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
+ "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%lx).\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%lx).\n", dwret);
+ buffer_len = build_reparse_buffer(rel_target, IO_REPARSE_TAG_SYMLINK, SYMLINK_FLAG_RELATIVE, &buffer);
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
+ ok(bret, "Failed to create symlink! (0x%lx)\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;
+ ok(string_len != lstrlenW(&targetW[1]), "Symlink destination length does not match ('%d' != '%d')!\n",
+ string_len, lstrlenW(&targetW[1]));
+ dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ ok((memcmp(dest, &targetW[1], string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
+ wine_dbgstr_w(dest), wine_dbgstr_w(&targetW[1]));
+ CloseHandle(handle);
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
diff --git a/include/ntifs.h b/include/ntifs.h
index 4539b89d583..0d02225bc4f 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;
@@ -51,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.35.1

View File

@ -0,0 +1,149 @@
From edaf25f8037c96fe83885ab47a1195c7f28e2fd6 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Sat, 6 Feb 2021 12:46:30 -0700
Subject: [PATCH] kernelbase: Add support for moving reparse points with
MoveFile*.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/kernelbase/file.c | 2 +-
dlls/ntdll/tests/file.c | 13 ++++++++++++-
dlls/ntdll/unix/file.c | 20 ++++++++++++++++++++
server/fd.c | 6 ++++--
4 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 5c9f0ebd485..2cbe8f4ac03 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -2471,7 +2471,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH MoveFileWithProgressW( const WCHAR *source, const
status = NtOpenFile( &source_handle, DELETE | SYNCHRONIZE, &attr, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- FILE_SYNCHRONOUS_IO_NONALERT );
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT );
RtlFreeUnicodeString( &nt_name );
if (!set_ntstatus( status )) goto error;
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index f9e179edc1d..6ccdb3c02dc 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5381,7 +5381,8 @@ static INT build_reparse_buffer(const WCHAR *filename, ULONG tag, ULONG flags,
static void test_reparse_points(void)
{
- WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH], volnameW[MAX_PATH];
+ WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH], volnameW[MAX_PATH], new_path[MAX_PATH];
+ static const WCHAR new_reparseW[] = {'\\','n','e','w','_','r','e','p','a','r','s','e',0};
static const WCHAR reparseW[] = {'\\','r','e','p','a','r','s','e',0};
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
static const WCHAR parentW[] = {'\\','.','.','\\',0};
@@ -5389,6 +5390,7 @@ static void test_reparse_points(void)
FILE_BASIC_INFORMATION old_attrib, new_attrib;
static const WCHAR fooW[] = {'f','o','o',0};
static WCHAR volW[] = {'c',':','\\',0};
+ const WCHAR *rel_target = &targetW[1];
WCHAR *dest, *long_path, *abs_target;
REPARSE_GUID_DATA_BUFFER guid_buffer;
static const WCHAR dotW[] = {'.',0};
@@ -5751,6 +5753,15 @@ static void test_reparse_points(void)
wine_dbgstr_w(dest), wine_dbgstr_w(&targetW[1]));
CloseHandle(handle);
+ /* Check moving a reparse point to another location */
+ lstrcpyW(new_path, path);
+ lstrcatW(new_path, parentW);
+ lstrcatW(new_path, new_reparseW);
+ bret = MoveFileW(reparse_path, new_path);
+ ok(bret, "Failed to move and rename reparse point.\n");
+ bret = MoveFileW(new_path, reparse_path);
+ ok(bret, "Failed to move and rename reparse point.\n");
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 201eeab7013..907d51f1fc3 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -5336,8 +5336,10 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
if (len >= sizeof(FILE_RENAME_INFORMATION))
{
FILE_RENAME_INFORMATION *info = ptr;
+ REPARSE_DATA_BUFFER *buffer = NULL;
UNICODE_STRING name_str, redir;
OBJECT_ATTRIBUTES attr;
+ ULONG buffer_len = 0;
char *unix_name;
name_str.Buffer = info->FileName;
@@ -5346,6 +5348,19 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
InitializeObjectAttributes( &attr, &name_str, OBJ_CASE_INSENSITIVE, info->RootDirectory, NULL );
get_redirect( &attr, &redir );
+ /* obtain all the data from the reparse point (if applicable) */
+ status = get_reparse_point( handle, NULL, &buffer_len );
+ if (status == STATUS_BUFFER_TOO_SMALL)
+ {
+ buffer = malloc( buffer_len );
+ status = get_reparse_point( handle, buffer, &buffer_len );
+ if (status != STATUS_SUCCESS)
+ {
+ free( buffer );
+ break;
+ }
+ }
+
status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF );
if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
{
@@ -5362,9 +5377,14 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
}
SERVER_END_REQ;
+ /* rebuild reparse point in new location (if applicable) */
+ if (buffer && status == STATUS_SUCCESS)
+ status = create_reparse_point( handle, buffer );
+
free( unix_name );
}
free( redir.Buffer );
+ free( buffer );
}
else status = STATUS_INVALID_PARAMETER_3;
break;
diff --git a/server/fd.c b/server/fd.c
index 1ed975673a6..60ba552f769 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -2704,7 +2704,7 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
goto failed;
}
- if (!stat( name, &st ))
+ if (!lstat( name, &st ))
{
if (!fstat( fd->unix_fd, &st2 ) && st.st_ino == st2.st_ino && st.st_dev == st2.st_dev)
{
@@ -2720,7 +2720,7 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
}
/* can't replace directories or special files */
- if (!S_ISREG( st.st_mode ))
+ if (!S_ISREG( st.st_mode ) && !S_ISLNK( st.st_mode ))
{
set_error( STATUS_ACCESS_DENIED );
goto failed;
@@ -2778,6 +2778,8 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
fd->nt_name = dup_nt_name( root, nt_name, &fd->nt_namelen );
free( fd->unix_name );
fd->closed->unix_name = fd->unix_name = realpath( name, NULL );
+ if (!fd->unix_name)
+ fd->closed->unix_name = fd->unix_name = dup_fd_name( root, name ); /* dangling symlink */
free( name );
if (!fd->unix_name)
set_error( STATUS_NO_MEMORY );
--
2.35.1

View File

@ -1,53 +0,0 @@
From 5a87d55e7613e67c854f62f66b9cc5221bfb993d 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/tests/file.c | 16 ++++++++++++++++
dlls/ntdll/unix/file.c | 1 +
2 files changed, 17 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index bf9d9677412..3a6396e5afc 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5592,6 +5592,22 @@ static void test_reparse_points(void)
- FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
ok(buffer->ReparseDataLength == total_len, "ReparseDataLength has unexpected value (%d != %d)\n",
buffer->ReparseDataLength, total_len);
+
+ /* 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:
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 00e92e83887..5ed116d94d9 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6422,6 +6422,7 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
switch(buffer->ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
+ case IO_REPARSE_TAG_SYMLINK:
status = remove_reparse_point( handle, buffer );
break;
default:
--
2.17.1

View File

@ -0,0 +1,30 @@
From 1af9d5003cb6d65be02fb68779e208488c242b82 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Sat, 3 Sep 2022 08:57:05 -0600
Subject: [PATCH] kernelbase: Add test for reparse point copy behavior.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/ntdll/tests/file.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 6ccdb3c02dc..b1290f9ef7b 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5762,6 +5762,12 @@ static void test_reparse_points(void)
bret = MoveFileW(new_path, reparse_path);
ok(bret, "Failed to move and rename reparse point.\n");
+ /* Check copying a reparse point to another location */
+ lstrcpyW(new_path, path);
+ lstrcatW(new_path, new_reparseW);
+ bret = CopyFileW(reparse_path, new_path, TRUE);
+ ok(!bret, "Reparse points cannot be copied.\n");
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
--
2.35.1

View File

@ -1,294 +0,0 @@
From bb6e330f99c2ca104f312cb790ff15eb4af79f41 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/tests/file.c | 30 ++++++++++---
dlls/ntdll/unix/file.c | 94 +++++++++++++++++++++++++++++++++++++----
include/ntifs.h | 2 +
include/winternl.h | 1 +
4 files changed, 113 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 3a6396e5afc..1ace7f76c1f 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5323,7 +5323,8 @@ static void test_mailslot_name(void)
CloseHandle( device );
}
-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;
@@ -5341,7 +5342,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;
@@ -5361,6 +5362,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;
@@ -5443,7 +5445,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());
@@ -5490,7 +5492,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);
@@ -5505,7 +5507,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);
@@ -5567,7 +5569,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());
@@ -5610,6 +5612,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[1], 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/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 5ed116d94d9..35ae5ab854a 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -3335,7 +3335,7 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
for (ptr = name, end = name + name_len; ptr < end; ptr++)
{
if (*ptr == '\\') return STATUS_OBJECT_NAME_INVALID; /* duplicate backslash */
- if (*ptr == '.')
+ if (*ptr == '.' && disposition != FILE_WINE_PATH)
{
if (ptr + 1 == end) return STATUS_OBJECT_NAME_INVALID; /* "." element */
if (ptr[1] == '\\') return STATUS_OBJECT_NAME_INVALID; /* "." element */
@@ -5970,17 +5970,20 @@ static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer,
*/
NTSTATUS create_reparse_point(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;
+ char *unix_src, *unix_dest, *unix_path = NULL;
char tmpdir[PATH_MAX], tmplink[PATH_MAX], *d;
SIZE_T unix_dest_len = PATH_MAX;
- char *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)
@@ -5989,11 +5992,13 @@ NTSTATUS create_reparse_point(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:
FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
@@ -6006,8 +6011,66 @@ NTSTATUS create_reparse_point(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)
+ {
+ ULONG nt_path_len = PATH_MAX, unix_path_len = PATH_MAX;
+ WCHAR *nt_path;
+
+ /* resolve the NT path of the source */
+ unix_path = malloc( strlen(unix_src) + 2 );
+ path_allocated = TRUE;
+ strcpy( unix_path, unix_src );
+ d = dirname( unix_path );
+ if (d != unix_path) strcpy( unix_path, d );
+ strcat( unix_path, "/");
+ for (;;)
+ {
+ nt_path = malloc( nt_path_len * sizeof(WCHAR) );
+ if (!nt_path)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ status = wine_unix_to_nt_file_name( unix_path, nt_path, &nt_path_len );
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
+ free( nt_path );
+ }
+ if (status != STATUS_SUCCESS)
+ goto cleanup;
+ free(unix_path);
+ /* re-resolve the unix path for the source */
+ for (;;)
+ {
+ UNICODE_STRING nt_path_tmp;
+ unix_path = malloc( unix_path_len );
+ if (!unix_path)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ nt_path_tmp.Buffer = nt_path;
+ nt_path_tmp.Length = wcslen(nt_path) * sizeof(WCHAR);
+ status = wine_nt_to_unix_file_name( &nt_path_tmp, unix_path, &unix_path_len, FALSE );
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
+ free( unix_path );
+ }
+ /* append the destination */
+ nt_dest.MaximumLength = dest_len + (wcslen( nt_path ) + 1) * sizeof(WCHAR);
+ nt_dest.Buffer = malloc( nt_dest.MaximumLength );
+ wcscpy( nt_dest.Buffer, nt_path );
+ free( nt_path );
+ memcpy( &nt_dest.Buffer[wcslen(nt_dest.Buffer)], dest, dest_len + sizeof(WCHAR));
+ nt_dest.Length = wcslen( nt_dest.Buffer ) * sizeof(WCHAR);
+ }
+ else
+ {
+ nt_dest.MaximumLength = (wcslen( dest ) + 1) * sizeof(WCHAR);
+ nt_dest.Buffer = malloc( nt_dest.MaximumLength );
+ wcscpy( nt_dest.Buffer, dest );
+ nt_dest.Length = dest_len;
+ }
+ nt_dest_allocated = TRUE;
+ /* resolve the NT path of the destination */
for (;;)
{
unix_dest = malloc( unix_dest_len );
@@ -6023,11 +6086,24 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
goto cleanup;
dest_allocated = TRUE;
+ /* check that the source and destination paths are the same up to the relative path */
+ if (flags == SYMLINK_FLAG_RELATIVE)
+ {
+ relative_offset = strlen(unix_path);
+ if (strncmp( unix_path, unix_dest, relative_offset ) != 0)
+ {
+ status = STATUS_IO_REPARSE_DATA_INVALID;
+ goto cleanup;
+ }
+ }
- TRACE( "Linking %s to %s\n", unix_src, unix_dest );
+ TRACE( "Linking %s to %s\n", unix_src, &unix_dest[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)
@@ -6048,7 +6124,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
strcat( magic_dest, "." );
strcat( magic_dest, "/" );
}
- strcat( magic_dest, unix_dest );
+ strcat( magic_dest, &unix_dest[relative_offset] );
/* Produce the link in a temporary location in the same folder */
strcpy( tmpdir, unix_src );
@@ -6098,7 +6174,9 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
cleanup:
if (tempdir_created) rmdir( tmpdir );
+ if (path_allocated) free( unix_path );
if (dest_allocated) free( unix_dest );
+ if (nt_dest_allocated) free( nt_dest.Buffer );
if (src_allocated) free( unix_src );
if (needs_close) close( dest_fd );
return status;
diff --git a/include/ntifs.h b/include/ntifs.h
index ab3273d3f81..0d02225bc4f 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 */
diff --git a/include/winternl.h b/include/winternl.h
index 24609e136bf..d7017f98db0 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -2842,6 +2842,7 @@ typedef struct _RTL_ATOM_TABLE
#define FILE_OVERWRITE 4
#define FILE_OVERWRITE_IF 5
#define FILE_MAXIMUM_DISPOSITION 5
+#define FILE_WINE_PATH 6
/* Characteristics of a File System */
#define FILE_REMOVABLE_MEDIA 0x00000001
--
2.17.1

View File

@ -1,111 +0,0 @@
From 7c1e6d56e5faa72f8adff738fc2f2e774ee1b754 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/tests/file.c | 15 ++++++++++++++-
dlls/ntdll/unix/file.c | 38 +++++++++++++++++++++++++++++++++-----
2 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 1ace7f76c1f..2f0903fe7f1 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5625,9 +5625,22 @@ 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[1], 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;
+ ok(string_len != lstrlenW(&targetW[1]), "Symlink destination length does not match ('%d' != '%d')!\n",
+ string_len, lstrlenW(&targetW[1]));
+ dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+ ok((memcmp(dest, &targetW[1], string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
+ wine_dbgstr_w(dest), wine_dbgstr_w(&targetW[1]));
+ CloseHandle(handle);
+
cleanup:
/* Cleanup */
pRtlFreeUnicodeString(&nameW);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index 35ae5ab854a..bf63985f777 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -6221,6 +6221,11 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
/* Decode the reparse tag from the symlink */
p = unix_dest;
+ if (*p == '.')
+ {
+ flags = SYMLINK_FLAG_RELATIVE;
+ p++;
+ }
if (*p++ != '/')
{
status = STATUS_NOT_IMPLEMENTED;
@@ -6258,24 +6263,47 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
memmove(unix_dest, p, unix_dest_len);
unix_dest[unix_dest_len] = 0;
- for (;;)
+ /* convert the relative path into an absolute path */
+ if (flags == SYMLINK_FLAG_RELATIVE)
{
+ int i;
+
+ nt_dest_len = unix_dest_len;
nt_dest = malloc( nt_dest_len * sizeof(WCHAR) );
if (!nt_dest)
{
status = STATUS_NO_MEMORY;
goto cleanup;
}
- status = wine_unix_to_nt_file_name( unix_dest, nt_dest, &nt_dest_len );
- if (status != STATUS_BUFFER_TOO_SMALL) break;
- free( nt_dest );
+ /* wine_unix_to_nt_file_name does not work on relative paths, so convert manually */
+ for (i = 0; i < unix_dest_len; i++)
+ {
+ if (unix_dest[i] == '/') unix_dest[i] = '\\';
+ }
+ ascii_to_unicode( nt_dest, unix_dest, unix_dest_len );
+ }
+ else
+ {
+ /* resolve the NT path */
+ for (;;)
+ {
+ nt_dest = malloc( nt_dest_len * sizeof(WCHAR) );
+ if (!nt_dest)
+ {
+ status = STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+ status = wine_unix_to_nt_file_name( unix_dest, nt_dest, &nt_dest_len );
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
+ free( nt_dest );
+ }
}
dest_allocated = TRUE;
if (status != STATUS_SUCCESS)
goto cleanup;
nt_dest_len *= sizeof(WCHAR);
- prefix_len = strlen("\\??\\");
+ prefix_len = (flags == SYMLINK_FLAG_RELATIVE) ? 0 : strlen("\\??\\");
switch(buffer->ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
--
2.17.1

Some files were not shown because too many files have changed in this diff Show More