You've already forked wine-staging
mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2025-12-15 08:03:15 -08:00
Rebase against 9250ecc5a6a64c73aada0ea751815412f7f00410.
This commit is contained in:
@@ -1,413 +0,0 @@
|
||||
From 5f3feef99f8c9740f04dbe0ee1b23ca27d60548e 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/unix/file.c | 302 +++++++++++++++++++++++++++++++++++++++++
|
||||
include/ddk/ntifs.h | 5 +
|
||||
4 files changed, 310 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index b675da34d58..63e3bb5bdc7 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -2137,6 +2137,8 @@ AC_CHECK_FUNCS(\
|
||||
process_vm_writev \
|
||||
sched_getcpu \
|
||||
sched_yield \
|
||||
+ renameat \
|
||||
+ renameat2 \
|
||||
setproctitle \
|
||||
setprogname \
|
||||
sigprocmask \
|
||||
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in
|
||||
index 3c0dfa7a895..b9a8bcba4cf 100644
|
||||
--- a/dlls/ntdll/Makefile.in
|
||||
+++ b/dlls/ntdll/Makefile.in
|
||||
@@ -5,7 +5,7 @@ IMPORTLIB = ntdll
|
||||
IMPORTS = $(TOMCRYPT_PE_LIBS) $(MUSL_PE_LIBS)
|
||||
EXTRAINCL = $(TOMCRYPT_PE_CFLAGS)
|
||||
UNIX_CFLAGS = $(UNWIND_CFLAGS) $(HWLOC_CFLAGS)
|
||||
-UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS) $(HWLOC_LIBS)
|
||||
+UNIX_LIBS = $(IOKIT_LIBS) $(COREFOUNDATION_LIBS) $(CORESERVICES_LIBS) $(RT_LIBS) $(PTHREAD_LIBS) $(UNWIND_LIBS) $(I386_LIBS) $(PROCSTAT_LIBS) $(HWLOC_LIBS) -lm
|
||||
|
||||
EXTRADLLFLAGS = -nodefaultlibs
|
||||
i386_EXTRADLLFLAGS = -Wl,--image-base,0x7bc00000
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 7ab1dbb2b55..5b4fcb3711d 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
+#include <math.h>
|
||||
+#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_MNTENT_H
|
||||
@@ -121,6 +123,7 @@
|
||||
#include "wine/list.h"
|
||||
#include "wine/debug.h"
|
||||
#include "unix_private.h"
|
||||
+#include "ddk/ntifs.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(file);
|
||||
WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
||||
@@ -132,6 +135,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 */
|
||||
@@ -249,6 +258,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( int dirfd, 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 (mkdirat( dirfd, path_tmp, mode ) != 0 && errno != EEXIST)
|
||||
+ return -1;
|
||||
+ *p = '/';
|
||||
+ }
|
||||
+ }
|
||||
+ if (mkdirat( dirfd, 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 )
|
||||
{
|
||||
@@ -1653,6 +1751,28 @@ static int parse_samba_dos_attrib_data( char *data, int len )
|
||||
}
|
||||
|
||||
|
||||
+/* 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;
|
||||
@@ -3455,6 +3575,181 @@ 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];
|
||||
+ 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;
|
||||
+ int link_dir_fd = -1;
|
||||
+ 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", debugstr_a(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 */
|
||||
+ strcpy( link_dir, tmpdir );
|
||||
+ link_dir[strlen(link_dir)-16] = 0;
|
||||
+ link_dir_fd = open( link_dir, O_RDONLY|O_DIRECTORY );
|
||||
+
|
||||
+ /* If there is any further information in the reparse tag then store it in the hidden folder */
|
||||
+ while(i < encoded_len)
|
||||
+ {
|
||||
+ int fd;
|
||||
+
|
||||
+ 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_fd, link_dir, 0777))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (symlinkat( target_path, link_dir_fd, link_path ))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ fd = openat( link_dir_fd, link_dir, O_RDONLY|O_DIRECTORY );
|
||||
+ close( link_dir_fd );
|
||||
+ link_dir_fd = fd;
|
||||
+ }
|
||||
+
|
||||
+ /* 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 (link_dir_fd != -1) close( link_dir_fd );
|
||||
+ if (tempdir_created) rmdir( tmpdir );
|
||||
+ if (needs_close) close( fd );
|
||||
+ free( unix_src );
|
||||
+ free( encoded );
|
||||
+
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/******************************************************************************
|
||||
* lookup_unix_name
|
||||
*
|
||||
@@ -6400,6 +6695,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");
|
||||
status = STATUS_SUCCESS;
|
||||
diff --git a/include/ddk/ntifs.h b/include/ddk/ntifs.h
|
||||
index 980235abdc9..90248b4897c 100644
|
||||
--- a/include/ddk/ntifs.h
|
||||
+++ b/include/ddk/ntifs.h
|
||||
@@ -166,6 +166,11 @@ typedef struct _REPARSE_DATA_BUFFER
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
|
||||
+ struct {
|
||||
+ ULONG Version;
|
||||
+ UCHAR PathBuffer[1];
|
||||
+ } LinuxSymbolicLinkReparseBuffer;
|
||||
+
|
||||
struct
|
||||
{
|
||||
UCHAR DataBuffer[1];
|
||||
--
|
||||
2.51.0
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
From 369f4d92683910c01d82719e448d21a74b7b82d8 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/unix/file.c | 221 ++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 231 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 0d58abbfa84..3e160fb1e5c 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -325,6 +325,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( int dirfd, const char *path, mode_t mode )
|
||||
{
|
||||
@@ -3715,6 +3793,132 @@ 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], 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;
|
||||
+ ssize_t ret;
|
||||
+ 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)
|
||||
+ {
|
||||
+ 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);
|
||||
+ encoded = realloc( encoded, encoded_len + 3 ); /* 3 chars = slash, chunk ID, NUL character */
|
||||
+ if (!encoded)
|
||||
+ {
|
||||
+ status = STATUS_NO_MEMORY;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ /* change to the link folder so that we can build any necessary additional data */
|
||||
+ strcpy( link_dir, unix_name );
|
||||
+ d = dirname( link_dir);
|
||||
+ if (d != link_dir) strcpy( link_dir, d );
|
||||
+ link_dir_fd = open( link_dir, O_RDONLY|O_DIRECTORY );
|
||||
+
|
||||
+ /* Copy the encoded data from the follow on symlinks */
|
||||
+ while(strlen(encoded) < encoded_len)
|
||||
+ {
|
||||
+ int fd;
|
||||
+
|
||||
+ strcpy( link_dir, link_path );
|
||||
+ ret = readlinkat( link_dir_fd, 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; /* readlink does not NUL terminate */
|
||||
+
|
||||
+ 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;
|
||||
+ fd = openat( link_dir_fd, link_dir, O_RDONLY|O_DIRECTORY );
|
||||
+ close( link_dir_fd );
|
||||
+ link_dir_fd = fd;
|
||||
+ }
|
||||
+
|
||||
+ /* 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:
|
||||
+ if (link_dir_fd != -1) close( link_dir_fd );
|
||||
+ free( unix_name );
|
||||
+ free( encoded );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/******************************************************************************
|
||||
* lookup_unix_name
|
||||
*
|
||||
@@ -6525,15 +6729,6 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
|
||||
break;
|
||||
}
|
||||
|
||||
- case FSCTL_GET_REPARSE_POINT:
|
||||
- if (out_buffer && out_size)
|
||||
- {
|
||||
- FIXME("FSCTL_GET_REPARSE_POINT semi-stub\n");
|
||||
- status = STATUS_NOT_A_REPARSE_POINT;
|
||||
- }
|
||||
- else status = STATUS_INVALID_USER_BUFFER;
|
||||
- break;
|
||||
-
|
||||
case FSCTL_GET_OBJECT_ID:
|
||||
{
|
||||
FILE_OBJECTID_BUFFER *info = out_buffer;
|
||||
@@ -6555,6 +6750,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.43.0
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
From 3c25b2e7839c1493f97d144f137822cbf9a498a3 Mon Sep 17 00:00:00 2001
|
||||
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
||||
Date: Thu, 16 Jan 2014 21:00:21 -0700
|
||||
Subject: [PATCH] ntdll: Add support for deleting reparse points.
|
||||
|
||||
Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 99 +++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 92ce83b685e..ba77aab61b7 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3812,6 +3812,99 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * Retrieve the unix name corresponding to a file handle, remove that symlink, and then recreate
|
||||
+ * a directory at the location of the old filename.
|
||||
+ */
|
||||
+NTSTATUS remove_reparse_point(HANDLE handle, REPARSE_GUID_DATA_BUFFER *buffer)
|
||||
+{
|
||||
+ 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;
|
||||
+
|
||||
+ 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 )))
|
||||
+ goto cleanup;
|
||||
+
|
||||
+ TRACE( "Deleting symlink %s\n", unix_name );
|
||||
+
|
||||
+ /* 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 );
|
||||
+ 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 (is_dir && mkdir( tmplink, st.st_mode ))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ else if (!is_dir)
|
||||
+ {
|
||||
+ 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)
|
||||
+ {
|
||||
+ FIXME( "Atomic exchange of directory with symbolic link unsupported on this system, "
|
||||
+ "using unsafe exchange instead.\n" );
|
||||
+ if (unlink( unix_name ))
|
||||
+ {
|
||||
+ status = errno_to_status( errno );
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ if (rename( tmplink, unix_name ))
|
||||
+ {
|
||||
+ 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( dest_fd );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+
|
||||
/******************************************************************************
|
||||
* lookup_unix_name
|
||||
*
|
||||
@@ -6571,6 +6664,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;
|
||||
+ status = remove_reparse_point( handle, buffer );
|
||||
+ break;
|
||||
+ }
|
||||
case FSCTL_GET_REPARSE_POINT:
|
||||
{
|
||||
REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
|
||||
--
|
||||
2.37.2
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
From 246a50e7cf09bd1d77d327e170c2d7432e0d5f5e Mon Sep 17 00:00:00 2001
|
||||
From: Lorenzo Ferrillo <lorenzofersteam@live.it>
|
||||
Date: Mon, 6 May 2024 23:46:44 +0200
|
||||
Subject: [PATCH] Return correct status value if handle is not a reparse point
|
||||
|
||||
---
|
||||
dlls/ntdll/unix/file.c | 19 ++++++++++++++-----
|
||||
1 file changed, 14 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
|
||||
index 2bfb9b7d051..93732543ae4 100644
|
||||
--- a/dlls/ntdll/unix/file.c
|
||||
+++ b/dlls/ntdll/unix/file.c
|
||||
@@ -3944,9 +3944,11 @@ NTSTATUS get_reparse_point_unix(const char *unix_name, REPARSE_DATA_BUFFER *buff
|
||||
char *p;
|
||||
|
||||
ret = readlink( unix_name, link_path, sizeof(link_path) );
|
||||
+
|
||||
if (ret < 0)
|
||||
{
|
||||
- status = errno_to_status( errno );
|
||||
+ if (errno == EINVAL) status = STATUS_NOT_A_REPARSE_POINT;
|
||||
+ else status = errno_to_status( errno );
|
||||
goto cleanup;
|
||||
}
|
||||
link_path_len = ret;
|
||||
@@ -7303,10 +7305,17 @@ NTSTATUS WINAPI NtFsControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
|
||||
}
|
||||
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;
|
||||
+ io->Information = 0;
|
||||
+ if (out_buffer){
|
||||
+ REPARSE_DATA_BUFFER *buffer = (REPARSE_DATA_BUFFER *)out_buffer;
|
||||
+ ULONG size = out_size;
|
||||
+ status = get_reparse_point( handle, buffer, &size );
|
||||
+ if (status == STATUS_SUCCESS)
|
||||
+ io->Information = size;
|
||||
+ }
|
||||
+ else {
|
||||
+ status = STATUS_INVALID_USER_BUFFER;
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
case FSCTL_SET_REPARSE_POINT:
|
||||
--
|
||||
2.43.0
|
||||
|
||||
Reference in New Issue
Block a user