From 1ed196f0effa262b297705f36975261f44001417 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Sun, 11 Sep 2022 19:36:38 -0500 Subject: [PATCH] ntdll-Junction_Points: Updates from Erich E. Hoover. Includes a fundamental rewrite of the way reparse points are stored. --- ...entfd-descriptors-for-pseudo-fd-obje.patch | 26 +- ...-support-for-creating-reparse-points.patch | 634 ++++++++++++++++++ ...-support-for-junction-point-creation.patch | 421 ------------ ...-support-for-reading-junction-points.patch | 206 ------ ...d-support-for-reading-reparse-points.patch | 293 ++++++++ ...support-for-deleting-reparse-points.patch} | 99 +-- ...est-for-junction-point-advertisement.patch | 29 - ...t-for-testing-for-reparse-points-wit.patch | 77 +++ ...rt-for-deleting-junction-points-with.patch | 206 ------ ...ement-FILE_OPEN_REPARSE_POINT-option.patch | 276 ++++++++ ...upport-for-deleting-reparse-points-w.patch | 85 +++ ...upport-for-deleting-reparse-points-w.patch | 58 ++ ...upport-for-absolute-symlink-creation.patch | 313 --------- ...upport-for-reading-absolute-symlinks.patch | 102 --- ...-tests-for-NT-symlink-reparse-points.patch | 358 ++++++++++ ...upport-for-moving-reparse-points-wit.patch | 149 ++++ ...ll-Add-support-for-deleting-symlinks.patch | 53 -- ...test-for-reparse-point-copy-behavior.patch | 30 + ...upport-for-relative-symlink-creation.patch | 294 -------- ...upport-for-reading-relative-symlinks.patch | 111 --- ...eparse-points-during-path-resolution.patch | 240 +++++++ ...l32-Advertise-reparse-point-support.patch} | 37 +- ...-ntdll-Add-support-for-file-symlinks.patch | 134 ---- ...tion-of-dangling-reparse-points-to-n.patch | 58 -- ...rse-points-to-target-the-applicable-.patch | 186 +++++ ...ort-symbolic-links-as-containing-zer.patch | 86 +++ ...-report-file-symbolic-links-as-files.patch | 193 ------ ...rt-FILE_FLAG_OPEN_REPARSE_POINT-for-.patch | 27 - ...tdll-Find-dangling-symlinks-quickly.patch} | 12 +- ...h-no-data-for-NtReadFile-on-reparse.patch} | 18 +- ...ement-FILE_OPEN_REPARSE_POINT-option.patch | 180 ----- ...ort-for-FileAttributeTagInformation.patch} | 45 +- ...file_times_precise-to-work-on-repars.patch | 117 ---- ...t-CreateSymbolicLink-A-W-with-ntdll.patch} | 39 +- ...roperly-handle-file-symlink-deletion.patch | 134 ---- ...Add-reparse-support-to-FindNextFile.patch} | 10 +- ...ly-handle-deleting-dangling-symlinks.patch | 34 - ...ILE_OPEN_REPARSE_POINT-in-RemoveDire.patch | 26 - ...rse-point-type-in-directory-listing.patch} | 8 +- ...ort-symbolic-links-as-containing-zer.patch | 111 --- ...e-point-target-in-directory-listing.patch} | 8 +- ...dd-junction-point-support-to-mklink.patch} | 12 +- ...ort-for-creating-Unix-Linux-symlinks.patch | 165 +++++ ...btaining-information-about-a-symlink.patch | 140 ---- ...ular-Unix-symlinks-as-WSL-Linux-Unix.patch | 67 ++ ...ermediary-symlink-in-reparse-point-m.patch | 68 ++ ...parse-point-properties-in-fd_get_fil.patch | 102 --- ...parse-point-prefix-symlink-if-the-pr.patch | 45 ++ ...ly-handle-renames-involving-symlinks.patch | 44 -- ...ILE_OPEN_REPARSE_POINT-in-MoveFileWi.patch | 26 - ...ILE_OPEN_REPARSE_POINT-in-DeleteFile.patch | 27 - ...coded-unix-symlinks-as-WSL-Linux-Uni.patch | 178 ----- ...ort-for-creating-Unix-Linux-symlinks.patch | 157 ----- ...wine-prefix-from-reparse-point-paths.patch | 61 -- ...er-to-reparse-point-paths-to-indicat.patch | 56 -- ...bsolute-reparse-point-targets-if-the.patch | 154 ----- .../0040-Fix-warnings.patch | 71 -- patches/patchinstall.sh | 72 +- ...open-files-without-any-permission-bi.patch | 22 +- 59 files changed, 3005 insertions(+), 3985 deletions(-) create mode 100644 patches/ntdll-Junction_Points/0001-ntdll-Add-support-for-creating-reparse-points.patch delete mode 100644 patches/ntdll-Junction_Points/0001-ntdll-Add-support-for-junction-point-creation.patch delete mode 100644 patches/ntdll-Junction_Points/0002-ntdll-Add-support-for-reading-junction-points.patch create mode 100644 patches/ntdll-Junction_Points/0002-ntdll-Add-support-for-reading-reparse-points.patch rename patches/ntdll-Junction_Points/{0003-ntdll-Add-support-for-deleting-junction-points.patch => 0003-ntdll-Add-support-for-deleting-reparse-points.patch} (74%) delete mode 100644 patches/ntdll-Junction_Points/0004-ntdll-Add-a-test-for-junction-point-advertisement.patch create mode 100644 patches/ntdll-Junction_Points/0004-ntdll-Add-support-for-testing-for-reparse-points-wit.patch delete mode 100644 patches/ntdll-Junction_Points/0005-server-Add-support-for-deleting-junction-points-with.patch create mode 100644 patches/ntdll-Junction_Points/0005-server-Implement-FILE_OPEN_REPARSE_POINT-option.patch create mode 100644 patches/ntdll-Junction_Points/0006-kernelbase-Add-support-for-deleting-reparse-points-w.patch create mode 100644 patches/ntdll-Junction_Points/0007-kernelbase-Add-support-for-deleting-reparse-points-w.patch delete mode 100644 patches/ntdll-Junction_Points/0007-ntdll-Add-support-for-absolute-symlink-creation.patch delete mode 100644 patches/ntdll-Junction_Points/0008-ntdll-Add-support-for-reading-absolute-symlinks.patch create mode 100644 patches/ntdll-Junction_Points/0008-ntdll-Add-tests-for-NT-symlink-reparse-points.patch create mode 100644 patches/ntdll-Junction_Points/0009-kernelbase-Add-support-for-moving-reparse-points-wit.patch delete mode 100644 patches/ntdll-Junction_Points/0009-ntdll-Add-support-for-deleting-symlinks.patch create mode 100644 patches/ntdll-Junction_Points/0010-kernelbase-Add-test-for-reparse-point-copy-behavior.patch delete mode 100644 patches/ntdll-Junction_Points/0010-ntdll-Add-support-for-relative-symlink-creation.patch delete mode 100644 patches/ntdll-Junction_Points/0011-ntdll-Add-support-for-reading-relative-symlinks.patch create mode 100644 patches/ntdll-Junction_Points/0011-ntdll-Follow-reparse-points-during-path-resolution.patch rename patches/ntdll-Junction_Points/{0006-kernel32-Advertise-junction-point-support.patch => 0012-kernel32-Advertise-reparse-point-support.patch} (79%) delete mode 100644 patches/ntdll-Junction_Points/0012-ntdll-Add-support-for-file-symlinks.patch delete mode 100644 patches/ntdll-Junction_Points/0013-ntdll-Allow-creation-of-dangling-reparse-points-to-n.patch create mode 100644 patches/ntdll-Junction_Points/0013-ntdll-Allow-reparse-points-to-target-the-applicable-.patch create mode 100644 patches/ntdll-Junction_Points/0014-ntdll-Always-report-symbolic-links-as-containing-zer.patch delete mode 100644 patches/ntdll-Junction_Points/0014-ntdll-Correctly-report-file-symbolic-links-as-files.patch delete mode 100644 patches/ntdll-Junction_Points/0015-kernelbase-Convert-FILE_FLAG_OPEN_REPARSE_POINT-for-.patch rename patches/ntdll-Junction_Points/{0022-ntdll-Find-dangling-symlinks-quickly.patch => 0015-ntdll-Find-dangling-symlinks-quickly.patch} (79%) rename patches/ntdll-Junction_Points/{0024-ntdll-Succeed-with-no-data-for-NtReadFile-on-reparse.patch => 0016-ntdll-Succeed-with-no-data-for-NtReadFile-on-reparse.patch} (82%) delete mode 100644 patches/ntdll-Junction_Points/0016-server-Implement-FILE_OPEN_REPARSE_POINT-option.patch rename patches/ntdll-Junction_Points/{0026-ntdll-Add-support-for-FileAttributeTagInformation.patch => 0017-ntdll-Add-support-for-FileAttributeTagInformation.patch} (56%) delete mode 100644 patches/ntdll-Junction_Points/0017-ntdll-Allow-set_file_times_precise-to-work-on-repars.patch rename patches/ntdll-Junction_Points/{0027-kernel32-Implement-CreateSymbolicLink-A-W-with-ntdll.patch => 0018-kernel32-Implement-CreateSymbolicLink-A-W-with-ntdll.patch} (95%) delete mode 100644 patches/ntdll-Junction_Points/0018-server-Properly-handle-file-symlink-deletion.patch rename patches/ntdll-Junction_Points/{0028-kernel32-Add-reparse-support-to-FindNextFile.patch => 0019-kernel32-Add-reparse-support-to-FindNextFile.patch} (88%) delete mode 100644 patches/ntdll-Junction_Points/0019-server-Properly-handle-deleting-dangling-symlinks.patch delete mode 100644 patches/ntdll-Junction_Points/0020-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-RemoveDire.patch rename patches/ntdll-Junction_Points/{0029-wcmd-Display-reparse-point-type-in-directory-listing.patch => 0020-wcmd-Display-reparse-point-type-in-directory-listing.patch} (90%) delete mode 100644 patches/ntdll-Junction_Points/0021-ntdll-Always-report-symbolic-links-as-containing-zer.patch rename patches/ntdll-Junction_Points/{0030-wcmd-Show-reparse-point-target-in-directory-listing.patch => 0021-wcmd-Show-reparse-point-target-in-directory-listing.patch} (93%) rename patches/ntdll-Junction_Points/{0031-wcmd-Add-junction-point-support-to-mklink.patch => 0022-wcmd-Add-junction-point-support-to-mklink.patch} (92%) create mode 100644 patches/ntdll-Junction_Points/0023-ntdll-Add-support-for-creating-Unix-Linux-symlinks.patch delete mode 100644 patches/ntdll-Junction_Points/0023-server-Fix-obtaining-information-about-a-symlink.patch create mode 100644 patches/ntdll-Junction_Points/0024-ntdll-Report-regular-Unix-symlinks-as-WSL-Linux-Unix.patch create mode 100644 patches/ntdll-Junction_Points/0025-ntdll-Add-an-intermediary-symlink-in-reparse-point-m.patch delete mode 100644 patches/ntdll-Junction_Points/0025-ntdll-Support-reparse-point-properties-in-fd_get_fil.patch create mode 100644 patches/ntdll-Junction_Points/0026-ntdll-Rewrite-reparse-point-prefix-symlink-if-the-pr.patch delete mode 100644 patches/ntdll-Junction_Points/0032-server-Properly-handle-renames-involving-symlinks.patch delete mode 100644 patches/ntdll-Junction_Points/0033-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-MoveFileWi.patch delete mode 100644 patches/ntdll-Junction_Points/0034-kernelbase-Use-FILE_OPEN_REPARSE_POINT-in-DeleteFile.patch delete mode 100644 patches/ntdll-Junction_Points/0035-ntdll-Treat-undecoded-unix-symlinks-as-WSL-Linux-Uni.patch delete mode 100644 patches/ntdll-Junction_Points/0036-ntdll-Add-support-for-creating-Unix-Linux-symlinks.patch delete mode 100644 patches/ntdll-Junction_Points/0037-ntdll-Strip-the-wine-prefix-from-reparse-point-paths.patch delete mode 100644 patches/ntdll-Junction_Points/0038-ntdll-Add-a-marker-to-reparse-point-paths-to-indicat.patch delete mode 100644 patches/ntdll-Junction_Points/0039-server-Rewrite-absolute-reparse-point-targets-if-the.patch delete mode 100644 patches/ntdll-Junction_Points/0040-Fix-warnings.patch diff --git a/patches/eventfd_synchronization/0046-server-Create-eventfd-descriptors-for-pseudo-fd-obje.patch b/patches/eventfd_synchronization/0046-server-Create-eventfd-descriptors-for-pseudo-fd-obje.patch index 18dfad0f..de2999ab 100644 --- a/patches/eventfd_synchronization/0046-server-Create-eventfd-descriptors-for-pseudo-fd-obje.patch +++ b/patches/eventfd_synchronization/0046-server-Create-eventfd-descriptors-for-pseudo-fd-obje.patch @@ -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 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 diff --git a/patches/ntdll-Junction_Points/0001-ntdll-Add-support-for-creating-reparse-points.patch b/patches/ntdll-Junction_Points/0001-ntdll-Add-support-for-creating-reparse-points.patch new file mode 100644 index 00000000..1dc7e9b3 --- /dev/null +++ b/patches/ntdll-Junction_Points/0001-ntdll-Add-support-for-creating-reparse-points.patch @@ -0,0 +1,634 @@ +From 6209b270e6f2a7913a95f6c1da18c11e2e2a73a5 Mon Sep 17 00:00:00 2001 +From: "Erich E. Hoover" +Date: Thu, 16 Jan 2014 20:56:49 -0700 +Subject: [PATCH] ntdll: Add support for creating reparse points. + +Signed-off-by: Erich E. Hoover +--- + 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 + #include + #include ++#include ++#include + #include + #include + #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