From: Elizabeth Figura zfigura@codeweavers.com
--- dlls/ntdll/tests/file.c | 32 +++++++++++------- server/fd.c | 75 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 12 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index c9a9184ea55..4f2f964e081 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6474,22 +6474,22 @@ static void test_reparse_points(void) CloseHandle( handle2 );
status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, NULL, 0, NULL, 0 ); - todo_wine ok( status == STATUS_INVALID_BUFFER_SIZE, "got %#lx\n", status ); + ok( status == STATUS_INVALID_BUFFER_SIZE, "got %#lx\n", status ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, 1, NULL, 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 ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, data_size, NULL, 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 ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, sizeof(REPARSE_DATA_BUFFER), NULL, 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 ); data->ReparseDataLength = 0; status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, data_size, NULL, 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 ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, sizeof(REPARSE_GUID_DATA_BUFFER), NULL, 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 );
data->ReparseTag = 0; status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, sizeof(REPARSE_DATA_BUFFER), NULL, 0 ); - todo_wine ok( status == STATUS_IO_REPARSE_TAG_INVALID, "got %#lx\n", status ); + ok( status == STATUS_IO_REPARSE_TAG_INVALID, "got %#lx\n", status );
data->ReparseTag = 3; status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, sizeof(REPARSE_DATA_BUFFER), NULL, 0 ); @@ -6500,7 +6500,7 @@ static void test_reparse_points(void) todo_wine ok( !status, "got %#lx\n", status );
status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, data, sizeof(REPARSE_DATA_BUFFER), NULL, 0 ); - todo_wine ok( status == STATUS_NOT_A_REPARSE_POINT, "got %#lx\n", status ); + ok( status == STATUS_NOT_A_REPARSE_POINT, "got %#lx\n", status );
/* Create a dangling symlink, and then open it without * FILE_OPEN_REPARSE_POINT but with FILE_OPEN_IF. This creates the target. @@ -6642,15 +6642,15 @@ static void test_reparse_points(void)
guid_data->ReparseDataLength = 0; status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, guid_data, data_size, NULL, 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 ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, guid_data, sizeof(REPARSE_GUID_DATA_BUFFER), NULL, 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 ); status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, guid_data, sizeof(REPARSE_DATA_BUFFER), NULL, 0 ); - todo_wine ok( !status, "got %#lx\n", status ); + ok( !status, "got %#lx\n", status );
swprintf( path, ARRAY_SIZE(path), L"%s/testreparse_customdir/file", temp_path ); handle2 = CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 ); - todo_wine ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); + ok( handle2 != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); CloseHandle( handle2 );
/* Set the "directory" bit on our custom tag. @@ -6690,6 +6690,14 @@ static void test_reparse_points(void)
ret = DeleteFileW( path ); todo_wine ok( ret == TRUE, "got error %lu\n", GetLastError() ); + if (!ret) + { + guid_data->ReparseDataLength = 0; + status = NtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_DELETE_REPARSE_POINT, guid_data, sizeof(REPARSE_DATA_BUFFER), NULL, 0 ); + ok( !status, "got %#lx\n", status ); + ret = DeleteFileW( path ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + }
status = NtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); ok( !status, "got %#lx\n", status ); diff --git a/server/fd.c b/server/fd.c index 183bc06ca6d..0b8fe21fd07 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2431,6 +2431,22 @@ static int xattr_fset( int filedes, const char *name, const void *value, size_t #endif }
+static int xattr_fremove( int filedes, const char *name ) +{ +#ifdef HAVE_SYS_XATTR_H +# ifdef XATTR_ADDITIONAL_OPTIONS + return fremovexattr( filedes, name, 0 ); +# else + return fremovexattr( filedes, name ); +# endif +#elif defined(HAVE_SYS_EXTATTR_H) + return extattr_delete_fd( filedes, EXTATTR_NAMESPACE_USER, &name[XATTR_USER_PREFIX_LEN] ); +#else + errno = ENOSYS; + return -1; +#endif +} + static void set_reparse_point( struct fd *fd, struct async *async ) { char *reparse_name; @@ -2481,6 +2497,61 @@ static void set_reparse_point( struct fd *fd, struct async *async ) } }
+static void delete_reparse_point( struct fd *fd, struct async *async ) +{ + const REPARSE_DATA_BUFFER *data = get_req_data(); + char *base_name; + size_t len; + + if (!fd->unix_name) + { + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + return; + } + + if (!get_req_data_size()) + { + set_error( STATUS_INVALID_BUFFER_SIZE ); + return; + } + + len = strlen( fd->unix_name ); + if (fd->unix_name[len - 1] != '?') + { + set_error( STATUS_NOT_A_REPARSE_POINT ); + return; + } + + if (get_req_data_size() != sizeof(REPARSE_DATA_BUFFER) || data->ReparseDataLength) + { + set_error( STATUS_IO_REPARSE_DATA_INVALID ); + return; + } + + if (!data->ReparseTag) + { + set_error( STATUS_IO_REPARSE_TAG_INVALID ); + return; + } + + if (!(base_name = mem_alloc( len ))) + return; + memcpy( base_name, fd->unix_name, len - 1 ); + base_name[len - 1] = 0; + + if (rename( fd->unix_name, base_name ) < 0) + { + file_set_error(); + free( base_name ); + return; + } + + free( fd->unix_name ); + fd->closed->unix_name = fd->unix_name = base_name; + + xattr_fremove( fd->unix_fd, XATTR_REPARSE ); +} + /* default read() routine */ void no_fd_read( struct fd *fd, struct async *async, file_pos_t pos ) { @@ -2603,6 +2674,10 @@ void default_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) set_reparse_point( fd, async ); break;
+ case FSCTL_DELETE_REPARSE_POINT: + delete_reparse_point( fd, async ); + break; + default: set_error( STATUS_NOT_SUPPORTED ); }