Commit f282c8e7 authored by Erich E. Hoover's avatar Erich E. Hoover Committed by Vitaly Lipatov

ntdll: Add support for relative symlink creation.

parent 036638bf
......@@ -5333,7 +5333,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;
......@@ -5351,7 +5352,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;
......@@ -5371,6 +5372,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;
......@@ -5453,7 +5455,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());
......@@ -5500,7 +5502,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);
......@@ -5515,7 +5517,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);
......@@ -5577,7 +5579,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());
......@@ -5620,6 +5622,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);
......
......@@ -3327,7 +3327,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 */
......@@ -5978,17 +5978,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)
......@@ -5997,11 +6000,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);
......@@ -6014,8 +6019,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 );
......@@ -6031,11 +6094,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)
......@@ -6056,7 +6132,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 );
......@@ -6106,7 +6182,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;
......
......@@ -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 */
......@@ -2866,6 +2866,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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment