Rebase against fe1175af410d04f806dda770502d0476a8ccbef6.

This commit is contained in:
Elizabeth Figura
2025-11-14 15:26:34 -06:00
parent 2bf94fc85b
commit ed8a24d79b
4 changed files with 1 additions and 405 deletions

View File

@@ -1,27 +0,0 @@
From 0423cf43baf2938b4b1e7d705c20af11fb13f82c 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: Add support for deleting reparse points with
RemoveDirectory.
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
dlls/kernelbase/file.c | 2 +-
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index bfb291fa925..6214f549406 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)
--
2.17.1

View File

@@ -1,28 +0,0 @@
From afd4ad0f6725f49daaa0fe2351c98faa6a57519a 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 ++-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 36b43d345d6..b7d16410d75 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -1002,7 +1002,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileW( LPCWSTR path )
InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL );
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.47.2

View File

@@ -1,349 +0,0 @@
From a9773394980e77b017dcb797766ec422b384a5cc 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/unix/file.c | 196 +++++++++++++++++++++++++++++++++++-----
2 files changed, 184 insertions(+), 23 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index f3274478ae2..860e563f69d 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -3741,6 +3741,35 @@ done:
}
+static NTSTATUS get_reparse_target( UNICODE_STRING *nt_target, REPARSE_DATA_BUFFER *buffer,
+ int *is_relative )
+{
+ 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;
+ *is_relative = FALSE;
+ break;
+ case IO_REPARSE_TAG_SYMLINK:
+ offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR);
+ target = &buffer->SymbolicLinkReparseBuffer.PathBuffer[offset];
+ target_len = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
+ *is_relative = (buffer->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) == SYMLINK_FLAG_RELATIVE;
+ break;
+ default:
+ return STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+ }
+ nt_target->Buffer = target;
+ nt_target->Length = target_len;
+ return STATUS_REPARSE;
+}
+
+
/*
* 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.
@@ -3917,16 +3946,14 @@ 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_name, REPARSE_DATA_BUFFER *buffer, ULONG *size)
{
char link_dir[PATH_MAX], link_path[PATH_MAX], *d;
int link_path_len, buffer_len, encoded_len;
REPARSE_DATA_BUFFER header;
ULONG out_size = *size;
- char *unix_name = NULL;
char *encoded = NULL;
int link_dir_fd = -1;
NTSTATUS status;
@@ -3934,9 +3961,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_name )))
- goto cleanup;
-
ret = readlink( unix_name, link_path, sizeof(link_path) );
if (ret < 0)
{
@@ -4036,12 +4060,76 @@ NTSTATUS get_reparse_point(HANDLE handle, REPARSE_DATA_BUFFER *buffer, ULONG *si
cleanup:
if (link_dir_fd != -1) close( link_dir_fd );
- free( unix_name );
free( encoded );
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)
+{
+ 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, const WCHAR *parent, int parent_len,
+ WCHAR **new_name, int *new_name_len)
+{
+ REPARSE_DATA_BUFFER *buffer = NULL;
+ UNICODE_STRING nt_target;
+ ULONG buffer_len = 0;
+ int is_relative;
+ NTSTATUS status;
+
+ 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;
+ }
+ if ((status = get_reparse_target( &nt_target, buffer, &is_relative )) == STATUS_REPARSE)
+ {
+ WCHAR *p;
+
+ p = *new_name = malloc( nt_target.Length + parent_len*sizeof(WCHAR) );
+ if (!p)
+ {
+ status = STATUS_NO_MEMORY;
+ goto done;
+ }
+ if (is_relative)
+ {
+ memcpy( p, parent, parent_len*sizeof(WCHAR) );
+ p += parent_len;
+ }
+ memcpy( p, nt_target.Buffer, nt_target.Length );
+ p += nt_target.Length/sizeof(WCHAR);
+ *new_name_len = p - *new_name;
+ }
+
+done:
+ 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.
@@ -4135,15 +4223,24 @@ cleanup:
}
+static NTSTATUS IoReplaceFileObjectName( FILE_OBJECT *fileobj, PWSTR name, USHORT name_len )
+{
+ fileobj->FileName.Buffer = name;
+ fileobj->FileName.Length = name_len;
+ return STATUS_SUCCESS;
+}
+
+
/******************************************************************************
* lookup_unix_name
*
* Helper for nt_to_unix_file_name
*/
-static NTSTATUS lookup_unix_name( int root_fd, const WCHAR *name, int name_len, char **buffer, int unix_len,
+static NTSTATUS lookup_unix_name( FILE_OBJECT *fileobj, int root_fd, const WCHAR *name, int name_len, char **buffer, int unix_len,
int pos, UINT disposition, BOOL is_unix )
{
static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, '/', 0 };
+ const WCHAR *fullname = fileobj->FileName.Buffer;
NTSTATUS status;
int ret;
struct stat st;
@@ -4200,6 +4297,8 @@ static NTSTATUS lookup_unix_name( int root_fd, const WCHAR *name, int name_len,
while (name_len)
{
const WCHAR *end, *next;
+ WCHAR *target = NULL;
+ int target_len = 0;
end = name;
while (end < name + name_len && *end != '\\') end++;
@@ -4219,8 +4318,31 @@ static NTSTATUS lookup_unix_name( int root_fd, const WCHAR *name, int name_len,
status = find_file_in_dir( root_fd, unix_name, pos, name, end - name, is_unix );
+ /* follow reparse point and restart from there (if applicable) */
+ if (name_len && find_reparse_target( unix_name, fullname, name - fullname, &target, &target_len ) == STATUS_REPARSE)
+ {
+ int new_name_len = target_len + name_len + 1;
+ WCHAR *p, *new_name;
+
+ if (!(p = new_name = malloc( new_name_len*sizeof(WCHAR) )))
+ {
+ free( target );
+ status = STATUS_NO_MEMORY;
+ break;
+ }
+ memcpy( p, target, target_len*sizeof(WCHAR) );
+ p += target_len;
+ (p++)[0] = '\\';
+ memcpy( p, next, name_len*sizeof(WCHAR) );
+ TRACE( "Follow reparse point %s => %s\n", debugstr_wn(fullname, end-fullname),
+ debugstr_wn(new_name, new_name_len) );
+ free( target );
+ if (IoReplaceFileObjectName( fileobj, new_name, new_name_len*sizeof(WCHAR) ))
+ free( new_name );
+ return STATUS_REPARSE;
+ }
/* 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)
{
@@ -4259,12 +4381,12 @@ static NTSTATUS lookup_unix_name( int root_fd, const WCHAR *name, int name_len,
/******************************************************************************
* nt_to_unix_file_name_no_root
*/
-static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char **unix_name_ret,
+static NTSTATUS nt_to_unix_file_name_no_root( FILE_OBJECT *fileobj, char **unix_name_ret,
UINT disposition )
{
static const WCHAR unixW[] = {'u','n','i','x'};
static const WCHAR invalid_charsW[] = { INVALID_NT_CHARS, 0 };
-
+ const UNICODE_STRING *nameW = &fileobj->FileName;
NTSTATUS status = STATUS_SUCCESS;
const WCHAR *name;
struct stat st;
@@ -4354,7 +4476,7 @@ static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char
name += prefix_len;
name_len -= prefix_len;
- status = lookup_unix_name( AT_FDCWD, name, name_len, &unix_name, unix_len, pos, disposition, is_unix );
+ status = lookup_unix_name( fileobj, AT_FDCWD, name, name_len, &unix_name, unix_len, pos, disposition, is_unix );
if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
{
TRACE( "%s -> %s\n", debugstr_us(nameW), debugstr_a(unix_name) );
@@ -4362,7 +4484,8 @@ static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char
}
else
{
- TRACE( "%s not found in %s\n", debugstr_w(name), debugstr_an(unix_name, pos) );
+ if (status != STATUS_REPARSE)
+ TRACE( "%s not found in %s\n", debugstr_w(name), debugstr_an(unix_name, pos) );
free( unix_name );
}
return status;
@@ -4380,18 +4503,30 @@ static NTSTATUS nt_to_unix_file_name_no_root( const UNICODE_STRING *nameW, char
*/
static NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition )
{
+ HANDLE rootdir = attr->RootDirectory;
enum server_fd_type type;
- int root_fd, needs_close;
+ int old_cwd, root_fd, needs_close;
+ int reparse_count = 0;
+ FILE_OBJECT fileobj;
const WCHAR *name;
char *unix_name;
int name_len, unix_len;
NTSTATUS status;
- if (!attr->RootDirectory) /* without root dir fall back to normal lookup */
- return nt_to_unix_file_name_no_root( attr->ObjectName, name_ret, disposition );
+ fileobj.FileName = *attr->ObjectName;
+reparse:
+ if (reparse_count++ == 31)
+ return STATUS_REPARSE_POINT_NOT_RESOLVED;
+ if (!rootdir) /* without root dir fall back to normal lookup */
+ {
+ status = nt_to_unix_file_name_no_root( &fileobj, name_ret, disposition );
+ if (status == STATUS_REPARSE) goto reparse;
+ if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer);
+ return status;
+ }
- name = attr->ObjectName->Buffer;
- name_len = attr->ObjectName->Length / sizeof(WCHAR);
+ name = fileobj.FileName.Buffer;
+ name_len = fileobj.FileName.Length / sizeof(WCHAR);
if (name_len && name[0] == '\\') return STATUS_INVALID_PARAMETER;
@@ -4399,7 +4534,7 @@ static NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name
if (!(unix_name = malloc( unix_len ))) return STATUS_NO_MEMORY;
unix_name[0] = '.';
- if (!(status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
+ if (!(status = server_get_unix_fd( rootdir, 0, &root_fd, &needs_close, &type, NULL )))
{
if (type != FD_TYPE_DIR)
{
@@ -4408,7 +4543,16 @@ static NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name
}
else
{
- status = lookup_unix_name( root_fd, name, name_len, &unix_name, unix_len, 1, disposition, FALSE );
+ mutex_lock( &dir_mutex );
+ if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
+ {
+ status = lookup_unix_name( &fileobj, root_fd, name, name_len, &unix_name, unix_len, 1,
+ disposition, FALSE );
+ if (fchdir( old_cwd ) == -1) chdir( "/" );
+ }
+ else status = errno_to_status( errno );
+ mutex_unlock( &dir_mutex );
+ if (old_cwd != -1) close( old_cwd );
if (needs_close) close( root_fd );
}
}
@@ -4416,14 +4560,22 @@ static NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name
if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE)
{
- TRACE( "%s -> %s\n", debugstr_us(attr->ObjectName), debugstr_a(unix_name) );
+ TRACE( "%s -> %s\n", debugstr_us(&fileobj.FileName), debugstr_a(unix_name) );
*name_ret = unix_name;
}
+ else if (status == STATUS_REPARSE)
+ {
+ if (fileobj.FileName.Buffer[0] == '\\') rootdir = 0;
+ free( unix_name );
+ goto reparse;
+ }
else
{
TRACE( "%s not found in %s\n", debugstr_w(name), unix_name );
free( unix_name );
}
+
+ if (fileobj.FileName.Buffer != attr->ObjectName->Buffer) free( fileobj.FileName.Buffer);
return status;
}
--
2.47.2

View File

@@ -1 +1 @@
76e7e90c3679766ac327b138e3269fb61ead6e4a
fe1175af410d04f806dda770502d0476a8ccbef6