From: Elizabeth Figura zfigura@codeweavers.com
--- server/fd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/server/fd.c b/server/fd.c index 4db5209ee4c..f70bec354a3 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2859,8 +2859,10 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da set_error( STATUS_OBJECT_PATH_SYNTAX_BAD ); return; } - if (!(name = mem_alloc( len + 1 ))) return; + if (!(name = mem_alloc( len + 2 ))) return; memcpy( name, nameptr, len ); + if (fd->unix_name[strlen( fd->unix_name ) - 1] == '?' && name[len - 1] != '?') + name[len++] = '?'; name[len] = 0;
if (root)
From: "Erich E. Hoover" erich.e.hoover@gmail.com
--- dlls/kernelbase/file.c | 4 ++-- dlls/ntdll/tests/file.c | 15 +++------------ 2 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 79cb99cbe3c..e2da94831df 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2553,7 +2553,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH MoveFileWithProgressW( const WCHAR *source, const InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL ); 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;
@@ -2672,7 +2672,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH ReplaceFileW( const WCHAR *replaced, const WCHAR * } attr.ObjectName = &nt_replacement_name; status = NtOpenFile( &hReplacement, GENERIC_READ | GENERIC_WRITE | DELETE | WRITE_DAC | SYNCHRONIZE, - &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); + &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT ); RtlFreeUnicodeString(&nt_replacement_name); if (!set_ntstatus( status )) return FALSE; NtClose( hReplacement ); diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 0b4c7503511..a0978dd901e 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6928,20 +6928,11 @@ static void test_reparse_points(void)
handle2 = CreateFileW( path, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0 ); - todo_wine ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); - todo_wine ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); - if (handle2 != INVALID_HANDLE_VALUE) CloseHandle( handle2 ); + ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); + ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() );
ret = MoveFileW( path2, path ); - todo_wine ok( ret == TRUE, "got error %lu\n", GetLastError() ); - if (!ret) - { - /* undo what we incorrectly did above */ - WCHAR target[MAX_PATH]; - swprintf( target, ARRAY_SIZE(target), L"%stestreparse_file", temp_path ); - ret = MoveFileW( path2, target ); - ok( ret == TRUE, "got error %lu\n", GetLastError() ); - } + ok( ret == TRUE, "got error %lu\n", GetLastError() );
handle2 = CreateFileW( path2, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, 0 ); ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() );
From: Elizabeth Figura zfigura@codeweavers.com
The broken deletion logic mentioned here is not specific to reparse points. Essentially, we delay deletion until all inodes are closed, but this is wrong; it should be tracked per path instead. --- dlls/kernelbase/file.c | 2 +- dlls/ntdll/tests/file.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index e2da94831df..d8a7c2abd74 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -970,7 +970,7 @@ BOOL WINAPI CreateHardLinkW( LPCWSTR dest, LPCWSTR source, SECURITY_ATTRIBUTES *
InitializeObjectAttributes( &attr, &ntSource, OBJ_CASE_INSENSITIVE, 0, NULL ); if (!(ret = set_ntstatus( NtOpenFile( &file, SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_SYNCHRONOUS_IO_NONALERT ) ))) + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT ) ))) goto done;
info->ReplaceIfExists = FALSE; diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index a0978dd901e..aa91f5a90aa 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6873,20 +6873,20 @@ static void test_reparse_points(void) ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); status = NtQueryInformationFile( handle2, &io, &tag_info, sizeof(tag_info), FileAttributeTagInformation ); ok( !status, "got %#lx\n", status ); - todo_wine ok( tag_info.FileAttributes == (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT), + ok( tag_info.FileAttributes == (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT), "got attributes %#lx\n", tag_info.FileAttributes ); - todo_wine ok( tag_info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got tag %#lx\n", tag_info.ReparseTag ); + ok( tag_info.ReparseTag == IO_REPARSE_TAG_SYMLINK, "got tag %#lx\n", tag_info.ReparseTag ); status = NtQueryInformationFile( handle2, &io, &std_info, sizeof(std_info), FileStandardInformation ); ok( !status, "got %#lx\n", status ); todo_wine ok( !std_info.AllocationSize.QuadPart, "got size %#I64x\n", std_info.AllocationSize.QuadPart ); - todo_wine ok( !std_info.EndOfFile.QuadPart, "got eof %#I64x\n", std_info.EndOfFile.QuadPart ); + ok( !std_info.EndOfFile.QuadPart, "got eof %#I64x\n", std_info.EndOfFile.QuadPart ); ok( std_info.NumberOfLinks == 2, "got %lu links\n", std_info.NumberOfLinks ); ok( !std_info.Directory, "got directory %u\n", std_info.Directory ); status = NtQueryInformationFile( handle, &io, &internal_info, sizeof(internal_info), FileInternalInformation ); ok( !status, "got %#lx\n", status ); status = NtQueryInformationFile( handle2, &io, &internal_info2, sizeof(internal_info2), FileInternalInformation ); ok( !status, "got %#lx\n", status ); - todo_wine ok( internal_info.IndexNumber.QuadPart == internal_info2.IndexNumber.QuadPart, "got ids %#I64x vs %#I64x\n", + ok( internal_info.IndexNumber.QuadPart == internal_info2.IndexNumber.QuadPart, "got ids %#I64x vs %#I64x\n", internal_info.IndexNumber.QuadPart, internal_info2.IndexNumber.QuadPart ); CloseHandle( handle2 );
@@ -6901,14 +6901,14 @@ static void test_reparse_points(void)
/* Clean up the hard link, and show in the process that DeleteFile() * deletes the symlink, not the target. */ - ret = DeleteFileW( path2 ); ok( ret == TRUE, "got error %lu\n", GetLastError() );
handle2 = CreateFileW( path2, GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0 ); - ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); - ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); + todo_wine ok( handle2 == INVALID_HANDLE_VALUE, "expected failure\n" ); + todo_wine ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); + if (handle2 != INVALID_HANDLE_VALUE) CloseHandle( handle2 );
handle2 = CreateFileW( path2, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, 0, 0 ); ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); @@ -6923,6 +6923,10 @@ static void test_reparse_points(void)
/* MoveFile(). */
+ /* FIXME: our deletion logic is broken where hardlinks are concerned; + * use a new path to work around that */ + swprintf( path2, ARRAY_SIZE(path2), L"%stestreparse_filelink3", temp_path ); + ret = MoveFileW( path, path2 ); ok( ret == TRUE, "got error %lu\n", GetLastError() );
From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/kernelbase/file.c | 3 ++- dlls/ntdll/tests/file.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index d8a7c2abd74..2db98df0e3a 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2910,7 +2910,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetFileAttributesW( LPCWSTR name, DWORD attributes }
InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL ); - status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT ); + status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT ); RtlFreeUnicodeString( &nt_name );
if (status == STATUS_SUCCESS) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index aa91f5a90aa..159eaeb834e 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6961,7 +6961,7 @@ static void test_reparse_points(void) "got attributes %#x\n", ret );
ret = GetFileAttributesW( path2 ); - todo_wine ok( ret == FILE_ATTRIBUTE_ARCHIVE, "got attributes %#x\n", ret ); + ok( ret == FILE_ATTRIBUTE_ARCHIVE, "got attributes %#x\n", ret );
status = NtQueryAttributesFile( &attr, &basic_info ); todo_wine ok( !status, "got %#lx\n", status );
From: Elizabeth Figura zfigura@codeweavers.com
Note that we don't want to use collapse_path() as-is, because everything else it does (i.e. forward slashes, duplicate slashes, trailing dots) should explicitly not apply to symlinks. We also should fail when rewinding past the drive root, rather than simply ignoring those .. segments.
The FIXME introduced here related to unwinding past the RootDirectory handle is not particularly problematic to address (we just need to query the server for its whole path). I've left it as a FIXME simply because it takes extra work to implement and is unlikely to come up; RootDirectory not used anywhere in kernel32 or kernelbase file code. --- dlls/ntdll/tests/file.c | 12 +++--- dlls/ntdll/unix/file.c | 96 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 159eaeb834e..1731e1e39e4 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -7050,7 +7050,7 @@ static void test_reparse_points(void)
RtlInitUnicodeString( &nameW, L"testreparse_dirlink\file" ); status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - todo_wine ok( !status, "got %#lx\n", status ); + ok( !status, "got %#lx\n", status ); NtClose( handle2 );
data_size = init_reparse_symlink( &data, L".\testreparse_dir\..\.\testreparse_dir2\.", SYMLINK_FLAG_RELATIVE ); @@ -7060,13 +7060,13 @@ static void test_reparse_points(void) RtlInitUnicodeString( &nameW, L"testreparse_dirlink" ); status = NtCreateFile( &handle2, GENERIC_ALL, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0 ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status );
status = NtCreateFile( &handle2, GENERIC_ALL, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN_IF, FILE_DIRECTORY_FILE, NULL, 0 ); - todo_wine ok( !status, "got %#lx\n", status ); + ok( !status, "got %#lx\n", status ); status = NtSetInformationFile( handle2, &io, &fdi, sizeof(fdi), FileDispositionInformation ); - todo_wine ok( !status, "got %#lx\n", status ); + ok( !status, "got %#lx\n", status ); NtClose( handle2 );
data_size = init_reparse_symlink( &data, L"./testreparse_dir", SYMLINK_FLAG_RELATIVE ); @@ -7088,7 +7088,7 @@ static void test_reparse_points(void) ok( !status, "got %#lx\n", status );
status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - todo_wine ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx\n", status );
data_size = init_reparse_symlink( &data, L"", SYMLINK_FLAG_RELATIVE ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT, data, data_size, NULL, 0 ); @@ -7110,7 +7110,7 @@ static void test_reparse_points(void)
RtlInitUnicodeString( &nameW, L"testreparse_dirlink\" ); status = NtOpenFile( &handle2, READ_CONTROL, &attr, &io, 0, 0 ); - todo_wine ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status ); + ok( status == STATUS_IO_REPARSE_DATA_INVALID, "got %#lx\n", status );
/* Create an absolute symlink. */
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 01e9c1bb9ed..5cabb6c37ff 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -3935,6 +3935,76 @@ static NTSTATUS resolve_absolute_reparse_point( const WCHAR *target, unsigned in }
+/* limited version of collapse_path() that only deals with . and .. elements + * in relative symlinks */ +static NTSTATUS collapse_relative_symlink( WCHAR *path, unsigned int len, unsigned int *ret_len ) +{ + const WCHAR *end = path + len; + WCHAR *p, *start, *next; + + if (path[0] == '\') + { + p = path + 4; + while (*p && *p != '\') p++; + p++; + } + else + { + p = path; + } + start = p; + + while (p < end) + { + if (*p == '.') + { + if (p + 1 == end) /* final . */ + { + if (p > start) p--; + end = p; + continue; + } + else if (p[1] == '\') /* .\ component */ + { + next = p + 2; + memmove( p, next, (end - next) * sizeof(WCHAR) ); + end -= 2; + continue; + } + else if (p[1] == '.') + { + if (p + 2 == end) /* final .. */ + { + if (p == start) return STATUS_IO_REPARSE_DATA_INVALID; + p--; + while (p > start && p[-1] != '\') p--; + if (p > start) p--; + end = p; + continue; + } + else if (p[2] == '\') /* ..\ component */ + { + if (p == start) return STATUS_IO_REPARSE_DATA_INVALID; + next = p + 3; + p--; + while (p > start && p[-1] != '\') p--; + memmove( p, next, (end - next) * sizeof(WCHAR) ); + end -= (next - p); + continue; + } + } + } + + /* skip to the next component */ + while (p < end && *p != '\') p++; + if (p < end) p++; + } + + *ret_len = end - path; + return STATUS_SUCCESS; +} + + static NTSTATUS resolve_reparse_point( int fd, int root_fd, OBJECT_ATTRIBUTES *attr, UNICODE_STRING *nt_name, unsigned int nt_pos, unsigned int reparse_len, char **unix_name, int unix_len, int pos, UINT disposition, BOOL open_reparse, BOOL is_unix, unsigned int reparse_count ) @@ -3980,6 +4050,7 @@ static NTSTATUS resolve_reparse_point( int fd, int root_fd, OBJECT_ATTRIBUTES *a
if (data->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) { + unsigned int collapsed_len; WCHAR *new_nt_name;
TRACE( "target %s\n", debugstr_wn(target, target_len) ); @@ -3998,17 +4069,32 @@ static NTSTATUS resolve_reparse_point( int fd, int root_fd, OBJECT_ATTRIBUTES *a
memcpy( new_nt_name, name, nt_pos * sizeof(WCHAR) ); memcpy( new_nt_name + nt_pos, target, target_len * sizeof(WCHAR) ); + + if ((status = collapse_relative_symlink( new_nt_name, nt_pos + target_len, &collapsed_len ))) + { + if (attr->RootDirectory) + { + /* FIXME: it's legal to unwind past the root directory (but + * not past the volume root), which we can't detect here. + * We need to retrieve the whole NT name */ + FIXME( "attempt to unwind past root directory %s\n", debugstr_wn(target, target_len) ); + } + free( new_nt_name ); + free( data ); + return status; + } + if (remainder_len) { - if (target[target_len - 1] != '\') - new_nt_name[nt_pos + target_len++] = '\'; - memcpy( new_nt_name + nt_pos + target_len, remainder, remainder_len * sizeof(WCHAR) ); + if (new_nt_name[collapsed_len - 1] != '\') + new_nt_name[collapsed_len++] = '\'; + memcpy( new_nt_name + collapsed_len, remainder, remainder_len * sizeof(WCHAR) ); } - new_nt_name[nt_pos + target_len + remainder_len] = 0; + new_nt_name[collapsed_len + remainder_len] = 0;
free( nt_name->Buffer ); nt_name->Buffer = new_nt_name; - nt_name->Length = (nt_pos + target_len + remainder_len) * sizeof(WCHAR); + nt_name->Length = (collapsed_len + remainder_len) * sizeof(WCHAR); nt_name->MaximumLength = nt_name->Length + sizeof(WCHAR); attr->ObjectName = nt_name;
This merge request was approved by Elizabeth Figura.