mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
ntdll-Junction_Points: Updates from Erich E. Hoover.
Includes a fundamental rewrite of the way reparse points are stored.
This commit is contained in:
parent
533337e25e
commit
1ed196f0ef
@ -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
|
||||
|
||||
|
@ -0,0 +1,634 @@
|
||||
From 6209b270e6f2a7913a95f6c1da18c11e2e2a73a5 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 16 Jan 2014 20:56:49 -0700
|
||||
Subject: [PATCH] ntdll: Add support for creating reparse points.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
configure.ac | 2 +
|
||||
dlls/ntdll/Makefile.in | 2 +-
|
||||
dlls/ntdll/tests/file.c | 152 ++++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 300 ++++++++++++++++++++++++++++++++++++++++
|
||||
include/Makefile.in | 1 +
|
||||
include/ntifs.h | 42 ++++++
|
||||
6 files changed, 498 insertions(+), 1 deletion(-)
|
||||
create mode 100644 include/ntifs.h
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 3ea4c2afe0a..d0ec1d837c3 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -2030,6 +2030,8 @@ AC_CHECK_FUNCS(\
|
||||
prctl \
|
||||
proc_pidinfo \
|
||||
sched_yield \
|
||||
+ renameat \
|
||||
+ renameat2 \
|
||||
setproctitle \
|
||||
setprogname \
|
||||
sigprocmask \
|
||||
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
|
||||
index 3b1cdb54f9f..6eb4690f8e0 100644
|
||||
--- a/dlls/ntdll/Makefile.in
|
||||
+++ b/dlls/ntdll/Makefile.in
|
||||
@@ -4,7 +4,7 @@ UNIXLIB = ntdll.so
|
||||
IMPORTLIB = ntdll
|
||||
IMPORTS = winecrt0
|
||||
UNIX_CFLAGS = $(UNWIND_CFLAGS)
|
||||
-UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS)
|
||||
+UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS) -lm
|
||||
|
||||
EXTRADLLFLAGS = -nodefaultlibs -Wl,--image-base,0x7bc00000
|
||||
x86_64_EXTRADLLFLAGS = -nodefaultlibs -Wl,--image-base,0x170000000
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 4b142f241e5..5f6cb223951 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
|
||||
@@ -5326,6 +5327,156 @@ 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)
|
||||
+{
|
||||
+ WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH], volnameW[MAX_PATH];
|
||||
+ 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};
|
||||
+ 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;
|
||||
+ WCHAR *long_path;
|
||||
+ 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 );
|
||||
+ if (!GetVolumeNameForVolumeMountPointW(volW, volnameW, MAX_PATH))
|
||||
+ {
|
||||
+ win_skip("Failed to obtain volume name for current volume.\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ GetVolumeInformationW(volnameW, 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);
|
||||
+ for (int i=0; i<1; i++)
|
||||
+ {
|
||||
+ lstrcatW(target_path, parentW);
|
||||
+ lstrcatW(target_path, path);
|
||||
+ }
|
||||
+ lstrcatW(target_path, targetW);
|
||||
+ bret = CreateDirectoryW(target_path, NULL);
|
||||
+ ok(bret, "Failed to create junction point target directory.\n");
|
||||
+ pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL);
|
||||
+
|
||||
+ /* construct a too long pathname (resulting reparse buffer over 16 kiB limit) */
|
||||
+ long_path = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 32767);
|
||||
+ lstrcpyW(long_path, nameW.Buffer);
|
||||
+ for (int i=0; i<250; i++)
|
||||
+ {
|
||||
+ lstrcatW(long_path, parentW);
|
||||
+ lstrcatW(long_path, path);
|
||||
+ }
|
||||
+ lstrcatW(long_path, targetW);
|
||||
+
|
||||
+ /* 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%lx).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ 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 && GetLastError()==ERROR_INVALID_REPARSE_DATA, "Unexpected error (0x%lx)\n", GetLastError());
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+ /* construct a long pathname to demonstrate correct behavior with very large reparse points */
|
||||
+ pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL);
|
||||
+ lstrcpyW(long_path, nameW.Buffer);
|
||||
+ for (int i=0; i<200; i++)
|
||||
+ {
|
||||
+ lstrcatW(long_path, parentW);
|
||||
+ lstrcatW(long_path, path);
|
||||
+ }
|
||||
+ lstrcatW(long_path, targetW);
|
||||
+
|
||||
+ /* use a sane (not obscenely long) target for the rest of testing */
|
||||
+ pRtlFreeUnicodeString(&nameW);
|
||||
+ 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%lx).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ 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());
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+cleanup:
|
||||
+ /* Cleanup */
|
||||
+ pRtlFreeUnicodeString(&nameW);
|
||||
+ 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");
|
||||
+ bret = RemoveDirectoryW(target_path);
|
||||
+ ok(bret, "Failed to remove temporary target directory!\n");
|
||||
+ RemoveDirectoryW(path);
|
||||
+}
|
||||
+
|
||||
START_TEST(file)
|
||||
{
|
||||
HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
|
||||
@@ -5398,5 +5549,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 d103e3195b5..3fb4ded846c 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
+#include <math.h>
|
||||
+#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_MNTENT_H
|
||||
@@ -127,6 +129,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);
|
||||
@@ -138,6 +141,12 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
||||
#undef EXT2_IOC_GETFLAGS
|
||||
#undef EXT4_CASEFOLD_FL
|
||||
|
||||
+#ifndef RENAME_EXCHANGE
|
||||
+#define RENAME_EXCHANGE (1 << 1)
|
||||
+#endif
|
||||
+
|
||||
+#define SYM_MAX (PATH_MAX-1) /* PATH_MAX includes the NUL character */
|
||||
+
|
||||
#ifdef linux
|
||||
|
||||
/* We want the real kernel dirent structure, not the libc one */
|
||||
@@ -239,6 +248,95 @@ static const BOOL is_case_sensitive = FALSE;
|
||||
static pthread_mutex_t dir_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t mnt_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
+#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 */
|
||||
+
|
||||
+static char *itoa( int i )
|
||||
+{
|
||||
+ static char buffer[11];
|
||||
+
|
||||
+ snprintf(buffer, sizeof(buffer), "%d", i);
|
||||
+ return buffer;
|
||||
+}
|
||||
+
|
||||
+/* base64url (RFC 4648 §5) encode a 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 UINT encode_base64url( const char *bin, unsigned int len, char *base64 )
|
||||
+{
|
||||
+ UINT n = 0, x;
|
||||
+ static const char base64enc[] =
|
||||
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
+
|
||||
+ while (len > 0)
|
||||
+ {
|
||||
+ /* first 6 bits, all from bin[0] */
|
||||
+ base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
|
||||
+ x = (bin[0] & 3) << 4;
|
||||
+
|
||||
+ /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
|
||||
+ if (len == 1)
|
||||
+ {
|
||||
+ base64[n++] = base64enc[x];
|
||||
+ break;
|
||||
+ }
|
||||
+ base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
|
||||
+ x = (bin[1] & 0x0f) << 2;
|
||||
+
|
||||
+ /* next 6 bits 4 from bin[1] and 2 from bin[2] */
|
||||
+ if (len == 2)
|
||||
+ {
|
||||
+ base64[n++] = base64enc[x];
|
||||
+ break;
|
||||
+ }
|
||||
+ base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
|
||||
+
|
||||
+ /* last 6 bits, all from bin [2] */
|
||||
+ base64[n++] = base64enc[bin[2] & 0x3f];
|
||||
+ bin += 3;
|
||||
+ len -= 3;
|
||||
+ }
|
||||
+ base64[n] = 0;
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+/* create a directory and all the needed parent directories */
|
||||
+static int mkdir_p(const char *path, mode_t mode)
|
||||
+{
|
||||
+ char path_tmp[PATH_MAX], *p;
|
||||
+
|
||||
+ strcpy( path_tmp, path );
|
||||
+ for (p = path_tmp + 1; *p; p++) {
|
||||
+ if (*p == '/') {
|
||||
+ *p = '\0';
|
||||
+ if (mkdir(path_tmp, mode) != 0 && errno != EEXIST)
|
||||
+ return -1;
|
||||
+ *p = '/';
|
||||
+ }
|
||||
+ }
|
||||
+ if (mkdir(path_tmp, mode) != 0 && errno != EEXIST)
|
||||
+ return -1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* check if a given Unicode char is OK in a DOS short name */
|
||||
static inline BOOL is_invalid_dos_char( WCHAR ch )
|
||||
{
|
||||
@@ -1574,6 +1672,28 @@ static inline ULONG get_file_attributes( const struct stat *st )
|
||||
}
|
||||
|
||||
|
||||
+/* determine whether a reparse point is meant to be a directory or a file */
|
||||
+static int is_reparse_dir( int fd, const char *path, BOOL *is_dir )
|
||||
+{
|
||||
+ char link_path[PATH_MAX], *p;
|
||||
+ int ret;
|
||||
+
|
||||
+ if ((ret = readlinkat( fd, 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 BOOL fd_is_mount_point( int fd, const struct stat *st )
|
||||
{
|
||||
struct stat parent;
|
||||
@@ -3313,6 +3433,179 @@ done:
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * 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)
|
||||
+{
|
||||
+ int buffer_len = buffer->ReparseDataLength+FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
|
||||
+ char target_path[PATH_MAX], link_path[PATH_MAX], link_dir[PATH_MAX], original_dir[PATH_MAX];
|
||||
+ int encoded_len = (int)ceil(buffer_len*4/3.0) + 1, chunk_len;
|
||||
+ char tmpdir[PATH_MAX], tmplink[PATH_MAX], *d;
|
||||
+ BOOL needs_close, tempdir_created = FALSE;
|
||||
+ char filename_buf[PATH_MAX], *filename;
|
||||
+ char *unix_src = NULL, *encoded = NULL;
|
||||
+ int i = 0, j = 0, depth = 0, fd;
|
||||
+ NTSTATUS status;
|
||||
+ struct stat st;
|
||||
+ BOOL is_dir;
|
||||
+
|
||||
+ if (buffer_len > 16*1024)
|
||||
+ return STATUS_IO_REPARSE_DATA_INVALID;
|
||||
+
|
||||
+ if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &fd, &needs_close, NULL, NULL )))
|
||||
+ return status;
|
||||
+ if (fstat( fd, &st ) == -1)
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if ((status = server_get_unix_name( handle, &unix_src )))
|
||||
+ goto cleanup;
|
||||
+ is_dir = S_ISDIR( st.st_mode );
|
||||
+ is_reparse_dir( AT_FDCWD, unix_src, &is_dir ); /* keep type (replace existing reparse point) */
|
||||
+ encoded = malloc( encoded_len );
|
||||
+ if (!encoded)
|
||||
+ {
|
||||
+ status = STATUS_NO_MEMORY;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ encoded_len = encode_base64url( (const char *)buffer, buffer_len, encoded );
|
||||
+
|
||||
+ TRACE( "Linking %s to %s\n", unix_src, encoded );
|
||||
+ strcpy( filename_buf, unix_src );
|
||||
+ filename = basename( filename_buf );
|
||||
+
|
||||
+ /* Create the symlink that represents the initial data in the reparse tag:
|
||||
+ * *) Begin all reparse tags with the hidden folder .REPARSE_POINT. This serves two purposes:
|
||||
+ * 1) it makes it easy to identify reparse points
|
||||
+ * 2) if the reparse buffer exceeds what can be stored in a single symlink (4095+1 bytes)
|
||||
+ * then we need to store additional data, so link to it and store it in a hidden folder
|
||||
+ * *) Append the filename of the reparse point to the hidden folder, this ensures that if
|
||||
+ * multiple reparse points contain the same data that there is no possibility of collision
|
||||
+ * *) Append a special flag to indicate whether this is a directory (./) or file (/)
|
||||
+ * *) Append the base64-url encoded reparse point buffer
|
||||
+ * *) Append the filename of the first continuing symlink (0) in case we need it
|
||||
+ */
|
||||
+ strcpy( target_path, ".REPARSE_POINT/" );
|
||||
+ strcat( target_path, filename );
|
||||
+ strcat( target_path, "/" );
|
||||
+ if (is_dir)
|
||||
+ strcat( target_path, "." );
|
||||
+ strcat( target_path, "/" );
|
||||
+ i = 0;
|
||||
+ for (depth=0; i<encoded_len && strlen(target_path)<SYM_MAX-2; i+=chunk_len, depth++)
|
||||
+ {
|
||||
+ chunk_len = min(NAME_MAX, SYM_MAX-2-strlen(target_path));
|
||||
+ strncat( target_path, &encoded[i], chunk_len );
|
||||
+ strcat( target_path, "/" );
|
||||
+ }
|
||||
+ strcat( target_path, itoa(j) );
|
||||
+
|
||||
+ /* 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( target_path, tmplink ))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* change to the link folder so that we can build any necessary additional data */
|
||||
+ getcwd( original_dir, PATH_MAX );
|
||||
+ strcpy( link_dir, tmpdir );
|
||||
+ link_dir[strlen(link_dir)-16] = 0;
|
||||
+ chdir( link_dir );
|
||||
+
|
||||
+ /* If there is any further information in the reparse tag then store it in the hidden folder */
|
||||
+ while(i < encoded_len)
|
||||
+ {
|
||||
+ j++;
|
||||
+ strcpy( link_path, target_path );
|
||||
+
|
||||
+ target_path[0] = 0;
|
||||
+ for (; depth>0; depth--)
|
||||
+ {
|
||||
+ strcat( target_path, "../" );
|
||||
+ }
|
||||
+ for (depth=0; i<encoded_len && strlen(target_path)<SYM_MAX-2; i+=chunk_len, depth++)
|
||||
+ {
|
||||
+ chunk_len = min(NAME_MAX, SYM_MAX-2-strlen(target_path));
|
||||
+ strncat( target_path, &encoded[i], chunk_len );
|
||||
+ strcat( target_path, "/" );
|
||||
+ }
|
||||
+ strcat( target_path, itoa(j) );
|
||||
+
|
||||
+ strcpy( link_dir, link_path );
|
||||
+ link_dir[strlen(link_dir)-1] = 0;
|
||||
+ if (mkdir_p( link_dir, 0777))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (symlink( target_path, link_path ))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ chdir( link_dir );
|
||||
+ }
|
||||
+
|
||||
+ /* revert to the original directory */
|
||||
+ chdir( original_dir );
|
||||
+
|
||||
+ /* Atomically move the initial link into position */
|
||||
+ if (!renameat2( -1, tmplink, -1, unix_src, RENAME_EXCHANGE ))
|
||||
+ {
|
||||
+ /* success: link and folder/file have switched locations */
|
||||
+ if (S_ISDIR( st.st_mode ))
|
||||
+ rmdir( tmplink ); /* remove the folder (at link location) */
|
||||
+ else
|
||||
+ unlink( tmplink ); /* remove the file (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 (needs_close) close( fd );
|
||||
+ free( unix_src );
|
||||
+ free( encoded );
|
||||
+
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/******************************************************************************
|
||||
* lookup_unix_name
|
||||
*
|
||||
@@ -6060,6 +6353,13 @@ 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;
|
||||
+ status = create_reparse_point( handle, buffer );
|
||||
+ 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 70134a7bcb7..bdddf5426fe 100644
|
||||
--- a/include/Makefile.in
|
||||
+++ b/include/Makefile.in
|
||||
@@ -563,6 +563,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.35.1
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,240 @@
|
||||
From ff29165847ca6ea105053843f46cac727c7083c8 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 3 Sep 2022 11:23:31 -0600
|
||||
Subject: [PATCH] ntdll: Follow reparse points during path resolution.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 11 +++-
|
||||
dlls/ntdll/unix/file.c | 122 +++++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 124 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index b1290f9ef7b..799a8cebe50 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5381,7 +5381,7 @@ 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], new_path[MAX_PATH];
|
||||
+ WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH], volnameW[MAX_PATH], new_path[MAX_PATH], thru_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};
|
||||
@@ -5768,11 +5768,20 @@ static void test_reparse_points(void)
|
||||
bret = CopyFileW(reparse_path, new_path, TRUE);
|
||||
ok(!bret, "Reparse points cannot be copied.\n");
|
||||
|
||||
+ /* Create a file on the other side of a reparse point */
|
||||
+ lstrcpyW(thru_path, reparse_path);
|
||||
+ lstrcatW(thru_path, new_reparseW);
|
||||
+ handle = CreateFileW(thru_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
|
||||
+ ok( handle != INVALID_HANDLE_VALUE, "Failed to create file on other side of reparse point: %lx.\n", GetLastError() );
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
pRtlFreeUnicodeString(&nameW);
|
||||
HeapFree(GetProcessHeap(), 0, long_path);
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ bret = DeleteFileW(thru_path);
|
||||
+ ok(bret, "Failed to delete file on other side of junction point!\n");
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
ok(bret, "Failed to remove temporary reparse point directory!\n");
|
||||
bret = RemoveDirectoryW(target_path);
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 907d51f1fc3..d520a73b253 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3700,14 +3700,13 @@ 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.
|
||||
+ * Obtain the reparse point buffer from the unix filename for the reparse point.
|
||||
*/
|
||||
-NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *size)
|
||||
+NTSTATUS get_reparse_point_unix(const char *unix_src, 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];
|
||||
+ char link_path[PATH_MAX];
|
||||
REPARSE_DATA_BUFFER header;
|
||||
ULONG out_size = *size;
|
||||
char *encoded = NULL;
|
||||
@@ -3716,9 +3715,6 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
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)
|
||||
{
|
||||
@@ -3822,6 +3818,75 @@ 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_name = NULL;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ if ((status = server_get_unix_name( handle, &unix_name )))
|
||||
+ return status;
|
||||
+ status = get_reparse_point_unix( unix_name, buffer, size );
|
||||
+ free( unix_name );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* find the NT target of a reparse point */
|
||||
+static NTSTATUS find_reparse_target( const char *unix_name, WCHAR **new_name, int *new_name_len)
|
||||
+{
|
||||
+ REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
+ int offset, target_len;
|
||||
+ ULONG buffer_len = 0;
|
||||
+ NTSTATUS status;
|
||||
+ WCHAR *target;
|
||||
+
|
||||
+ status = get_reparse_point_unix( unix_name, NULL, &buffer_len );
|
||||
+ if (status != STATUS_BUFFER_TOO_SMALL)
|
||||
+ return status;
|
||||
+
|
||||
+ buffer = malloc( buffer_len );
|
||||
+ if (!buffer)
|
||||
+ return STATUS_NO_MEMORY;
|
||||
+ if ((status = get_reparse_point_unix( unix_name, buffer, &buffer_len )) != STATUS_SUCCESS)
|
||||
+ {
|
||||
+ free( buffer );
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ switch( buffer->ReparseTag )
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ offset = buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
|
||||
+ target = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
+ target_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
+ break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
|
||||
+ target = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
+ target_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
+ break;
|
||||
+ default:
|
||||
+ WARN( "Reparse point with unsupported tag: 0x%08x\n", buffer->ReparseTag );
|
||||
+ status = STATUS_NOT_IMPLEMENTED;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (status == STATUS_SUCCESS)
|
||||
+ {
|
||||
+ *new_name_len = target_len/sizeof(WCHAR);
|
||||
+ *new_name = malloc( target_len );
|
||||
+ if (*new_name) memcpy( *new_name, target, target_len );
|
||||
+ }
|
||||
+
|
||||
+ free( buffer );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* Retrieve the unix name corresponding to a file handle, remove that symlink, and then recreate
|
||||
* a directory at the location of the old filename.
|
||||
@@ -3924,11 +3989,13 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
UINT disposition, BOOL is_unix )
|
||||
{
|
||||
static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, '/', 0 };
|
||||
+ WCHAR *name_buf = NULL;
|
||||
NTSTATUS status;
|
||||
int ret;
|
||||
struct stat st;
|
||||
char *unix_name = *buffer;
|
||||
const WCHAR *ptr, *end;
|
||||
+ int old_cwd;
|
||||
|
||||
/* check syntax of individual components */
|
||||
|
||||
@@ -3977,9 +4044,13 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
|
||||
/* now do it component by component */
|
||||
|
||||
+ old_cwd = open( ".", O_RDONLY );
|
||||
+
|
||||
while (name_len)
|
||||
{
|
||||
const WCHAR *end, *next;
|
||||
+ WCHAR *target = NULL;
|
||||
+ int target_len = 0;
|
||||
|
||||
end = name;
|
||||
while (end < name + name_len && *end != '\\') end++;
|
||||
@@ -3999,8 +4070,39 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
|
||||
status = find_file_in_dir( unix_name, pos, name, end - name, is_unix );
|
||||
|
||||
+ /* follow reparse point and restart from there (if applicable) */
|
||||
+ if (find_reparse_target( unix_name, &target, &target_len ) == STATUS_SUCCESS)
|
||||
+ {
|
||||
+ int new_name_len = target_len + name_len + 1;
|
||||
+ WCHAR *new_name, *p;
|
||||
+
|
||||
+ if (!(p = new_name = malloc( new_name_len*sizeof(WCHAR) )))
|
||||
+ {
|
||||
+ free( target );
|
||||
+ return STATUS_NO_MEMORY;
|
||||
+ }
|
||||
+ memcpy( p, target, target_len*sizeof(WCHAR) );
|
||||
+ p += target_len;
|
||||
+ (p++)[0] = '\\';
|
||||
+ memcpy( p, next, name_len*sizeof(WCHAR) );
|
||||
+ if (target[0] == '\\')
|
||||
+ {
|
||||
+ strcpy( unix_name, "/" );
|
||||
+ pos = 0;
|
||||
+ }
|
||||
+ else
|
||||
+ unix_name = dirname( unix_name );
|
||||
+ TRACE( "Follow reparse point %s => %s, %s\n", debugstr_wn(name, end-name), unix_name,
|
||||
+ debugstr_wn(new_name, new_name_len) );
|
||||
+ next = new_name;
|
||||
+ name_len = new_name_len;
|
||||
+ free( target );
|
||||
+ free( name_buf );
|
||||
+ name_buf = new_name;
|
||||
+ status = STATUS_SUCCESS;
|
||||
+ }
|
||||
/* if this is the last element, not finding it is not necessarily fatal */
|
||||
- if (!name_len)
|
||||
+ else if (!name_len)
|
||||
{
|
||||
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
{
|
||||
@@ -4028,10 +4130,14 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
|
||||
if (status != STATUS_SUCCESS) break;
|
||||
|
||||
+ chdir( unix_name );
|
||||
pos += strlen( unix_name + pos );
|
||||
name = next;
|
||||
}
|
||||
|
||||
+ fchdir( old_cwd );
|
||||
+ free( name_buf );
|
||||
+
|
||||
return status;
|
||||
}
|
||||
|
||||
--
|
||||
2.35.1
|
||||
|
@ -1,15 +1,14 @@
|
||||
From 34e53ddfbd8c65e37d0cc3e68793833e733eb107 Mon Sep 17 00:00:00 2001
|
||||
From 2863150ec05b9abf82d0c72bc33b4c64092dc4ee Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 12 Dec 2020 17:28:31 -0700
|
||||
Subject: [PATCH] kernel32: Advertise junction point support.
|
||||
Subject: [PATCH] kernel32: Advertise reparse point support.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/mountmgr.sys/device.c | 33 ++++++++++++-
|
||||
dlls/mountmgr.sys/unixlib.c | 97 +++++++++++++++++++++++++++++++++++++
|
||||
dlls/mountmgr.sys/unixlib.h | 8 +++
|
||||
dlls/ntdll/tests/file.c | 9 +++-
|
||||
4 files changed, 144 insertions(+), 3 deletions(-)
|
||||
3 files changed, 137 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c
|
||||
index 0757036c8e3..6b2187c0991 100644
|
||||
@ -208,34 +207,6 @@ index ef5b10732f6..6d3902173b4 100644
|
||||
};
|
||||
|
||||
extern unixlib_handle_t mountmgr_handle;
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index ce8c37a7337..177151832a8 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5355,8 +5355,8 @@ static INT build_reparse_buffer(const WCHAR *filename, REPARSE_DATA_BUFFER **pbu
|
||||
|
||||
static void test_reparse_points(void)
|
||||
{
|
||||
+ WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH], volnameW[MAX_PATH];
|
||||
static const WCHAR reparseW[] = {'\\','r','e','p','a','r','s','e',0};
|
||||
- WCHAR path[MAX_PATH], reparse_path[MAX_PATH], target_path[MAX_PATH];
|
||||
static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
|
||||
INT buffer_len, string_len, path_len, total_len;
|
||||
FILE_BASIC_INFORMATION old_attrib, new_attrib;
|
||||
@@ -5385,7 +5385,12 @@ static void test_reparse_points(void)
|
||||
pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL);
|
||||
volW[0] = nameW.Buffer[4];
|
||||
pRtlFreeUnicodeString( &nameW );
|
||||
- GetVolumeInformationW(volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0);
|
||||
+ if (!GetVolumeNameForVolumeMountPointW(volW, volnameW, MAX_PATH))
|
||||
+ {
|
||||
+ win_skip("Failed to obtain volume name for current volume.\n");
|
||||
+ return;
|
||||
+ }
|
||||
+ GetVolumeInformationW(volnameW, 0, 0, 0, &dwLen, &dwFlags, 0, 0);
|
||||
if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS))
|
||||
{
|
||||
skip("File system does not support reparse points.\n");
|
||||
--
|
||||
2.37.2
|
||||
2.35.1
|
||||
|
@ -1,134 +0,0 @@
|
||||
From 5947b38607a0235f1f52cda37760325254a586bb Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 11 Apr 2019 17:57:53 -0600
|
||||
Subject: ntdll: Add support for file symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 29 +++++++++++++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 25 ++++++++++++++++++++-----
|
||||
2 files changed, 49 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 2f0903fe7f1..dd1fe247288 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5552,6 +5552,35 @@ static void test_reparse_points(void)
|
||||
/* Delete the junction point directory and create a blank slate for symlink tests */
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
ok(bret, "Failed to delete junction point!\n");
|
||||
+
|
||||
+ /* Create the file symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ 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%x).\n", dwret);
|
||||
+ buffer_len = build_reparse_buffer(nameW.Buffer, IO_REPARSE_TAG_SYMLINK, 0, &buffer);
|
||||
+ bret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0);
|
||||
+ ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+ /* Check deleting a file symlink as if it were a directory */
|
||||
+ bret = RemoveDirectoryW(reparse_path);
|
||||
+ todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
+ err = GetLastError();
|
||||
+ todo_wine ok(err == ERROR_DIRECTORY,
|
||||
+ "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
+ ERROR_DIRECTORY, err);
|
||||
+ dwret = GetFileAttributesW(reparse_path);
|
||||
+ todo_wine ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
|
||||
+
|
||||
+ /* Delete the symlink as a file */
|
||||
+ bret = DeleteFileW(reparse_path);
|
||||
+ todo_wine ok(bret, "Failed to delete symlink as a file!\n");
|
||||
+
|
||||
+ /* Create a blank slate for directory symlink tests */
|
||||
bret = CreateDirectoryW(reparse_path, NULL);
|
||||
ok(bret, "Failed to create junction point directory.\n");
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index bf63985f777..e082ff5d322 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -5980,6 +5980,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
int relative_offset = 0;
|
||||
UNICODE_STRING nt_dest;
|
||||
int dest_len, offset;
|
||||
+ BOOL is_dir = TRUE;
|
||||
NTSTATUS status;
|
||||
struct stat st;
|
||||
WCHAR *dest;
|
||||
@@ -6113,7 +6114,6 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
/* 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 );
|
||||
@@ -6147,8 +6147,11 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
/* 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) */
|
||||
+ /* success: link and folder/file have switched locations */
|
||||
+ if (is_dir)
|
||||
+ rmdir( tmplink ); /* remove the folder (at link location) */
|
||||
+ else
|
||||
+ unlink( tmplink ); /* remove the file (at link location) */
|
||||
}
|
||||
else if (errno == ENOSYS)
|
||||
{
|
||||
@@ -6367,6 +6370,7 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
|
||||
char tmpdir[PATH_MAX], tmpfile[PATH_MAX], *d;
|
||||
BOOL tempdir_created = FALSE;
|
||||
int dest_fd, needs_close;
|
||||
+ BOOL is_dir = TRUE;
|
||||
NTSTATUS status;
|
||||
char *unix_name;
|
||||
struct stat st;
|
||||
@@ -6379,12 +6383,13 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
|
||||
|
||||
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 );
|
||||
@@ -6397,11 +6402,21 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
|
||||
tempdir_created = TRUE;
|
||||
strcpy( tmpfile, tmpdir );
|
||||
strcat( tmpfile, "/tmpfile" );
|
||||
- if (mkdir( tmpfile, st.st_mode ))
|
||||
+ if (is_dir && mkdir( tmpfile, st.st_mode ))
|
||||
{
|
||||
status = errno_to_status( errno );
|
||||
goto cleanup;
|
||||
}
|
||||
+ else if (!is_dir)
|
||||
+ {
|
||||
+ int fd = open( tmpfile, 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( tmpfile, st.st_uid, st.st_gid );
|
||||
/* Atomically move the directory into position */
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,58 +0,0 @@
|
||||
From a25c97e1a004f773d8d1ad4d233aa140ca8d9c63 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Tue, 30 Apr 2019 16:24:54 -0600
|
||||
Subject: [PATCH] ntdll: Allow creation of dangling reparse points to
|
||||
non-existent paths.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 19 +++++++++++++++++--
|
||||
1 file changed, 17 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 82abd3e451b..fb4b3b65756 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3394,7 +3394,8 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
/* if this is the last element, not finding it is not necessarily fatal */
|
||||
if (!name_len)
|
||||
{
|
||||
- if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
+ if (status == STATUS_OBJECT_NAME_NOT_FOUND
|
||||
+ || (disposition == FILE_WINE_PATH && status == STATUS_OBJECT_NAME_NOT_FOUND))
|
||||
{
|
||||
if (disposition != FILE_OPEN && disposition != FILE_OVERWRITE)
|
||||
{
|
||||
@@ -3416,6 +3417,20 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
}
|
||||
if (end < next) strcat( unix_name, "/" );
|
||||
}
|
||||
+ else if (disposition == FILE_WINE_PATH && (status == STATUS_OBJECT_PATH_NOT_FOUND
|
||||
+ || status == STATUS_OBJECT_NAME_NOT_FOUND))
|
||||
+ {
|
||||
+ ret = ntdll_wcstoumbs( name, end - name, unix_name + pos + 1, MAX_DIR_ENTRY_LEN + 1, TRUE );
|
||||
+ if (ret > 0 && ret <= MAX_DIR_ENTRY_LEN)
|
||||
+ {
|
||||
+ unix_name[pos] = '/';
|
||||
+ unix_name[pos + 1 + ret] = 0;
|
||||
+ status = STATUS_NO_SUCH_FILE;
|
||||
+ pos += strlen( unix_name + pos );
|
||||
+ name = next;
|
||||
+ continue;
|
||||
+ }
|
||||
+ }
|
||||
else if (status == STATUS_OBJECT_NAME_NOT_FOUND) status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||||
|
||||
if (status != STATUS_SUCCESS) break;
|
||||
@@ -6076,7 +6091,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
status = STATUS_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
- status = wine_nt_to_unix_file_name( &nt_dest, unix_dest, &unix_dest_len, FALSE );
|
||||
+ status = wine_nt_to_unix_file_name( &nt_dest, unix_dest, &unix_dest_len, FILE_WINE_PATH );
|
||||
if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
free( unix_dest );
|
||||
}
|
||||
--
|
||||
2.35.1
|
||||
|
@ -0,0 +1,186 @@
|
||||
From 47d17451b657c657e63625b808ce6461169cd331 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sun, 4 Sep 2022 13:19:16 -0600
|
||||
Subject: [PATCH] ntdll: Allow reparse points to target the applicable Unix
|
||||
file/directory.
|
||||
|
||||
Allows lookup_unix_name 'shortcut' to succeed, as well as allowing
|
||||
the user to follow the symlink outside of Wine.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 151 +++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 151 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index d520a73b253..2a98d6baa28 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3526,6 +3526,150 @@ done:
|
||||
}
|
||||
|
||||
|
||||
+static NTSTATUS get_reparse_target( UNICODE_STRING *nt_target, REPARSE_DATA_BUFFER *buffer )
|
||||
+{
|
||||
+ int target_len, offset;
|
||||
+ WCHAR *target;
|
||||
+
|
||||
+ switch( buffer->ReparseTag )
|
||||
+ {
|
||||
+ case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
+ offset = buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
|
||||
+ target = &buffer->MountPointReparseBuffer.PathBuffer[offset];
|
||||
+ target_len = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
||||
+ break;
|
||||
+ case IO_REPARSE_TAG_SYMLINK:
|
||||
+ offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
|
||||
+ target = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
+ target_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return STATUS_NOT_IMPLEMENTED;
|
||||
+ }
|
||||
+ nt_target->Buffer = target;
|
||||
+ nt_target->Length = target_len;
|
||||
+ return STATUS_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* add a symlink to the unix target at the last point of the reparse point metadata */
|
||||
+NTSTATUS create_reparse_target( const char *unix_src, int depth, const char *link_path,
|
||||
+ REPARSE_DATA_BUFFER *buffer )
|
||||
+{
|
||||
+ ULONG nt_path_len = PATH_MAX, unix_path_len = PATH_MAX;
|
||||
+ UNICODE_STRING nt_target, nt_full_target;
|
||||
+ ULONG unix_target_len = PATH_MAX;
|
||||
+ char *unix_path = NULL, *d;
|
||||
+ char target_path[PATH_MAX];
|
||||
+ OBJECT_ATTRIBUTES attr;
|
||||
+ char *unix_target;
|
||||
+ int is_relative;
|
||||
+ NTSTATUS status;
|
||||
+ WCHAR *nt_path;
|
||||
+
|
||||
+ status = get_reparse_target( &nt_target, buffer );
|
||||
+ if (status != STATUS_SUCCESS)
|
||||
+ return status;
|
||||
+ /* if the target path is relative then turn the source path into an NT path */
|
||||
+ is_relative = (nt_target.Buffer[0] != '\\');
|
||||
+ if (is_relative)
|
||||
+ {
|
||||
+ UNICODE_STRING nt_path_tmp;
|
||||
+
|
||||
+ /* resolve the NT path of the source */
|
||||
+ unix_path = malloc( strlen(unix_src) + 2 );
|
||||
+ if (!unix_path) return STATUS_NO_MEMORY;
|
||||
+ 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)
|
||||
+ {
|
||||
+ free( unix_path );
|
||||
+ return STATUS_NO_MEMORY;
|
||||
+ }
|
||||
+ status = wine_unix_to_nt_file_name( unix_path, nt_path, &nt_path_len );
|
||||
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
+ free( nt_path );
|
||||
+ }
|
||||
+ free( unix_path );
|
||||
+ if (status != STATUS_SUCCESS)
|
||||
+ return status;
|
||||
+ /* re-resolve the unix path for the source */
|
||||
+ nt_path_tmp.Buffer = nt_path;
|
||||
+ nt_path_tmp.Length = wcslen(nt_path) * sizeof(WCHAR);
|
||||
+ InitializeObjectAttributes( &attr, &nt_path_tmp, 0, 0, NULL );
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ unix_path = malloc( unix_path_len );
|
||||
+ if (!unix_path) return STATUS_NO_MEMORY;
|
||||
+ status = wine_nt_to_unix_file_name( &attr, unix_path, &unix_path_len, FILE_OPEN_IF );
|
||||
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
+ free( unix_path );
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ nt_path = malloc( sizeof(WCHAR) );
|
||||
+ if (!nt_path) return STATUS_NO_MEMORY;
|
||||
+ nt_path[0] = 0;
|
||||
+ }
|
||||
+ /* append the target path (if absolute, appends to empty string) */
|
||||
+ nt_full_target.MaximumLength = nt_target.Length + wcslen(nt_path) * sizeof(WCHAR);
|
||||
+ nt_full_target.Buffer = malloc( nt_full_target.MaximumLength );
|
||||
+ if (!nt_full_target.Buffer)
|
||||
+ {
|
||||
+ status = STATUS_NO_MEMORY;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ wcscpy( nt_full_target.Buffer, nt_path );
|
||||
+ free( nt_path );
|
||||
+ memcpy( &nt_full_target.Buffer[wcslen(nt_full_target.Buffer)], nt_target.Buffer, nt_target.Length + sizeof(WCHAR));
|
||||
+ nt_full_target.Length = wcslen( nt_full_target.Buffer ) * sizeof(WCHAR);
|
||||
+ /* find the unix path for the target */
|
||||
+ InitializeObjectAttributes( &attr, &nt_full_target, 0, 0, NULL );
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ unix_target = malloc( unix_target_len );
|
||||
+ if (!unix_target)
|
||||
+ {
|
||||
+ status = STATUS_NO_MEMORY;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ status = wine_nt_to_unix_file_name( &attr, unix_target, &unix_target_len, FILE_OPEN_IF );
|
||||
+ if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
+ free( unix_target );
|
||||
+ }
|
||||
+ /* create the symlink to the target at the last metadata location */
|
||||
+ if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
|
||||
+ {
|
||||
+ int relative_offset;
|
||||
+
|
||||
+ target_path[0] = 0;
|
||||
+ relative_offset = unix_path ? strlen( unix_path ) : 0;
|
||||
+ if (unix_path && strncmp( unix_path, unix_target, relative_offset ) != 0)
|
||||
+ {
|
||||
+ relative_offset = 0;
|
||||
+ is_relative = FALSE;
|
||||
+ }
|
||||
+ for (;is_relative && depth > 0; depth--)
|
||||
+ strcat( target_path, "../" );
|
||||
+ strcat( target_path, &unix_target[relative_offset] );
|
||||
+ TRACE( "adding reparse point target: %s\n", target_path );
|
||||
+ symlink( target_path, link_path );
|
||||
+ }
|
||||
+ free( unix_target );
|
||||
+ status = STATUS_SUCCESS;
|
||||
+
|
||||
+cleanup:
|
||||
+ free( unix_path );
|
||||
+ free( nt_full_target.Buffer );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* 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.
|
||||
@@ -3655,6 +3799,13 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
chdir( link_dir );
|
||||
}
|
||||
|
||||
+ /* create the very last link directory */
|
||||
+ strcpy( link_path, target_path );
|
||||
+ strcpy( link_dir, link_path );
|
||||
+ link_dir[strlen(link_dir)-1] = 0;
|
||||
+ if (mkdir_p( link_dir, 0777) == 0)
|
||||
+ create_reparse_target( unix_src, depth + 2, link_path, buffer );
|
||||
+
|
||||
/* revert to the original directory */
|
||||
chdir( original_dir );
|
||||
|
||||
--
|
||||
2.35.1
|
||||
|
@ -0,0 +1,86 @@
|
||||
From 18903b2921a76abaa12677c1c8d1b936d7e966d5 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 1 May 2019 12:06:20 -0600
|
||||
Subject: [PATCH] ntdll: Always report symbolic links as containing zero bytes.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 32 ++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 4 ++++
|
||||
2 files changed, 36 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 799a8cebe50..84797ba46b7 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5396,6 +5396,7 @@ static void test_reparse_points(void)
|
||||
static const WCHAR dotW[] = {'.',0};
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
+ WIN32_FILE_ATTRIBUTE_DATA fad;
|
||||
WCHAR buf[] = {0,0,0,0};
|
||||
HANDLE handle, token;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
@@ -5626,6 +5627,37 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to create symlink! (0x%lx)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* Check the size of the symlink */
|
||||
+ bret = GetFileAttributesExW(reparse_path, GetFileExInfoStandard, &fad);
|
||||
+ ok(bret, "Failed to read file attributes from the symlink target.\n");
|
||||
+ ok(fad.nFileSizeLow == 0 && fad.nFileSizeHigh == 0, "Size of symlink is not zero.\n");
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to open symlink file.\n");
|
||||
+ ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
+ bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
+ todo_wine ok(bret, "Failed to read data from the symlink.\n");
|
||||
+ ok(dwLen == 0, "Length of symlink data is not zero.\n");
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+ /* Check the size/data of the symlink target */
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS, 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.\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 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);
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 2a98d6baa28..ba3ea91b92f 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1797,6 +1797,8 @@ static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULON
|
||||
|
||||
/* symbolic links (either junction points or NT symlinks) are "reparse points" */
|
||||
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
+ /* symbolic links always report size 0 */
|
||||
+ st->st_size = 0;
|
||||
/* 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);
|
||||
@@ -1857,6 +1859,8 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
|
||||
stat( path, st );
|
||||
/* symbolic links (either junction points or NT symlinks) are "reparse points" */
|
||||
*attr |= FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
+ /* symbolic links always report size 0 */
|
||||
+ st->st_size = 0;
|
||||
/* 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);
|
||||
--
|
||||
2.35.1
|
||||
|
@ -1,193 +0,0 @@
|
||||
From e8f479e5fe6157f37a12af40ac4e336b93f8c04a Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 30 Mar 2019 12:00:51 -0600
|
||||
Subject: ntdll: Correctly report file symbolic links as files.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 118 +++++++++++++++++++++++++++--------------
|
||||
1 file changed, 79 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index e749e192f6f..461ad89938f 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1599,6 +1599,9 @@ static inline int get_file_xattr( char *hexattr, int attrlen )
|
||||
return 0;
|
||||
}
|
||||
|
||||
+NTSTATUS get_symlink_properties(const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
+ DWORD *tag, ULONG *flags, BOOL *is_dir);
|
||||
+
|
||||
/* fetch the attributes of a file */
|
||||
static inline ULONG get_file_attributes( const struct stat *st )
|
||||
{
|
||||
@@ -1683,10 +1686,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 (get_symlink_properties( path, NULL, NULL, NULL, NULL, &is_dir ) == STATUS_SUCCESS)
|
||||
+ 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 )))
|
||||
{
|
||||
@@ -6201,47 +6209,35 @@ 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)
|
||||
+NTSTATUS get_symlink_properties(const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
+ DWORD *tag, ULONG *flags, BOOL *is_dir)
|
||||
{
|
||||
- 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;
|
||||
+ int len = MAX_PATH;
|
||||
+ DWORD reparse_tag;
|
||||
NTSTATUS status;
|
||||
- ULONG flags = 0;
|
||||
- WCHAR *nt_dest;
|
||||
+ BOOL dir_flag;
|
||||
+ char *p, *tmp;
|
||||
ssize_t ret;
|
||||
- char *p;
|
||||
int i;
|
||||
|
||||
- if ((status = server_get_unix_fd( handle, FILE_ANY_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
|
||||
- return status;
|
||||
-
|
||||
- if ((status = server_get_unix_name( handle, &unix_src )))
|
||||
- goto cleanup;
|
||||
-
|
||||
- ret = readlink( unix_src, unix_dest, sizeof(unix_dest) );
|
||||
- if (ret < 0)
|
||||
+ if (unix_dest_len) len = *unix_dest_len;
|
||||
+ if (!unix_dest)
|
||||
+ tmp = malloc( len );
|
||||
+ else
|
||||
+ tmp = unix_dest;
|
||||
+ if ((ret = readlink( unix_src, tmp, len )) < 0)
|
||||
{
|
||||
status = errno_to_status( errno );
|
||||
goto cleanup;
|
||||
}
|
||||
- unix_dest_len = ret;
|
||||
+ len = ret;
|
||||
+ tmp[len] = 0;
|
||||
|
||||
/* Decode the reparse tag from the symlink */
|
||||
- p = unix_dest;
|
||||
+ p = tmp;
|
||||
if (*p == '.')
|
||||
{
|
||||
- flags = SYMLINK_FLAG_RELATIVE;
|
||||
+ if (flags) *flags = SYMLINK_FLAG_RELATIVE;
|
||||
p++;
|
||||
}
|
||||
if (*p++ != '/')
|
||||
@@ -6249,7 +6245,7 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
- buffer->ReparseTag = 0;
|
||||
+ reparse_tag = 0;
|
||||
for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
{
|
||||
char c = *p++;
|
||||
@@ -6264,22 +6260,66 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
- buffer->ReparseTag |= (val << i);
|
||||
+ reparse_tag |= (val << i);
|
||||
}
|
||||
/* skip past the directory/file flag */
|
||||
- if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ if (reparse_tag == IO_REPARSE_TAG_SYMLINK)
|
||||
{
|
||||
char c = *p++;
|
||||
|
||||
- if ((c != '/' && c != '.') || (c == '.' && *p++ != '/'))
|
||||
+ if (c == '/')
|
||||
+ dir_flag = FALSE;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ dir_flag = TRUE;
|
||||
+ else
|
||||
{
|
||||
status = STATUS_NOT_IMPLEMENTED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
- unix_dest_len -= (p - unix_dest);
|
||||
- memmove(unix_dest, p, unix_dest_len);
|
||||
- unix_dest[unix_dest_len] = 0;
|
||||
+ else
|
||||
+ dir_flag = TRUE;
|
||||
+ len -= (p - tmp);
|
||||
+ if (tag) *tag = reparse_tag;
|
||||
+ if (is_dir) *is_dir = dir_flag;
|
||||
+ if (unix_dest) memmove(unix_dest, p, len + 1);
|
||||
+ if (unix_dest_len) *unix_dest_len = len;
|
||||
+ status = STATUS_SUCCESS;
|
||||
+
|
||||
+cleanup:
|
||||
+ if (!unix_dest) free( tmp );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * Retrieve the unix name corresponding to a file handle and use that to find the destination of the
|
||||
+ * symlink corresponding to that file handle.
|
||||
+ */
|
||||
+NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *size)
|
||||
+{
|
||||
+ INT prefix_len, path_len, total_len;
|
||||
+ char *unix_src, unix_dest[PATH_MAX];
|
||||
+ VOID *subst_name, *print_name;
|
||||
+ SIZE_T nt_dest_len = PATH_MAX;
|
||||
+ int unix_dest_len = PATH_MAX;
|
||||
+ BOOL dest_allocated = FALSE;
|
||||
+ int dest_fd, needs_close;
|
||||
+ ULONG out_size = *size;
|
||||
+ DWORD max_length;
|
||||
+ NTSTATUS status;
|
||||
+ ULONG flags = 0;
|
||||
+ WCHAR *nt_dest;
|
||||
+
|
||||
+ 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;
|
||||
+
|
||||
+ if ((status = get_symlink_properties( unix_src, unix_dest, &unix_dest_len, &buffer->ReparseTag,
|
||||
+ &flags, NULL )))
|
||||
+ goto cleanup;
|
||||
|
||||
/* convert the relative path into an absolute path */
|
||||
if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,27 +0,0 @@
|
||||
From 08e37ba50a5dc930c0d46199836d72100008ee31 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sun, 22 Nov 2020 22:35:50 -0700
|
||||
Subject: kernelbase: Convert FILE_FLAG_OPEN_REPARSE_POINT for passing to
|
||||
ntdll.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernelbase/file.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index 5c7b649d805..a9e05be62da 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -727,6 +727,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;
|
||||
}
|
||||
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 491f25543e5e15cc9f9c12da0d67665c79f53720 Mon Sep 17 00:00:00 2001
|
||||
From 4bd2d5bd482153546e644778dd7723dadf639d45 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 1 May 2019 17:48:51 -0600
|
||||
Subject: ntdll: Find dangling symlinks quickly.
|
||||
Subject: [PATCH] ntdll: Find dangling symlinks quickly.
|
||||
|
||||
This is also necessary on systems (such as MacOS) that support
|
||||
case-insensitive lookups of files.
|
||||
@ -12,10 +12,10 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 42ae14851fa..cd7fca79d49 100644
|
||||
index ba3ea91b92f..db7c493a458 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -2723,7 +2723,7 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i
|
||||
@@ -2914,7 +2914,7 @@ static NTSTATUS find_file_in_dir( char *unix_name, int pos, const WCHAR *name, i
|
||||
if (ret >= 0 && ret <= MAX_DIR_ENTRY_LEN)
|
||||
{
|
||||
unix_name[pos + ret] = 0;
|
||||
@ -24,7 +24,7 @@ index 42ae14851fa..cd7fca79d49 100644
|
||||
}
|
||||
if (check_case) goto not_found; /* we want an exact match */
|
||||
|
||||
@@ -3387,7 +3387,7 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
@@ -4185,7 +4185,7 @@ static NTSTATUS lookup_unix_name( const WCHAR *name, int name_len, char **buffer
|
||||
char *p;
|
||||
unix_name[pos + 1 + ret] = 0;
|
||||
for (p = unix_name + pos ; *p; p++) if (*p == '\\') *p = '/';
|
||||
@ -34,5 +34,5 @@ index 42ae14851fa..cd7fca79d49 100644
|
||||
if (disposition == FILE_CREATE) return STATUS_OBJECT_NAME_COLLISION;
|
||||
return STATUS_SUCCESS;
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -1,4 +1,4 @@
|
||||
From 92381fe379f4e971168e1e3d9c67394eb8d0f697 Mon Sep 17 00:00:00 2001
|
||||
From d3873d9acf5d2883a528d59a3c6f6a269694fa01 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Mon, 23 Nov 2020 13:08:02 -0700
|
||||
Subject: [PATCH] ntdll: Succeed with no data for NtReadFile on reparse points.
|
||||
@ -12,12 +12,12 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
4 files changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 92cff9d9b2e..1e867146cd9 100644
|
||||
index 84797ba46b7..f613a64b92b 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5591,7 +5591,7 @@ static void test_reparse_points(void)
|
||||
@@ -5636,7 +5636,7 @@ static void test_reparse_points(void)
|
||||
ok(handle != INVALID_HANDLE_VALUE, "Failed to open symlink file.\n");
|
||||
todo_wine ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
- todo_wine ok(bret, "Failed to read data from the symlink.\n");
|
||||
+ ok(bret, "Failed to read data from the symlink.\n");
|
||||
@ -25,10 +25,10 @@ index 92cff9d9b2e..1e867146cd9 100644
|
||||
CloseHandle(handle);
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 0d6f08072a7..d7e0be3f65c 100644
|
||||
index db7c493a458..8266478cd65 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -5415,6 +5415,11 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo
|
||||
@@ -6264,6 +6264,11 @@ NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, vo
|
||||
if (needs_close) close( unix_handle );
|
||||
return status;
|
||||
}
|
||||
@ -41,7 +41,7 @@ index 0d6f08072a7..d7e0be3f65c 100644
|
||||
if (type == FD_TYPE_SERIAL && async_read && length)
|
||||
{
|
||||
diff --git a/server/file.c b/server/file.c
|
||||
index eb2dc5696ed..b9d0eb2d5f4 100644
|
||||
index 76c687833c9..b4f99ddc93b 100644
|
||||
--- a/server/file.c
|
||||
+++ b/server/file.c
|
||||
@@ -294,6 +294,7 @@ static enum server_fd_type file_get_fd_type( struct fd *fd )
|
||||
@ -53,10 +53,10 @@ index eb2dc5696ed..b9d0eb2d5f4 100644
|
||||
if (S_ISDIR(file->mode)) return FD_TYPE_DIR;
|
||||
return FD_TYPE_CHAR;
|
||||
diff --git a/server/protocol.def b/server/protocol.def
|
||||
index 93c17e091cb..238e23b0a85 100644
|
||||
index d828d41d1f7..aa7398369ad 100644
|
||||
--- a/server/protocol.def
|
||||
+++ b/server/protocol.def
|
||||
@@ -1382,6 +1382,7 @@ enum server_fd_type
|
||||
@@ -1381,6 +1381,7 @@ enum server_fd_type
|
||||
{
|
||||
FD_TYPE_INVALID, /* invalid file (no associated fd) */
|
||||
FD_TYPE_FILE, /* regular file */
|
@ -1,180 +0,0 @@
|
||||
From f9a9c4ef4515184fde0478591bfc08d8de9c49c1 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 12 Dec 2020 16:54:28 -0700
|
||||
Subject: server: Implement FILE_OPEN_REPARSE_POINT option.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 27 +++++++++++++++++++++++----
|
||||
server/fd.c | 39 ++++++++++++++++++++++++++++++++-------
|
||||
2 files changed, 55 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index dd1fe247288..490d5160761 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5388,6 +5388,7 @@ 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;
|
||||
@@ -5475,7 +5476,7 @@ static void test_reparse_points(void)
|
||||
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);
|
||||
+ todo_wine ok(dwret == STATUS_SUCCESS, "Failed to set junction point folder's attributes (0x%x).\n", dwret);
|
||||
memset(&guid_buffer, 0x00, sizeof(guid_buffer));
|
||||
guid_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||||
bret = DeviceIoControl(handle, FSCTL_DELETE_REPARSE_POINT, (LPVOID)&guid_buffer,
|
||||
@@ -5484,7 +5485,7 @@ static void test_reparse_points(void)
|
||||
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(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ todo_wine ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
"Junction point folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
@@ -5565,6 +5566,24 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to create symlink! (0x%x)\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%x).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ ok(GetFileSize(handle, NULL) == sizeof(fooW), "symlink target size does not match (%d != %d)\n",
|
||||
+ GetFileSize(handle, NULL), 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 (%d != %d).\n",
|
||||
+ dwLen, 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);
|
||||
todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
@@ -5628,7 +5647,7 @@ static void test_reparse_points(void)
|
||||
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);
|
||||
+ todo_wine 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,
|
||||
@@ -5637,7 +5656,7 @@ static void test_reparse_points(void)
|
||||
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,
|
||||
+ todo_wine ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
"Symlink folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index ce1c47d58a9..4fe8be31ab3 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -101,6 +101,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
|
||||
@@ -1902,6 +1906,14 @@ void get_nt_name( struct fd *fd, struct unicode_str *name )
|
||||
name->len = fd->nt_namelen;
|
||||
}
|
||||
|
||||
+int check_symlink( char *name )
|
||||
+{
|
||||
+ struct stat st;
|
||||
+
|
||||
+ lstat( name, &st );
|
||||
+ return S_ISLNK( st.st_mode );
|
||||
+}
|
||||
+
|
||||
/* 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,
|
||||
@@ -1962,6 +1974,18 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
}
|
||||
else rw_mode = O_RDONLY;
|
||||
|
||||
+ fd->unix_name = NULL;
|
||||
+ if ((path = dup_fd_name( root, name )))
|
||||
+ {
|
||||
+ int is_symlink = check_symlink( path );
|
||||
+#if defined(O_SYMLINK)
|
||||
+ if (is_symlink && (options & FILE_OPEN_REPARSE_POINT) && !(flags & O_CREAT))
|
||||
+ flags |= O_SYMLINK;
|
||||
+#endif
|
||||
+ fd->unlink_name = path;
|
||||
+ fd->unix_name = realpath( path, NULL );
|
||||
+ }
|
||||
+
|
||||
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 */
|
||||
@@ -1979,12 +2003,6 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
}
|
||||
|
||||
fd->nt_name = dup_nt_name( root, nt_name, &fd->nt_namelen );
|
||||
- fd->unix_name = NULL;
|
||||
- if ((path = dup_fd_name( root, name )))
|
||||
- {
|
||||
- fd->unlink_name = path;
|
||||
- fd->unix_name = realpath( path, NULL );
|
||||
- }
|
||||
|
||||
closed_fd->unix_fd = fd->unix_fd;
|
||||
closed_fd->unlink = 0;
|
||||
@@ -2453,6 +2471,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;
|
||||
@@ -2460,8 +2479,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;
|
||||
}
|
||||
@@ -2473,6 +2497,7 @@ static int is_dir_empty( int fd )
|
||||
empty = 0;
|
||||
}
|
||||
closedir( dir );
|
||||
+ close( dir_fd );
|
||||
return empty;
|
||||
}
|
||||
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,60 +1,71 @@
|
||||
From c908729cdf6a62f259f20a24ab287b056f09326b Mon Sep 17 00:00:00 2001
|
||||
From e5f483d10b2b90259061e115695beb9ae3e9aedf Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Wed, 25 Nov 2020 09:19:42 -0700
|
||||
Subject: ntdll: Add support for FileAttributeTagInformation.
|
||||
Subject: [PATCH] ntdll: Add support for FileAttributeTagInformation.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 6 ++++++
|
||||
dlls/ntdll/unix/file.c | 4 +++-
|
||||
2 files changed, 9 insertions(+), 1 deletion(-)
|
||||
dlls/ntdll/tests/file.c | 6 ++++++
|
||||
dlls/ntdll/unix/file.c | 15 ++++++++++++++-
|
||||
2 files changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index cde3b86ade2..18887895e40 100644
|
||||
index f613a64b92b..a84abd66106 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5386,6 +5386,7 @@ static void test_reparse_points(void)
|
||||
static WCHAR volW[] = {'c',':','\\',0};
|
||||
@@ -5394,6 +5394,7 @@ static void test_reparse_points(void)
|
||||
WCHAR *dest, *long_path, *abs_target;
|
||||
REPARSE_GUID_DATA_BUFFER guid_buffer;
|
||||
static const WCHAR dotW[] = {'.',0};
|
||||
+ FILE_ATTRIBUTE_TAG_INFORMATION info;
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
WIN32_FILE_ATTRIBUTE_DATA fad;
|
||||
@@ -5394,6 +5395,7 @@ static void test_reparse_points(void)
|
||||
@@ -5402,6 +5403,7 @@ static void test_reparse_points(void)
|
||||
IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING nameW;
|
||||
TOKEN_PRIVILEGES tp;
|
||||
+ NTSTATUS status;
|
||||
WCHAR *dest;
|
||||
LUID luid;
|
||||
BOOL bret;
|
||||
@@ -5583,6 +5585,10 @@ static void test_reparse_points(void)
|
||||
|
||||
@@ -5638,6 +5640,10 @@ static void test_reparse_points(void)
|
||||
bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
ok(bret, "Failed to read data from the symlink.\n");
|
||||
ok(dwLen == 0, "Length of symlink data is not zero.\n");
|
||||
+ memset(&info, 0x0, sizeof(info));
|
||||
+ status = pNtQueryInformationFile(handle, &iosb, &info, sizeof(info), FileAttributeTagInformation);
|
||||
+ ok( status == STATUS_SUCCESS, "got %#x\n", status );
|
||||
+ ok( info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got reparse tag %#x\n", info.ReparseTag );
|
||||
+ ok( status == STATUS_SUCCESS, "got %#lx\n", status );
|
||||
+ ok( info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got reparse tag %#lx\n", info.ReparseTag );
|
||||
CloseHandle(handle);
|
||||
|
||||
/* Check the size/data of the symlink target */
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 75194023e69..a01c31413ad 100644
|
||||
index 8266478cd65..d5d62f3ba94 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -4561,7 +4561,9 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
|
||||
@@ -5373,7 +5373,20 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
|
||||
{
|
||||
FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr;
|
||||
info->FileAttributes = attr;
|
||||
- info->ReparseTag = 0; /* FIXME */
|
||||
+ info->ReparseTag = 0;
|
||||
+ if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
+ get_symlink_properties( fd, "", NULL, NULL, &info->ReparseTag, NULL, NULL );
|
||||
+ {
|
||||
+ REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
+ ULONG buffer_len = 0;
|
||||
+
|
||||
+ if (get_reparse_point( handle, NULL, &buffer_len ) == STATUS_BUFFER_TOO_SMALL)
|
||||
+ {
|
||||
+ buffer = malloc( buffer_len );
|
||||
+ if (get_reparse_point( handle, buffer, &buffer_len ) == STATUS_SUCCESS)
|
||||
+ info->ReparseTag = buffer->ReparseTag;
|
||||
+ free( buffer );
|
||||
+ }
|
||||
+ }
|
||||
if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st ))
|
||||
info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||||
}
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -1,117 +0,0 @@
|
||||
From ac5d188111d81beef35e3953ae4f2f0f1c6a1b6a Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sun, 22 Nov 2020 22:37:33 -0700
|
||||
Subject: ntdll: Allow set_file_times_precise to work on reparse points.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
configure.ac | 3 ++-
|
||||
dlls/ntdll/tests/file.c | 8 ++++----
|
||||
dlls/ntdll/unix/file.c | 22 ++++++++++++++++++----
|
||||
3 files changed, 24 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 9e113d939ec..7c2705b88b8 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -2034,7 +2034,8 @@ AC_CHECK_FUNCS(\
|
||||
sigprocmask \
|
||||
sysinfo \
|
||||
tcdrain \
|
||||
- thr_kill2
|
||||
+ thr_kill2 \
|
||||
+ utimensat
|
||||
)
|
||||
CFLAGS="$ac_save_CFLAGS"
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 490d5160761..2449ee54799 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5476,7 +5476,7 @@ static void test_reparse_points(void)
|
||||
memset(&old_attrib, 0x00, sizeof(old_attrib));
|
||||
old_attrib.LastAccessTime.QuadPart = 0x200deadcafebeef;
|
||||
dwret = NtSetInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
- todo_wine 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%x).\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,
|
||||
@@ -5485,7 +5485,7 @@ static void test_reparse_points(void)
|
||||
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);
|
||||
- todo_wine ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
"Junction point folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
@@ -5647,7 +5647,7 @@ static void test_reparse_points(void)
|
||||
memset(&old_attrib, 0x00, sizeof(old_attrib));
|
||||
old_attrib.LastAccessTime.QuadPart = 0x200deadcafebeef;
|
||||
dwret = NtSetInformationFile(handle, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation);
|
||||
- todo_wine ok(dwret == STATUS_SUCCESS, "Failed to set symlink folder's attributes (0x%x).\n", dwret);
|
||||
+ 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,
|
||||
@@ -5656,7 +5656,7 @@ static void test_reparse_points(void)
|
||||
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);
|
||||
- todo_wine ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
+ ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart,
|
||||
"Symlink folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 461ad89938f..364bda14b4b 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1724,6 +1724,14 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
|
||||
}
|
||||
|
||||
|
||||
+#if defined(__ANDROID__) && !defined(HAVE_UTIMENSAT)
|
||||
+static int utimensat( int fd, const char *name, const struct timespec spec[2], int flags )
|
||||
+{
|
||||
+ return syscall( __NR_utimensat, fd, name, spec, flags );
|
||||
+}
|
||||
+#define HAVE_UTIMENSAT
|
||||
+#endif /* __ANDROID__ */
|
||||
+
|
||||
#if defined(__ANDROID__) && !defined(HAVE_FUTIMENS)
|
||||
static int futimens( int fd, const struct timespec spec[2] )
|
||||
{
|
||||
@@ -1739,7 +1747,7 @@ static int futimens( int fd, const struct timespec spec[2] )
|
||||
static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
|
||||
const LARGE_INTEGER *atime, NTSTATUS *status )
|
||||
{
|
||||
-#ifdef HAVE_FUTIMENS
|
||||
+#if defined(HAVE_FUTIMENS) || defined(HAVE_UTIMENSAT)
|
||||
struct timespec tv[2];
|
||||
|
||||
tv[0].tv_sec = tv[1].tv_sec = 0;
|
||||
@@ -1755,10 +1763,16 @@ static BOOL set_file_times_precise( int fd, const LARGE_INTEGER *mtime,
|
||||
tv[1].tv_nsec = (mtime->QuadPart % 10000000) * 100;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
- if (!&futimens) return FALSE;
|
||||
+ if (!&utimensat || !&futimens) return FALSE;
|
||||
+#endif
|
||||
+#if defined(HAVE_UTIMENSAT)
|
||||
+ /* futimens does not work on O_PATH|O_NOFOLLOW (O_SYMLINK) file descriptors, so if fd is for a
|
||||
+ * symlink then use utimensat with an empty path (.) and do not follow the link. */
|
||||
+ if (utimensat(fd, ".", tv, AT_SYMLINK_NOFOLLOW) == 0) *status = STATUS_SUCCESS;
|
||||
+ else
|
||||
#endif
|
||||
- if (futimens( fd, tv ) == -1) *status = errno_to_status( errno );
|
||||
- else *status = STATUS_SUCCESS;
|
||||
+ if (futimens(fd, tv) == 0) *status = STATUS_SUCCESS;
|
||||
+ else *status = errno_to_status( errno );
|
||||
return TRUE;
|
||||
#else
|
||||
return FALSE;
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,4 +1,4 @@
|
||||
From 73ce551bae8a040575c3f631b8f67a5eb79bda37 Mon Sep 17 00:00:00 2001
|
||||
From f88d7e0e5ee70eaaa50c5fd448ad2cce1197c5cd Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 13 Mar 2019 16:02:05 -0600
|
||||
Subject: [PATCH] kernel32: Implement CreateSymbolicLink[A|W] with ntdll
|
||||
@ -7,11 +7,11 @@ Subject: [PATCH] kernel32: Implement CreateSymbolicLink[A|W] with ntdll
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernel32/path.c | 12 +++-
|
||||
dlls/kernel32/tests/path.c | 94 +++++++++++++++++++++++++++++
|
||||
dlls/kernelbase/file.c | 105 ++++++++++++++++++++++++++++++++-
|
||||
dlls/msvcp120/tests/msvcp120.c | 75 +++++++++++------------
|
||||
dlls/kernel32/tests/path.c | 94 ++++++++++++++++++++++++++++++
|
||||
dlls/kernelbase/file.c | 103 ++++++++++++++++++++++++++++++++-
|
||||
dlls/msvcp120/tests/msvcp120.c | 75 +++++++++++-------------
|
||||
dlls/msvcp140/tests/msvcp140.c | 63 +++++++++-----------
|
||||
5 files changed, 267 insertions(+), 82 deletions(-)
|
||||
5 files changed, 266 insertions(+), 81 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c
|
||||
index 2dd3eac3c26..97428dd4d95 100644
|
||||
@ -37,7 +37,7 @@ index 2dd3eac3c26..97428dd4d95 100644
|
||||
|
||||
/*************************************************************************
|
||||
diff --git a/dlls/kernel32/tests/path.c b/dlls/kernel32/tests/path.c
|
||||
index db376eef24a..33a29ab1cea 100644
|
||||
index 3c57e81f4c6..fa143cb4e48 100644
|
||||
--- a/dlls/kernel32/tests/path.c
|
||||
+++ b/dlls/kernel32/tests/path.c
|
||||
@@ -83,6 +83,9 @@ static NTSTATUS (WINAPI *pLdrGetDllPath)(LPCWSTR,ULONG,LPWSTR*,LPWSTR*);
|
||||
@ -161,7 +161,7 @@ index db376eef24a..33a29ab1cea 100644
|
||||
+ test_CreateSymbolicLink();
|
||||
}
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index 4ef43b83b66..c2f6a8307a8 100644
|
||||
index 2cbe8f4ac03..736589e9a5f 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -38,6 +38,7 @@
|
||||
@ -172,12 +172,9 @@ index 4ef43b83b66..c2f6a8307a8 100644
|
||||
|
||||
#include "kernelbase.h"
|
||||
#include "wine/exception.h"
|
||||
@@ -938,10 +939,108 @@ done:
|
||||
/*************************************************************************
|
||||
* CreateSymbolicLinkW (kernelbase.@)
|
||||
@@ -948,8 +949,106 @@ done:
|
||||
*/
|
||||
-BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags )
|
||||
+BOOLEAN WINAPI DECLSPEC_HOTPATCH CreateSymbolicLinkW( const WCHAR *link, const WCHAR *target, DWORD flags )
|
||||
BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags )
|
||||
{
|
||||
- FIXME( "(%s %s %ld): stub\n", debugstr_w(link), debugstr_w(target), flags );
|
||||
- return TRUE;
|
||||
@ -195,7 +192,7 @@ index 4ef43b83b66..c2f6a8307a8 100644
|
||||
+ HANDLE hlink;
|
||||
+ DWORD dwret;
|
||||
+
|
||||
+ TRACE( "(%s %s %#lx)\n", debugstr_w(link), debugstr_w(target), flags );
|
||||
+ TRACE( "(%s %s %ld): stub\n", debugstr_w(link), debugstr_w(target), flags );
|
||||
+
|
||||
+ is_relative = (RtlDetermineDosPathNameType_U( target ) == RELATIVE_PATH);
|
||||
+ is_dir = (flags & SYMBOLIC_LINK_FLAG_DIRECTORY);
|
||||
@ -285,7 +282,7 @@ index 4ef43b83b66..c2f6a8307a8 100644
|
||||
|
||||
|
||||
diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c
|
||||
index 9e36071d06a..681acfc1c39 100644
|
||||
index 44c0a3f0fe0..e5561636584 100644
|
||||
--- a/dlls/msvcp120/tests/msvcp120.c
|
||||
+++ b/dlls/msvcp120/tests/msvcp120.c
|
||||
@@ -1613,15 +1613,14 @@ static void test_tr2_sys__Stat(void)
|
||||
@ -427,10 +424,10 @@ index 9e36071d06a..681acfc1c39 100644
|
||||
}
|
||||
|
||||
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c
|
||||
index c79e425190b..f3b98ec4e73 100644
|
||||
index 640e44e5b7c..6e9727f5854 100644
|
||||
--- a/dlls/msvcp140/tests/msvcp140.c
|
||||
+++ b/dlls/msvcp140/tests/msvcp140.c
|
||||
@@ -787,16 +787,15 @@ static void test_Stat(void)
|
||||
@@ -857,16 +857,15 @@ static void test_Stat(void)
|
||||
WCHAR const *path;
|
||||
enum file_type ret;
|
||||
int perms;
|
||||
@ -455,7 +452,7 @@ index c79e425190b..f3b98ec4e73 100644
|
||||
};
|
||||
|
||||
GetCurrentDirectoryW(MAX_PATH, origin_path);
|
||||
@@ -854,26 +853,20 @@ static void test_Stat(void)
|
||||
@@ -924,26 +923,20 @@ static void test_Stat(void)
|
||||
for(i=0; i<ARRAY_SIZE(tests); i++) {
|
||||
perms = 0xdeadbeef;
|
||||
val = p_Stat(tests[i].path, &perms);
|
||||
@ -490,7 +487,7 @@ index c79e425190b..f3b98ec4e73 100644
|
||||
}
|
||||
|
||||
GetSystemDirectoryW(sys_path, MAX_PATH);
|
||||
@@ -885,9 +878,9 @@ static void test_Stat(void)
|
||||
@@ -955,9 +948,9 @@ static void test_Stat(void)
|
||||
ok(perms == expected_perms, "_Stat(): perms expect: 0%o, got 0%o\n", expected_perms, perms);
|
||||
|
||||
if(ret) {
|
||||
@ -502,7 +499,7 @@ index c79e425190b..f3b98ec4e73 100644
|
||||
"expect wine_test_dir/dir_link to exist\n");
|
||||
}
|
||||
ok(DeleteFileW(L"wine_test_dir/f1"), "expect wine_test_dir/f1 to exist\n");
|
||||
@@ -1006,15 +999,14 @@ static void test_Unlink(void)
|
||||
@@ -1076,15 +1069,14 @@ static void test_Unlink(void)
|
||||
struct {
|
||||
WCHAR const *path;
|
||||
int last_error;
|
||||
@ -525,7 +522,7 @@ index c79e425190b..f3b98ec4e73 100644
|
||||
};
|
||||
|
||||
GetCurrentDirectoryW(MAX_PATH, current_path);
|
||||
@@ -1043,9 +1035,8 @@ static void test_Unlink(void)
|
||||
@@ -1113,9 +1105,8 @@ static void test_Unlink(void)
|
||||
for(i=0; i<ARRAY_SIZE(tests); i++) {
|
||||
errno = 0xdeadbeef;
|
||||
ret = p_Unlink(tests[i].path);
|
||||
@ -538,5 +535,5 @@ index c79e425190b..f3b98ec4e73 100644
|
||||
}
|
||||
|
||||
--
|
||||
2.34.1
|
||||
2.35.1
|
||||
|
@ -1,134 +0,0 @@
|
||||
From 854ece04789b7243ebb440c8dfe0bc6bebbf71b2 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 30 Mar 2019 13:41:07 -0600
|
||||
Subject: server: Properly handle file symlink deletion.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 12 ++++-----
|
||||
server/fd.c | 58 +++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 62 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 2449ee54799..9fe806ff3cc 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5586,18 +5586,18 @@ static void test_reparse_points(void)
|
||||
|
||||
/* Check deleting a file symlink as if it were a directory */
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
- todo_wine ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
+ ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
|
||||
err = GetLastError();
|
||||
- todo_wine ok(err == ERROR_DIRECTORY,
|
||||
- "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
- ERROR_DIRECTORY, err);
|
||||
+ ok(err == ERROR_DIRECTORY,
|
||||
+ "Expected last error 0x%x for RemoveDirectory on file symlink (actually 0x%x)!\n",
|
||||
+ ERROR_DIRECTORY, err);
|
||||
dwret = GetFileAttributesW(reparse_path);
|
||||
- todo_wine ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
+ ok(dwret != (DWORD)~0, "Symlink doesn't exist (attributes: 0x%x)!\n", dwret);
|
||||
ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a symlink! (attributes: 0x%x)\n", dwret);
|
||||
|
||||
/* Delete the symlink as a file */
|
||||
bret = DeleteFileW(reparse_path);
|
||||
- todo_wine ok(bret, "Failed to delete symlink as a file!\n");
|
||||
+ ok(bret, "Failed to delete symlink as a file!\n");
|
||||
|
||||
/* Create a blank slate for directory symlink tests */
|
||||
bret = CreateDirectoryW(reparse_path, NULL);
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index 4fe8be31ab3..c12677e7615 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -1914,6 +1914,55 @@ int check_symlink( char *name )
|
||||
return S_ISLNK( st.st_mode );
|
||||
}
|
||||
|
||||
+static void decode_symlink(char *name, int *is_dir)
|
||||
+{
|
||||
+ char link[MAX_PATH], *p;
|
||||
+ ULONG reparse_tag;
|
||||
+ int len, i;
|
||||
+
|
||||
+ len = readlink( name, link, sizeof(link) );
|
||||
+ if (len == -1)
|
||||
+ return;
|
||||
+ link[len] = 0;
|
||||
+ p = link;
|
||||
+ /* skip past relative/absolute indication */
|
||||
+ if (*p == '.')
|
||||
+ p++;
|
||||
+ if (*p++ != '/')
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+ /* decode the reparse tag */
|
||||
+ reparse_tag = 0;
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+ int val;
|
||||
+
|
||||
+ if (c == '/')
|
||||
+ val = 0;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ val = 1;
|
||||
+ else
|
||||
+ return;
|
||||
+ reparse_tag |= (val << i);
|
||||
+ }
|
||||
+ /* decode the directory/file flag */
|
||||
+ if (reparse_tag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ {
|
||||
+ char c = *p++;
|
||||
+
|
||||
+ if (c == '/')
|
||||
+ *is_dir = FALSE;
|
||||
+ else if (c == '.' && *p++ == '/')
|
||||
+ *is_dir = TRUE;
|
||||
+ else
|
||||
+ return;
|
||||
+ }
|
||||
+ else
|
||||
+ *is_dir = TRUE;
|
||||
+}
|
||||
+
|
||||
/* open() wrapper that returns a struct fd with no fd user set */
|
||||
struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_name,
|
||||
int flags, mode_t *mode, unsigned int access,
|
||||
@@ -2016,6 +2065,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -2030,16 +2080,20 @@ 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;
|
||||
|
||||
+ /* decode symlink type */
|
||||
fstat( fd->unix_fd, &st );
|
||||
*mode = st.st_mode;
|
||||
+ is_dir = S_ISDIR(st.st_mode);
|
||||
+ if (is_link)
|
||||
+ decode_symlink(fd->unlink_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;
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From c778a312bce6674c23fffb06b82b36fcd8e1ff15 Mon Sep 17 00:00:00 2001
|
||||
From fa2bfaa979927ffa57400dea9e5c86f72f680284 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 29 May 2019 15:11:42 -0600
|
||||
Subject: kernel32: Add reparse support to FindNextFile.
|
||||
Subject: [PATCH] kernel32: Add reparse support to FindNextFile.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
@ -9,10 +9,10 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index e44a5755460..b3926ade9d5 100644
|
||||
index 736589e9a5f..47b10d17acc 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -1499,6 +1499,30 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da
|
||||
@@ -1508,6 +1508,30 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *da
|
||||
memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength );
|
||||
data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0;
|
||||
|
||||
@ -44,5 +44,5 @@ index e44a5755460..b3926ade9d5 100644
|
||||
{
|
||||
memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength );
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -1,34 +0,0 @@
|
||||
From 37ad149d47e7807e1a02d8b1e4e65ac51f84d8ae Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 12 Dec 2020 17:40:27 -0700
|
||||
Subject: server: Properly handle deleting dangling symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
server/fd.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index c12677e7615..b048568e85f 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -2033,6 +2033,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
#endif
|
||||
fd->unlink_name = path;
|
||||
fd->unix_name = realpath( path, NULL );
|
||||
+ if (!fd->unix_name) fd->unix_name = dup_fd_name( root, name ); /* dangling symlink */
|
||||
}
|
||||
|
||||
if ((fd->unix_fd = open( name, rw_mode | (flags & ~O_TRUNC), *mode )) == -1)
|
||||
@@ -2590,7 +2591,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.17.1
|
||||
|
@ -1,26 +0,0 @@
|
||||
From cba12ea98f0f7bf8fb133042f2d866c6d21c74af 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: kernelbase: Use FILE_OPEN_REPARSE_POINT in RemoveDirectory[A|W].
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernelbase/file.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index a9e05be62da..904a4f0979f 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -3487,7 +3487,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)
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 3eee2d48450f815b03f31f4b7c16accfbb8607b6 Mon Sep 17 00:00:00 2001
|
||||
From 5625bfea146c3845a6678dc3c4de49b1850e90b1 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 29 May 2019 15:18:50 -0600
|
||||
Subject: wcmd: Display reparse point type in directory listings.
|
||||
Subject: [PATCH] wcmd: Display reparse point type in directory listings.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
@ -9,7 +9,7 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
1 file changed, 26 insertions(+)
|
||||
|
||||
diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
|
||||
index 24b18bfa81b..40554aed368 100644
|
||||
index 8417687939a..a849807c76e 100644
|
||||
--- a/programs/cmd/directory.c
|
||||
+++ b/programs/cmd/directory.c
|
||||
@@ -395,6 +395,32 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
|
||||
@ -46,5 +46,5 @@ index 24b18bfa81b..40554aed368 100644
|
||||
dir_count++;
|
||||
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -1,111 +0,0 @@
|
||||
From 397ab90ccdf2d0fb0492955d2544fde31f2b7204 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 1 May 2019 12:06:20 -0600
|
||||
Subject: ntdll: Always report symbolic links as containing zero bytes.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 45 +++++++++++++++++++++++++++++++++++++++--
|
||||
dlls/ntdll/unix/file.c | 2 ++
|
||||
2 files changed, 45 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index 9fe806ff3cc..ebed2600920 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5388,6 +5388,7 @@ static void test_reparse_points(void)
|
||||
static const WCHAR dotW[] = {'.',0};
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
+ WIN32_FILE_ATTRIBUTE_DATA fad;
|
||||
WCHAR buf[] = {0,0,0,0};
|
||||
HANDLE handle, token;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
@@ -5527,8 +5528,6 @@ static void test_reparse_points(void)
|
||||
"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);
|
||||
@@ -5553,6 +5552,13 @@ static void test_reparse_points(void)
|
||||
/* Delete the junction point directory and create a blank slate for symlink tests */
|
||||
bret = RemoveDirectoryW(reparse_path);
|
||||
ok(bret, "Failed to delete junction point!\n");
|
||||
+ 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 (%d vs %d)\n",
|
||||
+ GetFileSize(handle, NULL), sizeof(fooW));
|
||||
+ CloseHandle(handle);
|
||||
|
||||
/* Create the file symlink */
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
@@ -5566,6 +5572,37 @@ static void test_reparse_points(void)
|
||||
ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* Check the size of the symlink */
|
||||
+ bret = GetFileAttributesExW(reparse_path, GetFileExInfoStandard, &fad);
|
||||
+ ok(bret, "Failed to read file attributes from the symlink target.\n");
|
||||
+ ok(fad.nFileSizeLow == 0 && fad.nFileSizeHigh == 0, "Size of symlink is not zero.\n");
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to open symlink file.\n");
|
||||
+ todo_wine ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
+ bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
+ todo_wine ok(bret, "Failed to read data from the symlink.\n");
|
||||
+ ok(dwLen == 0, "Length of symlink data is not zero.\n");
|
||||
+ CloseHandle(handle);
|
||||
+
|
||||
+ /* Check the size/data of the symlink target */
|
||||
+ handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
+ FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
+ if (handle == INVALID_HANDLE_VALUE)
|
||||
+ {
|
||||
+ win_skip("Failed to open symlink file handle (0x%x).\n", GetLastError());
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ ok(GetFileSize(handle, NULL) == sizeof(fooW), "symlink target size does not match (%d != %d)\n",
|
||||
+ GetFileSize(handle, NULL), sizeof(fooW));
|
||||
+ bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
+ ok(bret, "Failed to read data from the symlink.\n");
|
||||
+ ok(dwLen == sizeof(fooW), "Length of symlink target data does not match (%d != %d).\n",
|
||||
+ dwLen, 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 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);
|
||||
@@ -5605,6 +5642,10 @@ static void test_reparse_points(void)
|
||||
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);
|
||||
+ 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);
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 364bda14b4b..42ae14851fa 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1690,6 +1690,8 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
|
||||
|
||||
/* return information about the destination (unless this is a dangling symlink) */
|
||||
stat( path, st );
|
||||
+ /* symbolic links always report size 0 */
|
||||
+ st->st_size = 0;
|
||||
/* 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 */
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From f6b36e58a07343ba7056873f82da376eb8895557 Mon Sep 17 00:00:00 2001
|
||||
From b488b1a3509afa3265d9c49bf7cc8dff4590c430 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 29 May 2019 15:38:30 -0600
|
||||
Subject: wcmd: Show reparse point target in directory listing.
|
||||
Subject: [PATCH] wcmd: Show reparse point target in directory listing.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
@ -9,7 +9,7 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
1 file changed, 35 insertions(+)
|
||||
|
||||
diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
|
||||
index 40554aed368..0618ffec8d1 100644
|
||||
index a849807c76e..5b0a19b442d 100644
|
||||
--- a/programs/cmd/directory.c
|
||||
+++ b/programs/cmd/directory.c
|
||||
@@ -23,6 +23,8 @@
|
||||
@ -62,5 +62,5 @@ index 40554aed368..0618ffec8d1 100644
|
||||
if (!((lstrcmpW(fd[i].cFileName, L".") == 0) ||
|
||||
(lstrcmpW(fd[i].cFileName, L"..") == 0))) {
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -1,7 +1,7 @@
|
||||
From f9d43a6e8272e1be5c375a88e0f9b7aadf552213 Mon Sep 17 00:00:00 2001
|
||||
From 2dd44b98c47ecbff362b77ddacd7ff23a541fca7 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Wed, 29 May 2019 16:01:45 -0600
|
||||
Subject: wcmd: Add junction point support to mklink.
|
||||
Subject: [PATCH] wcmd: Add junction point support to mklink.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
@ -9,7 +9,7 @@ Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
1 file changed, 47 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
|
||||
index 29e1e6a5767..a34cfdbe553 100644
|
||||
index dd3ae5b509b..53734b1e940 100644
|
||||
--- a/programs/cmd/builtins.c
|
||||
+++ b/programs/cmd/builtins.c
|
||||
@@ -31,6 +31,9 @@
|
||||
@ -22,7 +22,7 @@ index 29e1e6a5767..a34cfdbe553 100644
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
|
||||
|
||||
@@ -4970,6 +4973,49 @@ void WCMD_color (void) {
|
||||
@@ -4997,6 +5000,49 @@ void WCMD_color (void) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ index 29e1e6a5767..a34cfdbe553 100644
|
||||
/****************************************************************************
|
||||
* WCMD_mklink
|
||||
*/
|
||||
@@ -5018,7 +5064,7 @@ void WCMD_mklink(WCHAR *args)
|
||||
@@ -5045,7 +5091,7 @@ void WCMD_mklink(WCHAR *args)
|
||||
else if(!junction)
|
||||
ret = CreateSymbolicLinkW(file1, file2, isdir);
|
||||
else
|
||||
@ -82,5 +82,5 @@ index 29e1e6a5767..a34cfdbe553 100644
|
||||
if(!ret)
|
||||
WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), file1);
|
||||
--
|
||||
2.17.1
|
||||
2.35.1
|
||||
|
@ -0,0 +1,165 @@
|
||||
From 489bbd44a182accd32dd26788ff19d0a439f49fd Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Mon, 3 May 2021 09:28:08 -0600
|
||||
Subject: [PATCH] ntdll: Add support for creating Unix/Linux symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 35 ++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 48 +++++++++++++++++++++++++++--------------
|
||||
include/ntifs.h | 4 ++++
|
||||
include/winnt.h | 1 +
|
||||
4 files changed, 72 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index a84abd66106..f8f5e7f1ace 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5398,6 +5398,8 @@ static void test_reparse_points(void)
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
WIN32_FILE_ATTRIBUTE_DATA fad;
|
||||
+ char unix_target[] = "target";
|
||||
+ UCHAR *unix_dest;
|
||||
WCHAR buf[] = {0,0,0,0};
|
||||
HANDLE handle, token;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
@@ -5762,6 +5764,39 @@ static void test_reparse_points(void)
|
||||
"Symlink folder's access time does not match.\n");
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* Create a Unix/Linux symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ RemoveDirectoryW(reparse_path);
|
||||
+ bret = CreateDirectoryW(reparse_path, NULL);
|
||||
+ 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);
|
||||
+ path_len = strlen(unix_target);
|
||||
+ buffer_len = offsetof(REPARSE_DATA_BUFFER, LinuxSymbolicLinkReparseBuffer.PathBuffer[path_len]);
|
||||
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
|
||||
+ buffer->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
+ buffer->ReparseDataLength = sizeof(ULONG) + path_len;
|
||||
+ memcpy(buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer, unix_target, path_len);
|
||||
+ 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());
|
||||
+
|
||||
+ /* Delete the symlink */
|
||||
+ memset(&guid_buffer, 0x00, sizeof(guid_buffer));
|
||||
+ guid_buffer.ReparseTag = IO_REPARSE_TAG_LX_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());
|
||||
+ CloseHandle(handle);
|
||||
+ RemoveDirectoryW(reparse_path);
|
||||
+ DeleteFileW(reparse_path);
|
||||
+ CreateDirectoryW(reparse_path, NULL);
|
||||
+
|
||||
/* Create a relative directory symlink */
|
||||
HeapFree(GetProcessHeap(), 0, buffer);
|
||||
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index d5d62f3ba94..d495051a75a 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3729,20 +3729,33 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
* *) Append the base64-url encoded reparse point buffer
|
||||
* *) Append the filename of the first continuing symlink (0) in case we need it
|
||||
*/
|
||||
- strcpy( target_path, ".REPARSE_POINT/" );
|
||||
- strcat( target_path, filename );
|
||||
- strcat( target_path, "/" );
|
||||
- if (is_dir)
|
||||
- strcat( target_path, "." );
|
||||
- strcat( target_path, "/" );
|
||||
- i = 0;
|
||||
- for (depth=0; i<encoded_len && strlen(target_path)<SYM_MAX-2; i+=chunk_len, depth++)
|
||||
+ if (buffer->ReparseTag != IO_REPARSE_TAG_LX_SYMLINK)
|
||||
{
|
||||
- chunk_len = min(NAME_MAX, SYM_MAX-2-strlen(target_path));
|
||||
- strncat( target_path, &encoded[i], chunk_len );
|
||||
+ strcpy( target_path, ".REPARSE_POINT/" );
|
||||
+ strcat( target_path, filename );
|
||||
strcat( target_path, "/" );
|
||||
+ if (is_dir)
|
||||
+ strcat( target_path, "." );
|
||||
+ strcat( target_path, "/" );
|
||||
+ for (depth=0; i<encoded_len && strlen(target_path)<SYM_MAX-2; i+=chunk_len, depth++)
|
||||
+ {
|
||||
+ chunk_len = min(NAME_MAX, SYM_MAX-2-strlen(target_path));
|
||||
+ strncat( target_path, &encoded[i], chunk_len );
|
||||
+ strcat( target_path, "/" );
|
||||
+ }
|
||||
+ strcat( target_path, itoa(j) );
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ int unix_dest_len;
|
||||
+ char *unix_dest;
|
||||
+
|
||||
+ unix_dest_len = buffer->ReparseDataLength - sizeof(ULONG);
|
||||
+ unix_dest = (char *) &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ memcpy( target_path, unix_dest, unix_dest_len );
|
||||
+ target_path[unix_dest_len] = 0;
|
||||
+ i = encoded_len; /* no extended metadata to store */
|
||||
}
|
||||
- strcat( target_path, itoa(j) );
|
||||
|
||||
/* Produce the link in a temporary location in the same folder */
|
||||
strcpy( tmpdir, unix_src );
|
||||
@@ -3804,11 +3817,14 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
}
|
||||
|
||||
/* create the very last link directory */
|
||||
- strcpy( link_path, target_path );
|
||||
- strcpy( link_dir, link_path );
|
||||
- link_dir[strlen(link_dir)-1] = 0;
|
||||
- if (mkdir_p( link_dir, 0777) == 0)
|
||||
- create_reparse_target( unix_src, depth + 2, link_path, buffer );
|
||||
+ if (buffer->ReparseTag != IO_REPARSE_TAG_LX_SYMLINK)
|
||||
+ {
|
||||
+ strcpy( link_path, target_path );
|
||||
+ strcpy( link_dir, link_path );
|
||||
+ link_dir[strlen(link_dir)-1] = 0;
|
||||
+ if (mkdir_p( link_dir, 0777) == 0)
|
||||
+ create_reparse_target( unix_src, depth + 2, link_path, buffer );
|
||||
+ }
|
||||
|
||||
/* revert to the original directory */
|
||||
chdir( original_dir );
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
index 0d02225bc4f..25af12a413a 100644
|
||||
--- a/include/ntifs.h
|
||||
+++ b/include/ntifs.h
|
||||
@@ -42,6 +42,10 @@ typedef struct _REPARSE_DATA_BUFFER {
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
+ struct {
|
||||
+ ULONG Version;
|
||||
+ UCHAR PathBuffer[1];
|
||||
+ } LinuxSymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
diff --git a/include/winnt.h b/include/winnt.h
|
||||
index 76aee026d84..96cab40201d 100644
|
||||
--- a/include/winnt.h
|
||||
+++ b/include/winnt.h
|
||||
@@ -2324,6 +2324,7 @@ extern struct _TEB * WINAPI NtCurrentTeb(void);
|
||||
#define IO_REPARSE_TAG_CLOUD_MASK __MSABI_LONG(0x0000F000)
|
||||
#define IO_REPARSE_TAG_APPEXECLINK __MSABI_LONG(0x8000001B)
|
||||
#define IO_REPARSE_TAG_GVFS __MSABI_LONG(0x9000001C)
|
||||
+#define IO_REPARSE_TAG_LX_SYMLINK __MSABI_LONG(0xA000001D)
|
||||
#define IO_REPARSE_TAG_STORAGE_SYNC __MSABI_LONG(0x8000001E)
|
||||
#define IO_REPARSE_TAG_WCI_TOMBSTONE __MSABI_LONG(0xA000001F)
|
||||
#define IO_REPARSE_TAG_UNHANDLED __MSABI_LONG(0x80000020)
|
||||
--
|
||||
2.35.1
|
||||
|
@ -1,140 +0,0 @@
|
||||
From c45352af86bac8dd17109990d94129ec3a14b4c4 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Sat, 18 Jul 2020 09:13:29 -0600
|
||||
Subject: server: Fix obtaining information about a symlink.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 19 ++++++++++---------
|
||||
server/fd.c | 7 ++++---
|
||||
server/protocol.def | 1 +
|
||||
3 files changed, 15 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index cd7fca79d49..49412c99d3c 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1982,7 +1982,7 @@ static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr,
|
||||
}
|
||||
|
||||
|
||||
-static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name )
|
||||
+static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name, BOOL nofollow )
|
||||
{
|
||||
data_size_t size = 1024;
|
||||
NTSTATUS ret;
|
||||
@@ -1995,6 +1995,7 @@ static NTSTATUS server_get_unix_name( HANDLE handle, char **unix_name )
|
||||
SERVER_START_REQ( get_handle_unix_name )
|
||||
{
|
||||
req->handle = wine_server_obj_handle( handle );
|
||||
+ req->nofollow = nofollow;
|
||||
wine_server_set_reply( req, name, size );
|
||||
ret = wine_server_call( req );
|
||||
size = reply->name_len;
|
||||
@@ -2190,7 +2191,7 @@ static NTSTATUS get_mountmgr_fs_info( HANDLE handle, int fd, struct mountmgr_uni
|
||||
NTSTATUS status;
|
||||
int letter;
|
||||
|
||||
- if ((status = server_get_unix_name( handle, &unix_name ))) return status;
|
||||
+ if ((status = server_get_unix_name( handle, &unix_name, FALSE ))) return status;
|
||||
letter = find_dos_device( unix_name );
|
||||
free( unix_name );
|
||||
|
||||
@@ -4424,7 +4425,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
|
||||
if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno );
|
||||
else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
|
||||
status = STATUS_INVALID_INFO_CLASS;
|
||||
- else if (!(status = server_get_unix_name( handle, &unix_name )))
|
||||
+ else if (!(status = server_get_unix_name( handle, &unix_name, FALSE )))
|
||||
{
|
||||
LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
|
||||
|
||||
@@ -4485,7 +4486,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
|
||||
FILE_NAME_INFORMATION *info = ptr;
|
||||
char *unix_name;
|
||||
|
||||
- if (!(status = server_get_unix_name( handle, &unix_name )))
|
||||
+ if (!(status = server_get_unix_name( handle, &unix_name, FALSE )))
|
||||
{
|
||||
LONG name_len = len - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
|
||||
status = fill_name_info( unix_name, info, &name_len );
|
||||
@@ -4499,7 +4500,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io,
|
||||
FILE_NETWORK_OPEN_INFORMATION *info = ptr;
|
||||
char *unix_name;
|
||||
|
||||
- if (!(status = server_get_unix_name( handle, &unix_name )))
|
||||
+ if (!(status = server_get_unix_name( handle, &unix_name, FALSE )))
|
||||
{
|
||||
ULONG attributes;
|
||||
struct stat st;
|
||||
@@ -6048,7 +6049,7 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
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 )))
|
||||
+ if ((status = server_get_unix_name( handle, &unix_src, FALSE )))
|
||||
goto cleanup;
|
||||
src_allocated = TRUE;
|
||||
if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
@@ -6330,7 +6331,7 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
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 )))
|
||||
+ if ((status = server_get_unix_name( handle, &unix_src, TRUE )))
|
||||
goto cleanup;
|
||||
|
||||
if ((status = get_symlink_properties( unix_src, unix_dest, &unix_dest_len, &buffer->ReparseTag,
|
||||
@@ -6449,7 +6450,7 @@ NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
|
||||
if ((status = server_get_unix_fd( handle, FILE_SPECIAL_ACCESS, &dest_fd, &needs_close, NULL, NULL )))
|
||||
return status;
|
||||
|
||||
- if ((status = server_get_unix_name( handle, &unix_name )))
|
||||
+ if ((status = server_get_unix_name( handle, &unix_name, TRUE )))
|
||||
goto cleanup;
|
||||
|
||||
TRACE( "Deleting symlink %s\n", unix_name );
|
||||
@@ -7559,7 +7560,7 @@ NTSTATUS WINAPI NtQueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS info_clas
|
||||
|
||||
/* first try as a file object */
|
||||
|
||||
- if (!(status = server_get_unix_name( handle, &unix_name )))
|
||||
+ if (!(status = server_get_unix_name( handle, &unix_name, FALSE )))
|
||||
{
|
||||
if (!(status = unix_to_nt_file_name( unix_name, &nt_name )))
|
||||
{
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index b048568e85f..a82594f8513 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -2876,11 +2876,12 @@ DECL_HANDLER(get_handle_unix_name)
|
||||
|
||||
if ((fd = get_handle_fd_obj( current->process, req->handle, 0 )))
|
||||
{
|
||||
- if (fd->unix_name)
|
||||
+ char *name = (req->nofollow ? fd->unlink_name : fd->unix_name);
|
||||
+ if (name)
|
||||
{
|
||||
- data_size_t name_len = strlen( fd->unix_name );
|
||||
+ data_size_t name_len = strlen( name );
|
||||
reply->name_len = name_len;
|
||||
- if (name_len <= get_reply_max_size()) set_reply_data( fd->unix_name, name_len );
|
||||
+ if (name_len <= get_reply_max_size()) set_reply_data( name, name_len );
|
||||
else set_error( STATUS_BUFFER_OVERFLOW );
|
||||
}
|
||||
else set_error( STATUS_OBJECT_TYPE_MISMATCH );
|
||||
diff --git a/server/protocol.def b/server/protocol.def
|
||||
index c83e6a2ef7c..385420eedda 100644
|
||||
--- a/server/protocol.def
|
||||
+++ b/server/protocol.def
|
||||
@@ -1346,6 +1346,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT };
|
||||
/* Get the Unix name from a file handle */
|
||||
@REQ(get_handle_unix_name)
|
||||
obj_handle_t handle; /* file handle */
|
||||
+ int nofollow; /* do not follow symlinks (return the link) */
|
||||
@REPLY
|
||||
data_size_t name_len; /* unix name length */
|
||||
VARARG(name,string); /* unix name */
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,67 @@
|
||||
From 3635077a0b682350123e9e2e9627dbdd973191e9 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:32:44 -0700
|
||||
Subject: [PATCH] ntdll: Report regular Unix symlinks as WSL Linux/Unix
|
||||
symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 18 ++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 12 +++++++++++-
|
||||
2 files changed, 29 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index f8f5e7f1ace..f0d80f4639b 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5786,6 +5786,24 @@ 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 symlink! (0x%lx)\n", GetLastError());
|
||||
|
||||
+ /* Read back the Unix/Linux 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)
|
||||
+ 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 symlink!\n");
|
||||
+ string_len = buffer->ReparseDataLength - sizeof(ULONG);
|
||||
+ unix_dest = &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ ok((memcmp(unix_dest, unix_target, string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
|
||||
+ unix_dest, unix_target);
|
||||
+ total_len = FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[path_len])
|
||||
+ - 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(&guid_buffer, 0x00, sizeof(guid_buffer));
|
||||
guid_buffer.ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index d495051a75a..6310c4b414b 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3896,7 +3896,17 @@ NTSTATUS get_reparse_point_unix(const char *unix_src, REPARSE_DATA_BUFFER *buffe
|
||||
link_path[link_path_len] = 0;
|
||||
if (strncmp( link_path, ".REPARSE_POINT/", 15) != 0)
|
||||
{
|
||||
- status = STATUS_NOT_IMPLEMENTED;
|
||||
+ /* treat regular Unix symlinks as WSL Linux/Unix symlinks */
|
||||
+ int max_length, total_len;
|
||||
+
|
||||
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[0]);
|
||||
+ if (link_path_len > max_length) { status = STATUS_BUFFER_TOO_SMALL; goto cleanup; }
|
||||
+ buffer->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
+ buffer->LinuxSymbolicLinkReparseBuffer.Version = 2;
|
||||
+ memcpy( &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[0], link_path, link_path_len );
|
||||
+ total_len = FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[link_path_len]);
|
||||
+ buffer->ReparseDataLength = total_len - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
|
||||
+ status = STATUS_SUCCESS;
|
||||
goto cleanup;
|
||||
}
|
||||
encoded_len = link_path_len;
|
||||
--
|
||||
2.35.1
|
||||
|
@ -0,0 +1,68 @@
|
||||
From e21b5f472423dd2cd6d43421d8f3c94d30e8ff3f Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:16:17 -0700
|
||||
Subject: [PATCH] ntdll: Add an intermediary symlink in reparse point metadata
|
||||
pointing to prefix.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 27 +++++++++++++++++++++++++++
|
||||
1 file changed, 27 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 6310c4b414b..12df28a516e 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3556,6 +3556,18 @@ static NTSTATUS get_reparse_target( UNICODE_STRING *nt_target, REPARSE_DATA_BUFF
|
||||
}
|
||||
|
||||
|
||||
+int find_prefix_end( const char *path, int *offset )
|
||||
+{
|
||||
+ static int config_dir_len = 0;
|
||||
+
|
||||
+ if (!config_dir_len) config_dir_len = strlen(config_dir);
|
||||
+ if (path[config_dir_len] != '/') return FALSE;
|
||||
+ if (strncmp( config_dir, path, config_dir_len ) != 0) return FALSE;
|
||||
+ *offset = config_dir_len;
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/* add a symlink to the unix target at the last point of the reparse point metadata */
|
||||
NTSTATUS create_reparse_target( const char *unix_src, int depth, const char *link_path,
|
||||
REPARSE_DATA_BUFFER *buffer )
|
||||
@@ -3650,6 +3662,8 @@ NTSTATUS create_reparse_target( const char *unix_src, int depth, const char *lin
|
||||
/* create the symlink to the target at the last metadata location */
|
||||
if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
|
||||
{
|
||||
+ const char prefix_string[] = "${WINEPREFIX}";
|
||||
+ int append_prefix = FALSE;
|
||||
int relative_offset;
|
||||
|
||||
target_path[0] = 0;
|
||||
@@ -3659,8 +3673,21 @@ NTSTATUS create_reparse_target( const char *unix_src, int depth, const char *lin
|
||||
relative_offset = 0;
|
||||
is_relative = FALSE;
|
||||
}
|
||||
+ else if (find_prefix_end( unix_target, &relative_offset ))
|
||||
+ {
|
||||
+ char prefix_link[MAX_PATH];
|
||||
+
|
||||
+ append_prefix = TRUE;
|
||||
+ is_relative = FALSE;
|
||||
+ strcpy( prefix_link, link_path );
|
||||
+ prefix_link[strlen(prefix_link)-1] = 0;
|
||||
+ strcat( prefix_link, prefix_string );
|
||||
+ symlink( config_dir, prefix_link );
|
||||
+ }
|
||||
for (;is_relative && depth > 0; depth--)
|
||||
strcat( target_path, "../" );
|
||||
+ if (append_prefix)
|
||||
+ strcat( target_path, prefix_string );
|
||||
strcat( target_path, &unix_target[relative_offset] );
|
||||
TRACE( "adding reparse point target: %s\n", target_path );
|
||||
symlink( target_path, link_path );
|
||||
--
|
||||
2.35.1
|
||||
|
@ -1,102 +0,0 @@
|
||||
From e52191dc584a752128f9c945e5527f6a5b7de369 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Wed, 25 Nov 2020 09:05:51 -0700
|
||||
Subject: ntdll: Support reparse point properties in fd_get_file_info.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 2 +-
|
||||
dlls/ntdll/unix/file.c | 26 +++++++++++++++++++-------
|
||||
2 files changed, 20 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index cb9a9506aba..cde3b86ade2 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5579,7 +5579,7 @@ static void test_reparse_points(void)
|
||||
handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
ok(handle != INVALID_HANDLE_VALUE, "Failed to open symlink file.\n");
|
||||
- todo_wine ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
+ ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
|
||||
bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
|
||||
ok(bret, "Failed to read data from the symlink.\n");
|
||||
ok(dwLen == 0, "Length of symlink data is not zero.\n");
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index f7ff9f4b5ed..75194023e69 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -1599,7 +1599,7 @@ static inline int get_file_xattr( char *hexattr, int attrlen )
|
||||
return 0;
|
||||
}
|
||||
|
||||
-NTSTATUS get_symlink_properties(const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
+NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
DWORD *tag, ULONG *flags, BOOL *is_dir);
|
||||
|
||||
/* fetch the attributes of a file */
|
||||
@@ -1633,10 +1633,22 @@ 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;
|
||||
+ /* symbolic links always report size 0 */
|
||||
+ st->st_size = 0;
|
||||
+ if (get_symlink_properties( fd, "", NULL, NULL, NULL, NULL, &is_dir ) == STATUS_SUCCESS)
|
||||
+ st->st_mode = (st->st_mode & ~S_IFMT) | (is_dir ? S_IFDIR : S_IFREG);
|
||||
+ }
|
||||
+ *attr |= get_file_attributes( st );
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1695,7 +1707,7 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
|
||||
/* 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 (get_symlink_properties( path, NULL, NULL, NULL, NULL, &is_dir ) == STATUS_SUCCESS)
|
||||
+ if (get_symlink_properties( AT_FDCWD, path, NULL, NULL, NULL, NULL, &is_dir ) == STATUS_SUCCESS)
|
||||
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 )))
|
||||
@@ -6231,7 +6243,7 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
-NTSTATUS get_symlink_properties(const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
+NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
DWORD *tag, ULONG *flags, BOOL *is_dir)
|
||||
{
|
||||
int len = MAX_PATH;
|
||||
@@ -6247,7 +6259,7 @@ NTSTATUS get_symlink_properties(const char *unix_src, char *unix_dest, int *unix
|
||||
tmp = malloc( len );
|
||||
else
|
||||
tmp = unix_dest;
|
||||
- if ((ret = readlink( unix_src, tmp, len )) < 0)
|
||||
+ if ((ret = readlinkat( fd, unix_src, tmp, len )) < 0)
|
||||
{
|
||||
status = errno_to_status( errno );
|
||||
goto cleanup;
|
||||
@@ -6339,8 +6351,8 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
if ((status = server_get_unix_name( handle, &unix_src, TRUE )))
|
||||
goto cleanup;
|
||||
|
||||
- if ((status = get_symlink_properties( unix_src, unix_dest, &unix_dest_len, &buffer->ReparseTag,
|
||||
- &flags, NULL )))
|
||||
+ if ((status = get_symlink_properties( AT_FDCWD, unix_src, unix_dest, &unix_dest_len,
|
||||
+ &buffer->ReparseTag, &flags, NULL )))
|
||||
goto cleanup;
|
||||
|
||||
/* convert the relative path into an absolute path */
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,45 @@
|
||||
From 3abdca9dc957d524aa00ccd518aaa2a41a348582 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:15:03 -0700
|
||||
Subject: [PATCH] ntdll: Rewrite reparse point prefix symlink if the prefix
|
||||
location changes.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 13 +++++++++++++
|
||||
1 file changed, 13 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 12df28a516e..75153ae7620 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3903,6 +3903,7 @@ cleanup:
|
||||
NTSTATUS get_reparse_point_unix(const char *unix_src, REPARSE_DATA_BUFFER *buffer, ULONG *size)
|
||||
{
|
||||
char link_dir[PATH_MAX], original_dir[PATH_MAX], *d;
|
||||
+ const char prefix_string[] = "${WINEPREFIX}";
|
||||
int link_path_len, buffer_len, encoded_len;
|
||||
char link_path[PATH_MAX];
|
||||
REPARSE_DATA_BUFFER header;
|
||||
@@ -4008,6 +4009,18 @@ NTSTATUS get_reparse_point_unix(const char *unix_src, REPARSE_DATA_BUFFER *buffe
|
||||
chdir( link_dir );
|
||||
}
|
||||
|
||||
+ /* if the prefix location has moved then update the Unix prefix passthrough link */
|
||||
+ strcpy( link_dir, link_path );
|
||||
+ link_dir[strlen(link_dir)-1] = 0;
|
||||
+ chdir( link_dir );
|
||||
+ link_path_len = readlink( prefix_string, link_path, sizeof(link_path) );
|
||||
+ if (link_path_len > 0) link_path[link_path_len] = 0;
|
||||
+ if (link_path_len > 0 && strcmp( config_dir, link_path) != 0)
|
||||
+ {
|
||||
+ unlink( prefix_string );
|
||||
+ symlink( config_dir, prefix_string );
|
||||
+ }
|
||||
+
|
||||
/* revert to the original directory */
|
||||
chdir( original_dir );
|
||||
|
||||
--
|
||||
2.35.1
|
||||
|
@ -1,44 +0,0 @@
|
||||
From a005fdcb0954856703625dba6085be823b77e137 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 12:17:23 -0700
|
||||
Subject: server: Properly handle renames involving symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
server/fd.c | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index a82594f8513..5cd76f1b0e0 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -2670,7 +2670,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)
|
||||
{
|
||||
@@ -2686,7 +2686,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;
|
||||
@@ -2746,6 +2746,8 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da
|
||||
free( fd->unix_name );
|
||||
fd->closed->unlink_name = fd->unlink_name = 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( NULL, name ); /* dangling symlink */
|
||||
if (!fd->unlink_name || !fd->unix_name)
|
||||
set_error( STATUS_NO_MEMORY );
|
||||
return;
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,26 +0,0 @@
|
||||
From 81ca19d4ab038357e8df6c91fa0aab8dc2114fe5 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: kernelbase: Use FILE_OPEN_REPARSE_POINT in MoveFileWithProgress.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernelbase/file.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index b3926ade9d5..1d793af8bd1 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -2585,7 +2585,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;
|
||||
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,27 +0,0 @@
|
||||
From 2c64ce2bbda215d3a37a6e578d4902d5ec1c54cb 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: kernelbase: Use FILE_OPEN_REPARSE_POINT in DeleteFile.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/kernelbase/file.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
|
||||
index 1d793af8bd1..21c7a59ac13 100644
|
||||
--- a/dlls/kernelbase/file.c
|
||||
+++ b/dlls/kernelbase/file.c
|
||||
@@ -1084,7 +1084,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 );
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,178 +0,0 @@
|
||||
From e4bb20ab33783828a03455ad59d940783f644728 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:32:44 -0700
|
||||
Subject: ntdll: Treat undecoded unix symlinks as WSL Linux/Unix symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 62 ++++++++++++++++++++++++------------------
|
||||
include/ntifs.h | 4 +++
|
||||
include/winnt.h | 1 +
|
||||
3 files changed, 41 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index a01c31413ad..752561931ab 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -6248,9 +6248,10 @@ cleanup:
|
||||
NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, int *unix_dest_len,
|
||||
DWORD *tag, ULONG *flags, BOOL *is_dir)
|
||||
{
|
||||
+ NTSTATUS status = STATUS_SUCCESS;
|
||||
int len = MAX_PATH;
|
||||
+ int decoded = FALSE;
|
||||
DWORD reparse_tag;
|
||||
- NTSTATUS status;
|
||||
BOOL dir_flag;
|
||||
char *p, *tmp;
|
||||
ssize_t ret;
|
||||
@@ -6277,10 +6278,7 @@ NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, i
|
||||
p++;
|
||||
}
|
||||
if (*p++ != '/')
|
||||
- {
|
||||
- status = STATUS_NOT_IMPLEMENTED;
|
||||
- goto cleanup;
|
||||
- }
|
||||
+ goto done;
|
||||
reparse_tag = 0;
|
||||
for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
{
|
||||
@@ -6292,10 +6290,7 @@ NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, i
|
||||
else if (c == '.' && *p++ == '/')
|
||||
val = 1;
|
||||
else
|
||||
- {
|
||||
- status = STATUS_NOT_IMPLEMENTED;
|
||||
- goto cleanup;
|
||||
- }
|
||||
+ goto done;
|
||||
reparse_tag |= (val << i);
|
||||
}
|
||||
/* skip past the directory/file flag */
|
||||
@@ -6308,19 +6303,31 @@ NTSTATUS get_symlink_properties(int fd, const char *unix_src, char *unix_dest, i
|
||||
else if (c == '.' && *p++ == '/')
|
||||
dir_flag = TRUE;
|
||||
else
|
||||
- {
|
||||
- status = STATUS_NOT_IMPLEMENTED;
|
||||
- goto cleanup;
|
||||
- }
|
||||
+ goto done;
|
||||
}
|
||||
else
|
||||
dir_flag = TRUE;
|
||||
+ decoded = TRUE;
|
||||
+
|
||||
+done:
|
||||
+ if (!decoded)
|
||||
+ {
|
||||
+ /* treat undecoded unix symlinks as NT symlinks */
|
||||
+ struct stat st;
|
||||
+
|
||||
+ p = tmp;
|
||||
+ reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
+ if (flags && *p != '/') *flags = SYMLINK_FLAG_RELATIVE;
|
||||
+ if (!fstatat( fd, unix_src, &st, 0 ))
|
||||
+ dir_flag = S_ISDIR(st.st_mode);
|
||||
+ else
|
||||
+ dir_flag = FALSE; /* treat dangling symlinks as files */
|
||||
+ }
|
||||
len -= (p - tmp);
|
||||
if (tag) *tag = reparse_tag;
|
||||
if (is_dir) *is_dir = dir_flag;
|
||||
if (unix_dest) memmove(unix_dest, p, len + 1);
|
||||
if (unix_dest_len) *unix_dest_len = len;
|
||||
- status = STATUS_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
if (!unix_dest) free( tmp );
|
||||
@@ -6334,9 +6341,9 @@ cleanup:
|
||||
*/
|
||||
NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *size)
|
||||
{
|
||||
+ VOID *subst_name = NULL, *print_name = NULL, *unix_name = NULL;
|
||||
INT prefix_len, path_len, total_len;
|
||||
char *unix_src, unix_dest[PATH_MAX];
|
||||
- VOID *subst_name, *print_name;
|
||||
SIZE_T nt_dest_len = PATH_MAX;
|
||||
int unix_dest_len = PATH_MAX;
|
||||
BOOL dest_allocated = FALSE;
|
||||
@@ -6402,6 +6409,7 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
{
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
max_length = out_size-FIELD_OFFSET(typeof(*buffer), MountPointReparseBuffer.PathBuffer[1]);
|
||||
+ if (nt_dest_len > max_length) { status = STATUS_BUFFER_TOO_SMALL; goto cleanup; }
|
||||
path_len = 0;
|
||||
buffer->MountPointReparseBuffer.SubstituteNameOffset = path_len;
|
||||
buffer->MountPointReparseBuffer.SubstituteNameLength = nt_dest_len;
|
||||
@@ -6415,6 +6423,7 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
break;
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
max_length = out_size-FIELD_OFFSET(typeof(*buffer), SymbolicLinkReparseBuffer.PathBuffer[1]);
|
||||
+ if (nt_dest_len > max_length) { status = STATUS_BUFFER_TOO_SMALL; goto cleanup; }
|
||||
path_len = 0;
|
||||
buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset = path_len;
|
||||
buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = nt_dest_len;
|
||||
@@ -6428,19 +6437,20 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
buffer->SymbolicLinkReparseBuffer.Flags = flags;
|
||||
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;
|
||||
+ WARN("unrecognized symbolic link reparse tag: 0x%08x\n", buffer->ReparseTag);
|
||||
+ case IO_REPARSE_TAG_LX_SYMLINK:
|
||||
+ /* report links without a reparse tag as a WSL linux/unix symlink */
|
||||
+ max_length = out_size-FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[0]);
|
||||
+ if (unix_dest_len > max_length) { status = STATUS_BUFFER_TOO_SMALL; goto cleanup; }
|
||||
+ buffer->LinuxSymbolicLinkReparseBuffer.Version = 2;
|
||||
+ unix_name = &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ total_len = FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[unix_dest_len]);
|
||||
+ break;
|
||||
}
|
||||
|
||||
- memcpy( subst_name, nt_dest, nt_dest_len );
|
||||
- memcpy( print_name, &nt_dest[prefix_len], nt_dest_len - prefix_len*sizeof(WCHAR) );
|
||||
+ if (subst_name) memcpy( subst_name, nt_dest, nt_dest_len );
|
||||
+ if (print_name) memcpy( print_name, &nt_dest[prefix_len], nt_dest_len - prefix_len*sizeof(WCHAR) );
|
||||
+ if (unix_name) memcpy( unix_name, unix_dest, unix_dest_len );
|
||||
*size = total_len;
|
||||
buffer->ReparseDataLength = total_len - FIELD_OFFSET(typeof(*buffer), GenericReparseBuffer);
|
||||
status = STATUS_SUCCESS;
|
||||
diff --git a/include/ntifs.h b/include/ntifs.h
|
||||
index 0d02225bc4f..25af12a413a 100644
|
||||
--- a/include/ntifs.h
|
||||
+++ b/include/ntifs.h
|
||||
@@ -42,6 +42,10 @@ typedef struct _REPARSE_DATA_BUFFER {
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
+ struct {
|
||||
+ ULONG Version;
|
||||
+ UCHAR PathBuffer[1];
|
||||
+ } LinuxSymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
diff --git a/include/winnt.h b/include/winnt.h
|
||||
index 425998702d6..1dd4d0109ba 100644
|
||||
--- a/include/winnt.h
|
||||
+++ b/include/winnt.h
|
||||
@@ -2245,6 +2245,7 @@ extern struct _TEB * WINAPI NtCurrentTeb(void);
|
||||
#define IO_REPARSE_TAG_CLOUD_MASK __MSABI_LONG(0x0000F000)
|
||||
#define IO_REPARSE_TAG_APPEXECLINK __MSABI_LONG(0x8000001B)
|
||||
#define IO_REPARSE_TAG_GVFS __MSABI_LONG(0x9000001C)
|
||||
+#define IO_REPARSE_TAG_LX_SYMLINK __MSABI_LONG(0xA000001D)
|
||||
#define IO_REPARSE_TAG_STORAGE_SYNC __MSABI_LONG(0x8000001E)
|
||||
#define IO_REPARSE_TAG_WCI_TOMBSTONE __MSABI_LONG(0xA000001F)
|
||||
#define IO_REPARSE_TAG_UNHANDLED __MSABI_LONG(0x80000020)
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,157 +0,0 @@
|
||||
From 1d139a330669c7d650a95c08872e11da8d9d28f6 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Mon, 3 May 2021 09:28:08 -0600
|
||||
Subject: [PATCH] ntdll: Add support for creating Unix/Linux symlinks.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/tests/file.c | 43 +++++++++++++++++++++++++++++++++++++++++
|
||||
dlls/ntdll/unix/file.c | 30 ++++++++++++++++++++--------
|
||||
2 files changed, 65 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
|
||||
index e4eb36e31b3..6f647ddcca5 100644
|
||||
--- a/dlls/ntdll/tests/file.c
|
||||
+++ b/dlls/ntdll/tests/file.c
|
||||
@@ -5390,7 +5390,9 @@ static void test_reparse_points(void)
|
||||
REPARSE_DATA_BUFFER *buffer = NULL;
|
||||
DWORD dwret, dwLen, dwFlags, err;
|
||||
WIN32_FILE_ATTRIBUTE_DATA fad;
|
||||
+ UCHAR *unix_dest;
|
||||
WCHAR buf[] = {0,0,0,0};
|
||||
+ char name[] = "target";
|
||||
HANDLE handle, token;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
UNICODE_STRING nameW;
|
||||
@@ -5736,6 +5738,47 @@ static void test_reparse_points(void)
|
||||
wine_dbgstr_w(dest), wine_dbgstr_w(&targetW[1]));
|
||||
CloseHandle(handle);
|
||||
|
||||
+ /* Create a Unix/Linux symlink */
|
||||
+ HeapFree(GetProcessHeap(), 0, buffer);
|
||||
+ RemoveDirectoryW(reparse_path);
|
||||
+ bret = CreateDirectoryW(reparse_path, NULL);
|
||||
+ 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);
|
||||
+ path_len = strlen(name);
|
||||
+ buffer_len = offsetof(REPARSE_DATA_BUFFER, LinuxSymbolicLinkReparseBuffer.PathBuffer[path_len]);
|
||||
+ buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len);
|
||||
+ buffer->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||
+ buffer->ReparseDataLength = sizeof(ULONG) + path_len;
|
||||
+ memcpy(buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer, name, path_len);
|
||||
+ 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());
|
||||
+
|
||||
+ /* Read back the Unix/Linux 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)
|
||||
+ 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 symlink!\n");
|
||||
+ string_len = buffer->ReparseDataLength - sizeof(ULONG);
|
||||
+ unix_dest = &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[0];
|
||||
+ ok((memcmp(unix_dest, name, string_len) == 0), "Symlink destination does not match ('%s' != '%s')!\n",
|
||||
+ unix_dest, name);
|
||||
+ total_len = FIELD_OFFSET(typeof(*buffer), LinuxSymbolicLinkReparseBuffer.PathBuffer[path_len])
|
||||
+ - 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 323bf865631..c146fc70639 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -6024,18 +6024,18 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
{
|
||||
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 *unix_src, *unix_dest = NULL, *unix_path = NULL;
|
||||
char tmpdir[PATH_MAX], tmplink[PATH_MAX], *d;
|
||||
SIZE_T unix_dest_len = PATH_MAX;
|
||||
char magic_dest[PATH_MAX];
|
||||
int dest_fd, needs_close;
|
||||
+ int dest_len = 0, offset;
|
||||
int relative_offset = 0;
|
||||
UNICODE_STRING nt_dest;
|
||||
- int dest_len, offset;
|
||||
BOOL is_dir = TRUE;
|
||||
+ WCHAR *dest = NULL;
|
||||
NTSTATUS status;
|
||||
struct stat st;
|
||||
- WCHAR *dest;
|
||||
ULONG flags;
|
||||
int i;
|
||||
|
||||
@@ -6053,6 +6053,12 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
dest = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
flags = buffer->SymbolicLinkReparseBuffer.Flags;
|
||||
break;
|
||||
+ case IO_REPARSE_TAG_LX_SYMLINK:
|
||||
+ offset = 0;
|
||||
+ flags = 0;
|
||||
+ unix_dest_len = buffer->ReparseDataLength - sizeof(ULONG);
|
||||
+ unix_dest = (char *) &buffer->LinuxSymbolicLinkReparseBuffer.PathBuffer[offset];
|
||||
+ break;
|
||||
default:
|
||||
FIXME("stub: FSCTL_SET_REPARSE_POINT(%x)\n", buffer->ReparseTag);
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
@@ -6064,6 +6070,9 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
if ((status = server_get_unix_name( handle, &unix_src, FALSE )))
|
||||
goto cleanup;
|
||||
src_allocated = TRUE;
|
||||
+
|
||||
+ if (unix_dest) goto have_dest;
|
||||
+
|
||||
if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
{
|
||||
ULONG nt_path_len = PATH_MAX, unix_path_len = PATH_MAX;
|
||||
@@ -6139,6 +6148,8 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
|
||||
goto cleanup;
|
||||
dest_allocated = TRUE;
|
||||
+
|
||||
+have_dest:
|
||||
/* check that the source and destination paths are the same up to the relative path */
|
||||
if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
{
|
||||
@@ -6154,14 +6165,17 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
|
||||
/* Encode the reparse tag into the symlink */
|
||||
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 != IO_REPARSE_TAG_LX_SYMLINK)
|
||||
{
|
||||
- if ((buffer->ReparseTag >> i) & 1)
|
||||
+ if (flags == SYMLINK_FLAG_RELATIVE)
|
||||
strcat( magic_dest, "." );
|
||||
strcat( magic_dest, "/" );
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ if ((buffer->ReparseTag >> i) & 1)
|
||||
+ strcat( magic_dest, "." );
|
||||
+ strcat( magic_dest, "/" );
|
||||
+ }
|
||||
}
|
||||
/* Encode the type (file or directory) if NT symlink */
|
||||
if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK)
|
||||
--
|
||||
2.34.1
|
||||
|
@ -1,61 +0,0 @@
|
||||
From 0e5ab82fbcc56e61c6290fed5451ab9d5d3ef997 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:15:46 -0700
|
||||
Subject: ntdll: Strip the wine prefix from reparse point paths external to the
|
||||
prefix.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 29 +++++++++++++++++++++++++++++
|
||||
1 file changed, 29 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 0c2117ca801..19a9584d4b3 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -6023,6 +6023,33 @@ static void ignore_server_ioctl_struct_holes( ULONG code, const void *in_buffer,
|
||||
}
|
||||
|
||||
|
||||
+void strip_external_path( char *path, SIZE_T *len )
|
||||
+{
|
||||
+ static char *unix_root = NULL;
|
||||
+ static int unix_root_len = 0;
|
||||
+
|
||||
+ if (unix_root == NULL)
|
||||
+ {
|
||||
+ OBJECT_ATTRIBUTES attr;
|
||||
+ UNICODE_STRING nameW;
|
||||
+ WCHAR *nt_name;
|
||||
+
|
||||
+ if (unix_to_nt_file_name( "/", &nt_name ) != STATUS_SUCCESS) return;
|
||||
+ nameW.Buffer = nt_name;
|
||||
+ nameW.Length = wcslen(nt_name) * sizeof(WCHAR);
|
||||
+ InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL );
|
||||
+ nt_to_unix_file_name( &attr, &unix_root, FILE_OPEN );
|
||||
+ free( nt_name );
|
||||
+ if (unix_root == NULL) return;
|
||||
+ unix_root_len = strlen(unix_root);
|
||||
+ }
|
||||
+
|
||||
+ if (strncmp( unix_root, path, unix_root_len ) != 0) return;
|
||||
+ *len -= unix_root_len;
|
||||
+ memmove( path, &path[unix_root_len - 1], *len + 1 );
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* 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.
|
||||
@@ -6167,6 +6194,8 @@ have_dest:
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
+ else
|
||||
+ strip_external_path( unix_dest, &unix_dest_len );
|
||||
|
||||
TRACE( "Linking %s to %s\n", unix_src, &unix_dest[relative_offset] );
|
||||
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,56 +0,0 @@
|
||||
From d250faa15d183fa34718a23806853dcd50b9c653 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:16:17 -0700
|
||||
Subject: ntdll: Add a marker to reparse point paths to indicate the prefix
|
||||
location.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 23 +++++++++++++++++++++++
|
||||
1 file changed, 23 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 19a9584d4b3..984d6e46cb9 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -6050,6 +6050,26 @@ void strip_external_path( char *path, SIZE_T *len )
|
||||
}
|
||||
|
||||
|
||||
+char *mark_prefix_end( char *path, SIZE_T *len )
|
||||
+{
|
||||
+ static char marker[] = "////.//.//"; /* "P" (0x50) encoded as a path (0=/ 1=./) */
|
||||
+ int new_path_len = *len + sizeof(marker) - 1;
|
||||
+ static int config_dir_len = 0;
|
||||
+ char *new_path;
|
||||
+
|
||||
+ if (!config_dir_len) config_dir_len = strlen(config_dir);
|
||||
+ if (path[config_dir_len] != '/') return path;
|
||||
+ if (strncmp( config_dir, path, config_dir_len ) != 0) return path;
|
||||
+ if (!(new_path = malloc( new_path_len ))) return path;
|
||||
+ *len = new_path_len;
|
||||
+ strcpy( new_path, config_dir );
|
||||
+ strcat( new_path, marker );
|
||||
+ strcat( new_path, &path[config_dir_len] );
|
||||
+ free( path );
|
||||
+ return new_path;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/*
|
||||
* 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.
|
||||
@@ -6195,7 +6215,10 @@ have_dest:
|
||||
}
|
||||
}
|
||||
else
|
||||
+ {
|
||||
strip_external_path( unix_dest, &unix_dest_len );
|
||||
+ unix_dest = mark_prefix_end( unix_dest, &unix_dest_len );
|
||||
+ }
|
||||
|
||||
TRACE( "Linking %s to %s\n", unix_src, &unix_dest[relative_offset] );
|
||||
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,154 +0,0 @@
|
||||
From 737fd051ea7b6dcba36fff41a3eb78fb1859eaa7 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
|
||||
Date: Sat, 6 Feb 2021 16:15:03 -0700
|
||||
Subject: server: Rewrite absolute reparse point targets if the prefix location
|
||||
changes.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
server/fd.c | 85 +++++++++++++++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 69 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index 5cd76f1b0e0..45a1b226f33 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -1906,23 +1906,16 @@ void get_nt_name( struct fd *fd, struct unicode_str *name )
|
||||
name->len = fd->nt_namelen;
|
||||
}
|
||||
|
||||
-int check_symlink( char *name )
|
||||
+static char *decode_symlink(const char *name, ULONG *tag, int *is_dir)
|
||||
{
|
||||
- struct stat st;
|
||||
-
|
||||
- lstat( name, &st );
|
||||
- return S_ISLNK( st.st_mode );
|
||||
-}
|
||||
-
|
||||
-static void decode_symlink(char *name, int *is_dir)
|
||||
-{
|
||||
- char link[MAX_PATH], *p;
|
||||
+ static char link[MAX_PATH];
|
||||
ULONG reparse_tag;
|
||||
int len, i;
|
||||
+ char *p;
|
||||
|
||||
len = readlink( name, link, sizeof(link) );
|
||||
if (len == -1)
|
||||
- return;
|
||||
+ return NULL;
|
||||
link[len] = 0;
|
||||
p = link;
|
||||
/* skip past relative/absolute indication */
|
||||
@@ -1930,7 +1923,7 @@ static void decode_symlink(char *name, int *is_dir)
|
||||
p++;
|
||||
if (*p++ != '/')
|
||||
{
|
||||
- return;
|
||||
+ return NULL;
|
||||
}
|
||||
/* decode the reparse tag */
|
||||
reparse_tag = 0;
|
||||
@@ -1944,7 +1937,7 @@ static void decode_symlink(char *name, int *is_dir)
|
||||
else if (c == '.' && *p++ == '/')
|
||||
val = 1;
|
||||
else
|
||||
- return;
|
||||
+ return NULL;
|
||||
reparse_tag |= (val << i);
|
||||
}
|
||||
/* decode the directory/file flag */
|
||||
@@ -1957,10 +1950,70 @@ static void decode_symlink(char *name, int *is_dir)
|
||||
else if (c == '.' && *p++ == '/')
|
||||
*is_dir = TRUE;
|
||||
else
|
||||
- return;
|
||||
+ return NULL;
|
||||
}
|
||||
else
|
||||
*is_dir = TRUE;
|
||||
+ if (tag) *tag = reparse_tag;
|
||||
+ return p;
|
||||
+}
|
||||
+
|
||||
+static int rewrite_symlink( const char *path )
|
||||
+{
|
||||
+ static char marker[] = "////.//.//"; /* "P" (0x50) encoded as a path (0=/ 1=./) */
|
||||
+ char *link, *prefix_end, *local_link;
|
||||
+ static char config_dir[MAX_PATH];
|
||||
+ static int config_dir_len = 0;
|
||||
+ char new_target[PATH_MAX];
|
||||
+ int len, is_dir, i;
|
||||
+ ULONG tag;
|
||||
+
|
||||
+ /* obtain the wine prefix path */
|
||||
+ if (!config_dir_len)
|
||||
+ {
|
||||
+ char tmp_dir[MAX_PATH];
|
||||
+
|
||||
+ if (getcwd( tmp_dir, sizeof(tmp_dir) ) == NULL) return FALSE;
|
||||
+ if (fchdir( config_dir_fd ) == -1) return FALSE;
|
||||
+ if (getcwd( config_dir, sizeof(config_dir) ) == NULL) return FALSE;
|
||||
+ if (chdir( tmp_dir ) == -1) return FALSE;
|
||||
+ config_dir_len = strlen( config_dir );
|
||||
+ }
|
||||
+
|
||||
+ /* grab the current link contents */
|
||||
+ link = decode_symlink( path, &tag, &is_dir );
|
||||
+ if (link == NULL) return FALSE;
|
||||
+
|
||||
+ /* find out if the prefix matches, if it does then do not modify the link */
|
||||
+ prefix_end = strstr( link, marker );
|
||||
+ if (prefix_end == NULL) return TRUE;
|
||||
+ local_link = prefix_end + strlen( marker );
|
||||
+ len = prefix_end - link;
|
||||
+ if (len == config_dir_len && strncmp( config_dir, link, len ) == 0) return TRUE;
|
||||
+ /* if the prefix does not match then re-encode the link with the new prefix */
|
||||
+
|
||||
+ /* Encode the reparse tag into the symlink */
|
||||
+ strcpy( new_target, "/" );
|
||||
+ for (i = 0; i < sizeof(ULONG)*8; i++)
|
||||
+ {
|
||||
+ if ((tag >> i) & 1)
|
||||
+ strcat( new_target, "." );
|
||||
+ strcat( new_target, "/" );
|
||||
+ }
|
||||
+ /* Encode the type (file or directory) if NT symlink */
|
||||
+ if (tag == IO_REPARSE_TAG_SYMLINK)
|
||||
+ {
|
||||
+ if (is_dir)
|
||||
+ strcat( new_target, "." );
|
||||
+ strcat( new_target, "/" );
|
||||
+ }
|
||||
+ strcat( new_target, config_dir );
|
||||
+ strcat( new_target, marker );
|
||||
+ strcat( new_target, local_link );
|
||||
+ /* replace the symlink */
|
||||
+ unlink( path );
|
||||
+ symlink( new_target, path );
|
||||
+ return TRUE;
|
||||
}
|
||||
|
||||
/* open() wrapper that returns a struct fd with no fd user set */
|
||||
@@ -2026,7 +2079,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 )))
|
||||
{
|
||||
- int is_symlink = check_symlink( path );
|
||||
+ int is_symlink = rewrite_symlink( path );
|
||||
#if defined(O_SYMLINK)
|
||||
if (is_symlink && (options & FILE_OPEN_REPARSE_POINT) && !(flags & O_CREAT))
|
||||
flags |= O_SYMLINK;
|
||||
@@ -2086,7 +2139,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
*mode = st.st_mode;
|
||||
is_dir = S_ISDIR(st.st_mode);
|
||||
if (is_link)
|
||||
- decode_symlink(fd->unlink_name, &is_dir);
|
||||
+ decode_symlink(fd->unlink_name, NULL, &is_dir);
|
||||
|
||||
/* check directory options */
|
||||
if ((options & FILE_DIRECTORY_FILE) && !is_dir)
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,71 +0,0 @@
|
||||
From 8c6455d6be0cba8dc98a95726d9d289089c2bf67 Mon Sep 17 00:00:00 2001
|
||||
From: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
|
||||
Date: Sat, 5 Mar 2022 11:08:23 +1100
|
||||
Subject: [PATCH] Fix warnings
|
||||
|
||||
These need to be merged into the patches above.
|
||||
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 14 ++++++++------
|
||||
1 file changed, 8 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 787616aa675..ee4f31a1e38 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -6150,16 +6150,14 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
/* re-resolve the unix path for the source */
|
||||
for (;;)
|
||||
{
|
||||
- UNICODE_STRING nt_path_tmp;
|
||||
+ OBJECT_ATTRIBUTES attr;
|
||||
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 );
|
||||
+ status = wine_nt_to_unix_file_name( &attr, unix_path, &unix_path_len, FILE_OPEN_IF );
|
||||
if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
free( unix_path );
|
||||
}
|
||||
@@ -6182,13 +6180,15 @@ NTSTATUS create_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer)
|
||||
/* resolve the NT path of the destination */
|
||||
for (;;)
|
||||
{
|
||||
+ OBJECT_ATTRIBUTES attr;
|
||||
+ ULONG len = unix_dest_len;
|
||||
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, FILE_WINE_PATH );
|
||||
+ status = wine_nt_to_unix_file_name( &attr, unix_dest, &len, FILE_WINE_PATH );
|
||||
if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
free( unix_dest );
|
||||
}
|
||||
@@ -6447,13 +6447,15 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
|
||||
/* resolve the NT path */
|
||||
for (;;)
|
||||
{
|
||||
+ ULONG 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 );
|
||||
+ status = wine_unix_to_nt_file_name( unix_dest, nt_dest, &len );
|
||||
+ nt_dest_len = len;
|
||||
if (status != STATUS_BUFFER_TOO_SMALL) break;
|
||||
free( nt_dest );
|
||||
}
|
||||
--
|
||||
2.34.1
|
||||
|
@ -1735,51 +1735,37 @@ fi
|
||||
# | Modified files:
|
||||
# | * configure.ac, dlls/kernel32/path.c, dlls/kernel32/tests/path.c, dlls/kernelbase/file.c, dlls/mountmgr.sys/device.c,
|
||||
# | dlls/mountmgr.sys/unixlib.c, dlls/mountmgr.sys/unixlib.h, dlls/msvcp120/tests/msvcp120.c,
|
||||
# | dlls/msvcp140/tests/msvcp140.c, dlls/ntdll/tests/file.c, dlls/ntdll/unix/file.c, include/Makefile.in, include/ntifs.h,
|
||||
# | include/winnt.h, include/winternl.h, programs/cmd/builtins.c, programs/cmd/directory.c, server/fd.c, server/file.c,
|
||||
# | server/protocol.def
|
||||
# | dlls/msvcp140/tests/msvcp140.c, dlls/ntdll/Makefile.in, dlls/ntdll/tests/file.c, dlls/ntdll/unix/file.c,
|
||||
# | include/Makefile.in, include/ntifs.h, include/winnt.h, programs/cmd/builtins.c, programs/cmd/directory.c, server/fd.c,
|
||||
# | server/file.c, server/protocol.def
|
||||
# |
|
||||
if test "$enable_ntdll_Junction_Points" -eq 1; then
|
||||
patch_apply ntdll-Junction_Points/0001-ntdll-Add-support-for-junction-point-creation.patch
|
||||
patch_apply ntdll-Junction_Points/0002-ntdll-Add-support-for-reading-junction-points.patch
|
||||
patch_apply ntdll-Junction_Points/0003-ntdll-Add-support-for-deleting-junction-points.patch
|
||||
patch_apply ntdll-Junction_Points/0004-ntdll-Add-a-test-for-junction-point-advertisement.patch
|
||||
patch_apply ntdll-Junction_Points/0005-server-Add-support-for-deleting-junction-points-with.patch
|
||||
patch_apply ntdll-Junction_Points/0006-kernel32-Advertise-junction-point-support.patch
|
||||
patch_apply ntdll-Junction_Points/0007-ntdll-Add-support-for-absolute-symlink-creation.patch
|
||||
patch_apply ntdll-Junction_Points/0008-ntdll-Add-support-for-reading-absolute-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0009-ntdll-Add-support-for-deleting-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0010-ntdll-Add-support-for-relative-symlink-creation.patch
|
||||
patch_apply ntdll-Junction_Points/0011-ntdll-Add-support-for-reading-relative-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0012-ntdll-Add-support-for-file-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0013-ntdll-Allow-creation-of-dangling-reparse-points-to-n.patch
|
||||
patch_apply ntdll-Junction_Points/0014-ntdll-Correctly-report-file-symbolic-links-as-files.patch
|
||||
patch_apply ntdll-Junction_Points/0015-kernelbase-Convert-FILE_FLAG_OPEN_REPARSE_POINT-for-.patch
|
||||
patch_apply ntdll-Junction_Points/0016-server-Implement-FILE_OPEN_REPARSE_POINT-option.patch
|
||||
patch_apply ntdll-Junction_Points/0017-ntdll-Allow-set_file_times_precise-to-work-on-repars.patch
|
||||
patch_apply ntdll-Junction_Points/0018-server-Properly-handle-file-symlink-deletion.patch
|
||||
patch_apply ntdll-Junction_Points/0019-server-Properly-handle-deleting-dangling-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0020-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-RemoveDire.patch
|
||||
patch_apply ntdll-Junction_Points/0021-ntdll-Always-report-symbolic-links-as-containing-zer.patch
|
||||
patch_apply ntdll-Junction_Points/0022-ntdll-Find-dangling-symlinks-quickly.patch
|
||||
patch_apply ntdll-Junction_Points/0023-server-Fix-obtaining-information-about-a-symlink.patch
|
||||
patch_apply ntdll-Junction_Points/0024-ntdll-Succeed-with-no-data-for-NtReadFile-on-reparse.patch
|
||||
patch_apply ntdll-Junction_Points/0025-ntdll-Support-reparse-point-properties-in-fd_get_fil.patch
|
||||
patch_apply ntdll-Junction_Points/0026-ntdll-Add-support-for-FileAttributeTagInformation.patch
|
||||
patch_apply ntdll-Junction_Points/0027-kernel32-Implement-CreateSymbolicLink-A-W-with-ntdll.patch
|
||||
patch_apply ntdll-Junction_Points/0028-kernel32-Add-reparse-support-to-FindNextFile.patch
|
||||
patch_apply ntdll-Junction_Points/0029-wcmd-Display-reparse-point-type-in-directory-listing.patch
|
||||
patch_apply ntdll-Junction_Points/0030-wcmd-Show-reparse-point-target-in-directory-listing.patch
|
||||
patch_apply ntdll-Junction_Points/0031-wcmd-Add-junction-point-support-to-mklink.patch
|
||||
patch_apply ntdll-Junction_Points/0032-server-Properly-handle-renames-involving-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0033-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-MoveFileWi.patch
|
||||
patch_apply ntdll-Junction_Points/0034-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-DeleteFile.patch
|
||||
patch_apply ntdll-Junction_Points/0035-ntdll-Treat-undecoded-unix-symlinks-as-WSL-Linux-Uni.patch
|
||||
patch_apply ntdll-Junction_Points/0036-ntdll-Add-support-for-creating-Unix-Linux-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0037-ntdll-Strip-the-wine-prefix-from-reparse-point-paths.patch
|
||||
patch_apply ntdll-Junction_Points/0038-ntdll-Add-a-marker-to-reparse-point-paths-to-indicat.patch
|
||||
patch_apply ntdll-Junction_Points/0039-server-Rewrite-absolute-reparse-point-targets-if-the.patch
|
||||
patch_apply ntdll-Junction_Points/0040-Fix-warnings.patch
|
||||
patch_apply ntdll-Junction_Points/0001-ntdll-Add-support-for-creating-reparse-points.patch
|
||||
patch_apply ntdll-Junction_Points/0002-ntdll-Add-support-for-reading-reparse-points.patch
|
||||
patch_apply ntdll-Junction_Points/0003-ntdll-Add-support-for-deleting-reparse-points.patch
|
||||
patch_apply ntdll-Junction_Points/0004-ntdll-Add-support-for-testing-for-reparse-points-wit.patch
|
||||
patch_apply ntdll-Junction_Points/0005-server-Implement-FILE_OPEN_REPARSE_POINT-option.patch
|
||||
patch_apply ntdll-Junction_Points/0006-kernelbase-Add-support-for-deleting-reparse-points-w.patch
|
||||
patch_apply ntdll-Junction_Points/0007-kernelbase-Add-support-for-deleting-reparse-points-w.patch
|
||||
patch_apply ntdll-Junction_Points/0008-ntdll-Add-tests-for-NT-symlink-reparse-points.patch
|
||||
patch_apply ntdll-Junction_Points/0009-kernelbase-Add-support-for-moving-reparse-points-wit.patch
|
||||
patch_apply ntdll-Junction_Points/0010-kernelbase-Add-test-for-reparse-point-copy-behavior.patch
|
||||
patch_apply ntdll-Junction_Points/0011-ntdll-Follow-reparse-points-during-path-resolution.patch
|
||||
patch_apply ntdll-Junction_Points/0012-kernel32-Advertise-reparse-point-support.patch
|
||||
patch_apply ntdll-Junction_Points/0013-ntdll-Allow-reparse-points-to-target-the-applicable-.patch
|
||||
patch_apply ntdll-Junction_Points/0014-ntdll-Always-report-symbolic-links-as-containing-zer.patch
|
||||
patch_apply ntdll-Junction_Points/0015-ntdll-Find-dangling-symlinks-quickly.patch
|
||||
patch_apply ntdll-Junction_Points/0016-ntdll-Succeed-with-no-data-for-NtReadFile-on-reparse.patch
|
||||
patch_apply ntdll-Junction_Points/0017-ntdll-Add-support-for-FileAttributeTagInformation.patch
|
||||
patch_apply ntdll-Junction_Points/0018-kernel32-Implement-CreateSymbolicLink-A-W-with-ntdll.patch
|
||||
patch_apply ntdll-Junction_Points/0019-kernel32-Add-reparse-support-to-FindNextFile.patch
|
||||
patch_apply ntdll-Junction_Points/0020-wcmd-Display-reparse-point-type-in-directory-listing.patch
|
||||
patch_apply ntdll-Junction_Points/0021-wcmd-Show-reparse-point-target-in-directory-listing.patch
|
||||
patch_apply ntdll-Junction_Points/0022-wcmd-Add-junction-point-support-to-mklink.patch
|
||||
patch_apply ntdll-Junction_Points/0023-ntdll-Add-support-for-creating-Unix-Linux-symlinks.patch
|
||||
patch_apply ntdll-Junction_Points/0024-ntdll-Report-regular-Unix-symlinks-as-WSL-Linux-Unix.patch
|
||||
patch_apply ntdll-Junction_Points/0025-ntdll-Add-an-intermediary-symlink-in-reparse-point-m.patch
|
||||
patch_apply ntdll-Junction_Points/0026-ntdll-Rewrite-reparse-point-prefix-symlink-if-the-pr.patch
|
||||
fi
|
||||
|
||||
# Patchset server-PeekMessage
|
||||
|
@ -1,4 +1,4 @@
|
||||
From fcd07d80234de455880990e2b2c488e9795f9dc0 Mon Sep 17 00:00:00 2001
|
||||
From bab7a4b5d00822b0a7965ac338a73538e06b5aa7 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Fri, 3 Apr 2015 03:58:47 +0200
|
||||
Subject: [PATCH] server: Allow to open files without any permission bits. (try
|
||||
@ -13,10 +13,10 @@ Changes in v2:
|
||||
2 files changed, 33 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
|
||||
index ed91ccc39d3..c534d1453d9 100644
|
||||
index 51bf60d0f36..ed8db358ab8 100644
|
||||
--- a/dlls/advapi32/tests/security.c
|
||||
+++ b/dlls/advapi32/tests/security.c
|
||||
@@ -3713,17 +3713,13 @@ static void test_CreateDirectoryA(void)
|
||||
@@ -3782,17 +3782,13 @@ static void test_CreateDirectoryA(void)
|
||||
error = pGetNamedSecurityInfoA(tmpfile, SE_FILE_OBJECT,
|
||||
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
(PSID *)&owner, NULL, &pDacl, NULL, &pSD);
|
||||
@ -40,7 +40,7 @@ index ed91ccc39d3..c534d1453d9 100644
|
||||
CloseHandle(hTemp);
|
||||
|
||||
/* Test inheritance of ACLs in NtCreateFile without security descriptor */
|
||||
@@ -3792,17 +3788,13 @@ static void test_CreateDirectoryA(void)
|
||||
@@ -3861,17 +3857,13 @@ static void test_CreateDirectoryA(void)
|
||||
error = pGetNamedSecurityInfoA(tmpfile, SE_FILE_OBJECT,
|
||||
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
(PSID *)&owner, NULL, &pDacl, NULL, &pSD);
|
||||
@ -65,10 +65,10 @@ index ed91ccc39d3..c534d1453d9 100644
|
||||
|
||||
done:
|
||||
diff --git a/server/fd.c b/server/fd.c
|
||||
index bf59359de03..323dd92e653 100644
|
||||
index db265a9b1b8..f025d51a1e0 100644
|
||||
--- a/server/fd.c
|
||||
+++ b/server/fd.c
|
||||
@@ -2044,6 +2044,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
@@ -2006,6 +2006,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
int root_fd = -1;
|
||||
int rw_mode;
|
||||
char *path;
|
||||
@ -76,7 +76,7 @@ index bf59359de03..323dd92e653 100644
|
||||
int created = (flags & O_CREAT);
|
||||
|
||||
if (((options & FILE_DELETE_ON_CLOSE) && !(access & DELETE)) ||
|
||||
@@ -2116,6 +2117,23 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
@@ -2074,6 +2075,23 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
if ((access & FILE_UNIX_WRITE_ACCESS) || (flags & O_CREAT))
|
||||
fd->unix_fd = open( name, O_RDONLY | (flags & ~(O_TRUNC | O_CREAT | O_EXCL)), *mode );
|
||||
}
|
||||
@ -100,7 +100,7 @@ index bf59359de03..323dd92e653 100644
|
||||
|
||||
if (fd->unix_fd == -1)
|
||||
{
|
||||
@@ -2124,6 +2142,8 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
@@ -2082,6 +2100,8 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
set_error( STATUS_OBJECT_NAME_INVALID );
|
||||
else
|
||||
file_set_error();
|
||||
@ -109,12 +109,12 @@ index bf59359de03..323dd92e653 100644
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@@ -2134,6 +2154,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
@@ -2097,6 +2117,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
|
||||
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;
|
||||
+ if (do_chmod) chmod( name, *mode );
|
||||
lstat( fd->unlink_name, &st );
|
||||
fstat( fd->unix_fd, &st );
|
||||
*mode = st.st_mode;
|
||||
|
||||
--
|
||||
|
Loading…
Reference in New Issue
Block a user