Also unifies hidden file handling.
Fixes bug [53826](https://bugs.winehq.org/show_bug.cgi?id=53826).
-- v12: ntdll: Try to avoid requesting the file name for a handle. ntdll: Ensure that attributes are kept the same when renaming or hard-linking a file. ntdll: Set xattr in NtCreateFile if inferred and requested attributes don't match. ntdll: Only infer hidden attribute from file name if xattr is not present. ntdll: Handle hidden file names in fd_get_file_info. ntdll: Pass file path into fd_get_file_info if available. ntdll: Do not compute file attributes for info classes that don't need them. ntdll: Handle hidden file names inside get_file_info instead of after it. ntdll: Do not open-code hidden file handling in get_dir_data_entry. ntdll/tests: Add test for file attributes of files with names beginning with a dot.
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/ntdll/tests/file.c | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 6186afdfb63..50d869e5164 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4051,6 +4051,87 @@ static void test_file_attribute_tag_information(void) CloseHandle( h ); }
+#define lok ok_(__FILE__, line) +#define rename_file(h,f) rename_file_(__LINE__,(h),(f)) +static BOOL rename_file_( int line, HANDLE h, const WCHAR *filename ) +{ + FILE_RENAME_INFORMATION *fri; + UNICODE_STRING ntpath; + IO_STATUS_BLOCK io; + NTSTATUS status; + BOOLEAN ret; + ULONG size; + + ret = pRtlDosPathNameToNtPathName_U( filename, &ntpath, NULL, NULL ); + lok( ret, "RtlDosPathNameToNtPathName_U failed\n" ); + if (!ret) return FALSE; + + size = offsetof( FILE_RENAME_INFORMATION, FileName ) + ntpath.Length; + fri = HeapAlloc( GetProcessHeap(), 0, size ); + lok( fri != NULL, "HeapAlloc failed\n" ); + if (!fri) return FALSE; + fri->ReplaceIfExists = TRUE; + fri->RootDirectory = NULL; + fri->FileNameLength = ntpath.Length; + memcpy( fri->FileName, ntpath.Buffer, ntpath.Length ); + pRtlFreeUnicodeString( &ntpath ); + + status = pNtSetInformationFile( h, &io, fri, size, FileRenameInformation ); + HeapFree( GetProcessHeap(), 0, fri ); + lok( status == STATUS_SUCCESS, "got %#lx\n", status ); + return status == STATUS_SUCCESS; +} +#undef lok + +static void test_dotfile_file_attributes(void) +{ + char temppath[MAX_PATH], filename[MAX_PATH]; + WCHAR temppathW[MAX_PATH], filenameW[MAX_PATH]; + FILE_BASIC_INFORMATION info = {}; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD attrs; + HANDLE h; + + GetTempPathA( MAX_PATH, temppath ); + GetTempFileNameA( temppath, ".foo", 0, filename ); + h = CreateFileA( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0 ); + ok( h != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + if (h == INVALID_HANDLE_VALUE) return; + + status = nt_get_file_attrs(filename, &attrs); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + todo_wine ok( !(attrs & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + CloseHandle( h ); + + GetTempPathW( MAX_PATH, temppathW ); + GetTempFileNameW( temppathW, L"foo", 0, filenameW ); + h = CreateFileW( filenameW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0 ); + ok( h != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + if (h == INVALID_HANDLE_VALUE) return; + + GetTempFileNameW( temppathW, L".foo", 0, filenameW ); + if (!rename_file( h, filenameW )) return; + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + GetTempFileNameW( temppathW, L"foo", 0, filenameW ); + if (!rename_file( h, filenameW )) return; + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + CloseHandle( h ); +} + static void test_file_mode(void) { UNICODE_STRING file_name, pipe_dev_name, mountmgr_dev_name, mailslot_dev_name; @@ -5499,6 +5580,7 @@ START_TEST(file) test_file_id_information(); test_file_access_information(); test_file_attribute_tag_information(); + test_dotfile_file_attributes(); test_file_mode(); test_file_readonly_access(); test_query_volume_information_file();
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 957d9f3b801..89eaf859dd1 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1303,7 +1303,7 @@ static BOOL is_hidden_file( const UNICODE_STRING *name ) end = p = name->Buffer + name->Length/sizeof(WCHAR); while (p > name->Buffer && p[-1] == '\') p--; while (p > name->Buffer && p[-1] != '\') p--; - return (p < end && *p == '.'); + return (p < end && p + 1 != end && p[0] == '.' && p[1] != '\' && (p[1] != '.' || (p + 2 != end && p[2] != '\'))); }
@@ -2224,6 +2224,7 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I union file_directory_info *info; struct stat st; ULONG name_len, start, dir_size, attributes; + UNICODE_STRING name;
if (get_file_info( names->unix_name, &st, &attributes ) == -1) { @@ -2253,8 +2254,8 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I { if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */
- if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] && - (names->long_name[1] != '.' || names->long_name[2])) + RtlInitUnicodeString( &name, names->long_name ); + if (is_hidden_file( &name )) attributes |= FILE_ATTRIBUTE_HIDDEN;
fill_file_info( &st, attributes, info, class );
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 89eaf859dd1..8814da1aac4 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1292,18 +1292,18 @@ static BOOLEAN get_dir_case_sensitivity( const char *dir ) /*********************************************************************** * is_hidden_file * - * Check if the specified file should be hidden based on its name and the show dot files option. + * Check if the specified file should be hidden based on its unix path and the show dot files option. */ -static BOOL is_hidden_file( const UNICODE_STRING *name ) +static BOOL is_hidden_file( const char *name ) { - WCHAR *p, *end; + const char *p, *end;
if (show_dot_files) return FALSE;
- end = p = name->Buffer + name->Length/sizeof(WCHAR); - while (p > name->Buffer && p[-1] == '\') p--; - while (p > name->Buffer && p[-1] != '\') p--; - return (p < end && p + 1 != end && p[0] == '.' && p[1] != '\' && (p[1] != '.' || (p + 2 != end && p[2] != '\'))); + end = p = name + strlen( name ); + while (p > name && p[-1] == '/') p--; + while (p > name && p[-1] != '/') p--; + return (p < end && p + 1 != end && p[0] == '.' && p[1] != '/' && (p[1] != '.' || (p + 2 != end && p[2] != '/'))); }
@@ -1677,6 +1677,9 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr ) } *attr |= get_file_attributes( st );
+ if (is_hidden_file( path )) + *attr |= FILE_ATTRIBUTE_HIDDEN; + attr_len = xattr_get( path, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 ); if (attr_len != -1) *attr |= parse_samba_dos_attrib_data( attr_data, attr_len ); @@ -2224,7 +2227,6 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I union file_directory_info *info; struct stat st; ULONG name_len, start, dir_size, attributes; - UNICODE_STRING name;
if (get_file_info( names->unix_name, &st, &attributes ) == -1) { @@ -2253,11 +2255,6 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I if (class != FileNamesInformation) { if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */ - - RtlInitUnicodeString( &name, names->long_name ); - if (is_hidden_file( &name )) - attributes |= FILE_ATTRIBUTE_HIDDEN; - fill_file_info( &st, attributes, info, class ); }
@@ -4214,7 +4211,6 @@ NTSTATUS WINAPI NtQueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr, info->AllocationSize = std.AllocationSize; info->EndOfFile = std.EndOfFile; info->FileAttributes = basic.FileAttributes; - if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; } free( unix_name ); } @@ -4245,10 +4241,7 @@ NTSTATUS WINAPI NtQueryAttributesFile( const OBJECT_ATTRIBUTES *attr, FILE_BASIC else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) status = STATUS_INVALID_INFO_CLASS; else - { status = fill_file_info( &st, attributes, info, FileBasicInformation ); - if (is_hidden_file( attr->ObjectName )) info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; - } free( unix_name ); } else WARN( "%s not found (%x)\n", debugstr_us(attr->ObjectName), status );
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 8814da1aac4..e24e77716d6 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1570,6 +1570,13 @@ static BOOL fd_is_mount_point( int fd, const struct stat *st ) }
+/* get the stat info for a file (by file descriptor) */ +static int fd_get_file_stat( int fd, struct stat *st ) +{ + return fstat( fd, st ); +} + + /* get the stat info and file attributes for a file (by file descriptor) */ static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr ) { @@ -1577,7 +1584,7 @@ static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULON int attr_len, ret;
*attr = 0; - ret = fstat( fd, st ); + ret = fd_get_file_stat( fd, st ); if (ret == -1) return ret; *attr |= get_file_attributes( st ); /* consider mount points to be reparse points (IO_REPARSE_TAG_MOUNT_POINT) */ @@ -4361,10 +4368,10 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, { FILE_STANDARD_INFORMATION *info = ptr;
- if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); + if (fd_get_file_stat( fd, &st ) == -1) status = errno_to_status( errno ); else { - fill_file_info( &st, attr, info, class ); + fill_file_info( &st, 0, info, class ); info->DeletePending = FALSE; /* FIXME */ } } @@ -4378,8 +4385,8 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, } break; case FileInternalInformation: - if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); - else fill_file_info( &st, attr, ptr, class ); + if (fd_get_file_stat( fd, &st ) == -1) status = errno_to_status( errno ); + else fill_file_info( &st, 0, ptr, class ); break; case FileEaInformation: { @@ -4388,8 +4395,8 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, } break; case FileEndOfFileInformation: - if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); - else fill_file_info( &st, attr, ptr, class ); + if (fd_get_file_stat( fd, &st ) == -1) status = errno_to_status( errno ); + else fill_file_info( &st, 0, ptr, class ); break; case FileAllInformation: { @@ -4504,7 +4511,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, } break; case FileIdInformation: - if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); + if (fd_get_file_stat( fd, &st ) == -1) status = errno_to_status( errno ); else { struct mountmgr_unix_drive drive;
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 57 ++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index e24e77716d6..28740130e14 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1578,7 +1578,7 @@ static int fd_get_file_stat( int fd, struct stat *st )
/* get the stat info and file attributes for a file (by file descriptor) */ -static int fd_get_file_info( int fd, unsigned int options, struct stat *st, ULONG *attr ) +static int fd_get_file_info( int fd, const char *unix_name, unsigned int options, struct stat *st, ULONG *attr ) { char attr_data[65]; int attr_len, ret; @@ -4357,13 +4357,21 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, switch (class) { case FileBasicInformation: - if (fd_get_file_info( fd, options, &st, &attr ) == -1) - status = errno_to_status( errno ); - else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) - status = STATUS_INVALID_INFO_CLASS; - else - fill_file_info( &st, attr, ptr, class ); - break; + { + char *unix_name; + + if (server_get_unix_name( handle, &unix_name )) + unix_name = NULL; + + if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) + status = errno_to_status( errno ); + else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) + status = STATUS_INVALID_INFO_CLASS; + else + fill_file_info( &st, attr, ptr, class ); + free( unix_name ); + break; + } case FileStandardInformation: { FILE_STANDARD_INFORMATION *info = ptr; @@ -4403,10 +4411,13 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, FILE_ALL_INFORMATION *info = ptr; char *unix_name;
- if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); + status = server_get_unix_name( handle, &unix_name ); + if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) status = errno_to_status( errno ); + else if (status) + break; else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) status = STATUS_INVALID_INFO_CLASS; - else if (!(status = server_get_unix_name( handle, &unix_name ))) + else { LONG name_len = len - FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName);
@@ -4419,9 +4430,9 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, info->AlignmentInformation.AlignmentRequirement = 1; /* FIXME */
status = fill_name_info( unix_name, &info->NameInformation, &name_len ); - free( unix_name ); io->Information = FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + name_len; } + free( unix_name ); } break; case FileMailslotQueryInformation: @@ -4525,16 +4536,24 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, } break; case FileAttributeTagInformation: - if (fd_get_file_info( fd, options, &st, &attr ) == -1) status = errno_to_status( errno ); - else { - FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr; - info->FileAttributes = attr; - info->ReparseTag = 0; /* FIXME */ - if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st )) - info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + char *unix_name; + + if (server_get_unix_name( handle, &unix_name )) + unix_name = NULL; + + if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) status = errno_to_status( errno ); + else + { + FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr; + info->FileAttributes = attr; + info->ReparseTag = 0; /* FIXME */ + if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st )) + info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + } + free( unix_name ); + break; } - break; default: FIXME("Unsupported class (%d)\n", class); status = STATUS_NOT_IMPLEMENTED;
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/tests/file.c | 4 ++-- dlls/ntdll/unix/file.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 50d869e5164..f5305060806 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4105,7 +4105,7 @@ static void test_dotfile_file_attributes(void)
status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); ok( status == STATUS_SUCCESS, "got %#lx\n", status ); - ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + todo_wine ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes );
CloseHandle( h );
@@ -4120,7 +4120,7 @@ static void test_dotfile_file_attributes(void)
status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); ok( status == STATUS_SUCCESS, "got %#lx\n", status ); - ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + todo_wine ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes );
GetTempFileNameW( temppathW, L"foo", 0, filenameW ); if (!rename_file( h, filenameW )) return; diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 28740130e14..f1056a94b2f 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1591,6 +1591,9 @@ static int fd_get_file_info( int fd, const char *unix_name, unsigned int options if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
+ if (unix_name && is_hidden_file( unix_name )) + *attr |= FILE_ATTRIBUTE_HIDDEN; + attr_len = xattr_fget( fd, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 ); if (attr_len != -1) *attr |= parse_samba_dos_attrib_data( attr_data, attr_len );
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index f1056a94b2f..f685c99ab52 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1591,14 +1591,13 @@ static int fd_get_file_info( int fd, const char *unix_name, unsigned int options if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, st )) *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
- if (unix_name && is_hidden_file( unix_name )) - *attr |= FILE_ATTRIBUTE_HIDDEN; - attr_len = xattr_fget( fd, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 ); if (attr_len != -1) *attr |= parse_samba_dos_attrib_data( attr_data, attr_len ); else { + if (unix_name && is_hidden_file( unix_name )) + *attr |= FILE_ATTRIBUTE_HIDDEN; if (errno == ENOTSUP) return ret; #ifdef ENODATA if (errno == ENODATA) return ret; @@ -1687,14 +1686,13 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr ) } *attr |= get_file_attributes( st );
- if (is_hidden_file( path )) - *attr |= FILE_ATTRIBUTE_HIDDEN; - attr_len = xattr_get( path, SAMBA_XATTR_DOS_ATTRIB, attr_data, sizeof(attr_data)-1 ); if (attr_len != -1) *attr |= parse_samba_dos_attrib_data( attr_data, attr_len ); else { + if (is_hidden_file( path )) + *attr |= FILE_ATTRIBUTE_HIDDEN; if (errno == ENOTSUP) return ret; #ifdef ENODATA if (errno == ENODATA) return ret;
From: Torge Matthies tmatthies@codeweavers.com
And make sure it doesn't get deleted.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53826 Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/tests/file.c | 4 ++-- dlls/ntdll/unix/file.c | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index f5305060806..efc591f52e8 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4101,11 +4101,11 @@ static void test_dotfile_file_attributes(void)
status = nt_get_file_attrs(filename, &attrs); ok( status == STATUS_SUCCESS, "got %#lx\n", status ); - todo_wine ok( !(attrs & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + ok( !(attrs & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes );
status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); ok( status == STATUS_SUCCESS, "got %#lx\n", status ); - todo_wine ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes );
CloseHandle( h );
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index f685c99ab52..36b4a18302a 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1609,11 +1609,11 @@ static int fd_get_file_info( int fd, const char *unix_name, unsigned int options }
-static int fd_set_dos_attrib( int fd, UINT attr ) +static int fd_set_dos_attrib( int fd, UINT attr, BOOL force_set ) { /* we only store the HIDDEN and SYSTEM attributes */ attr &= XATTR_ATTRIBS_MASK; - if (attr != 0) + if (force_set || attr != 0) { /* encode the attributes in Samba 3 ASCII format. Samba 4 has extended * this format with more features, but retains compatibility with the @@ -1627,7 +1627,7 @@ static int fd_set_dos_attrib( int fd, UINT attr )
/* set the stat info and file attributes for a file (by file descriptor) */ -NTSTATUS fd_set_file_info( int fd, UINT attr ) +NTSTATUS fd_set_file_info( int fd, UINT attr, BOOL force_set_xattr ) { struct stat st;
@@ -1646,7 +1646,8 @@ NTSTATUS fd_set_file_info( int fd, UINT attr ) } if (fchmod( fd, st.st_mode ) == -1) return errno_to_status( errno );
- if (fd_set_dos_attrib( fd, attr ) == -1 && errno != ENOTSUP) + force_set_xattr = force_set_xattr || st.st_nlink > 1; + if (fd_set_dos_attrib( fd, attr, force_set_xattr ) == -1 && errno != ENOTSUP) WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)\n", errno, strerror( errno ) );
@@ -3972,6 +3973,7 @@ NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBU OBJECT_ATTRIBUTES new_attr; UNICODE_STRING nt_name; char *unix_name; + BOOL name_hidden = FALSE; BOOL created = FALSE; unsigned int status;
@@ -4014,6 +4016,7 @@ NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBU
if (status == STATUS_SUCCESS) { + name_hidden = is_hidden_file( unix_name ); status = open_unix_file( handle, unix_name, access, &new_attr, attributes, sharing, disposition, options, ea_buffer, ea_length ); free( unix_name ); @@ -4041,14 +4044,15 @@ NTSTATUS WINAPI NtCreateFile( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBU break; }
- if (io->Information == FILE_CREATED && (attributes & XATTR_ATTRIBS_MASK)) + if (io->Information == FILE_CREATED && + ((attributes & XATTR_ATTRIBS_MASK) || name_hidden)) { int fd, needs_close;
/* set any DOS extended attributes */ if (!server_get_unix_fd( *handle, 0, &fd, &needs_close, NULL, NULL )) { - if (fd_set_dos_attrib( fd, attributes ) == -1 && errno != ENOTSUP) + if (fd_set_dos_attrib( fd, attributes, TRUE ) == -1 && errno != ENOTSUP) WARN( "Failed to set extended attribute " SAMBA_XATTR_DOS_ATTRIB ". errno %d (%s)", errno, strerror( errno ) ); if (needs_close) close( fd ); @@ -4584,10 +4588,14 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, { const FILE_BASIC_INFORMATION *info = ptr; LARGE_INTEGER mtime, atime; + char *unix_name;
if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) return io->u.Status = status;
+ if ((status = server_get_unix_name( handle, &unix_name ))) + unix_name = NULL; + mtime.QuadPart = info->LastWriteTime.QuadPart == -1 ? 0 : info->LastWriteTime.QuadPart; atime.QuadPart = info->LastAccessTime.QuadPart == -1 ? 0 : info->LastAccessTime.QuadPart;
@@ -4595,9 +4603,13 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, status = set_file_times( fd, &mtime, &atime );
if (status == STATUS_SUCCESS && info->FileAttributes) - status = fd_set_file_info( fd, info->FileAttributes ); + { + BOOL force_xattr = unix_name && is_hidden_file( unix_name ); + status = fd_set_file_info( fd, info->FileAttributes, force_xattr ); + }
if (needs_close) close( fd ); + free( unix_name ); } else status = STATUS_INVALID_PARAMETER_3; break;
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/tests/file.c | 2 +- dlls/ntdll/unix/file.c | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index efc591f52e8..615942d917e 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4120,7 +4120,7 @@ static void test_dotfile_file_attributes(void)
status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); ok( status == STATUS_SUCCESS, "got %#lx\n", status ); - todo_wine ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes );
GetTempFileNameW( temppathW, L"foo", 0, filenameW ); if (!rename_file( h, filenameW )) return; diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 36b4a18302a..00f4e5c93d5 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4570,6 +4570,33 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, }
+static NTSTATUS refresh_file_attrs( HANDLE handle, BOOL force_set_xattr ) +{ + unsigned int options; + BOOL needs_close; + NTSTATUS status; + char *unix_name; + struct stat st; + ULONG attrib; + int fd; + + if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options ))) + return status; + if (server_get_unix_name( handle, &unix_name )) + unix_name = NULL; + + if (fd_get_file_info( fd, unix_name, options, &st, &attrib ) == -1) + status = errno_to_status( errno ); + else + status = fd_set_file_info( fd, attrib, force_set_xattr ); + + free( unix_name ); + if (needs_close) + close( fd ); + return status; +} + + /****************************************************************************** * NtSetInformationFile (NTDLL.@) */ @@ -4788,7 +4815,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, FILE_RENAME_INFORMATION *info = ptr; UNICODE_STRING name_str, redir; OBJECT_ATTRIBUTES attr; - char *unix_name; + char *unix_name = NULL;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4797,6 +4824,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, get_redirect( &attr, &redir );
status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + if (status == STATUS_SUCCESS && is_hidden_file( unix_name )) + status = refresh_file_attrs( handle, TRUE ); if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE) { SERVER_START_REQ( set_fd_name_info ) @@ -4811,9 +4840,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, status = wine_server_call( req ); } SERVER_END_REQ; - - free( unix_name ); } + free( unix_name ); free( redir.Buffer ); } else status = STATUS_INVALID_PARAMETER_3; @@ -4825,7 +4853,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, FILE_LINK_INFORMATION *info = ptr; UNICODE_STRING name_str, redir; OBJECT_ATTRIBUTES attr; - char *unix_name; + char *unix_name = NULL;
name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; @@ -4834,6 +4862,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, get_redirect( &attr, &redir );
status = nt_to_unix_file_name( &attr, &unix_name, FILE_OPEN_IF ); + if (status == STATUS_SUCCESS && is_hidden_file( unix_name )) + status = refresh_file_attrs( handle, TRUE ); if (status == STATUS_SUCCESS || status == STATUS_NO_SUCH_FILE) { SERVER_START_REQ( set_fd_name_info ) @@ -4848,9 +4878,8 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, status = wine_server_call( req ); } SERVER_END_REQ; - - free( unix_name ); } + free( unix_name ); free( redir.Buffer ); } else status = STATUS_INVALID_PARAMETER_3;
From: Torge Matthies tmatthies@codeweavers.com
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/ntdll/unix/file.c | 126 ++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 70 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 00f4e5c93d5..c842399bdf6 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1577,8 +1577,40 @@ static int fd_get_file_stat( int fd, struct stat *st ) }
+static unsigned int server_get_unix_name( HANDLE handle, char **unix_name ) +{ + data_size_t size = 1024; + unsigned int ret; + char *name; + + for (;;) + { + if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY; + + SERVER_START_REQ( get_handle_unix_name ) + { + req->handle = wine_server_obj_handle( handle ); + wine_server_set_reply( req, name, size ); + ret = wine_server_call( req ); + size = reply->name_len; + } + SERVER_END_REQ; + + if (!ret) + { + name[size] = 0; + *unix_name = name; + break; + } + free( name ); + if (ret != STATUS_BUFFER_OVERFLOW) break; + } + return ret; +} + + /* get the stat info and file attributes for a file (by file descriptor) */ -static int fd_get_file_info( int fd, const char *unix_name, unsigned int options, struct stat *st, ULONG *attr ) +static int fd_get_file_info( int fd, char *unix_name, HANDLE handle, unsigned int options, struct stat *st, ULONG *attr ) { char attr_data[65]; int attr_len, ret; @@ -1598,6 +1630,12 @@ static int fd_get_file_info( int fd, const char *unix_name, unsigned int options { if (unix_name && is_hidden_file( unix_name )) *attr |= FILE_ATTRIBUTE_HIDDEN; + else if (handle && server_get_unix_name( handle, &unix_name )) + { + if (is_hidden_file( unix_name )) + *attr |= FILE_ATTRIBUTE_HIDDEN; + free( unix_name ); + } if (errno == ENOTSUP) return ret; #ifdef ENODATA if (errno == ENODATA) return ret; @@ -1946,38 +1984,6 @@ static NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr, return STATUS_SUCCESS; }
- -static unsigned int server_get_unix_name( HANDLE handle, char **unix_name ) -{ - data_size_t size = 1024; - unsigned int ret; - char *name; - - for (;;) - { - if (!(name = malloc( size + 1 ))) return STATUS_NO_MEMORY; - - SERVER_START_REQ( get_handle_unix_name ) - { - req->handle = wine_server_obj_handle( handle ); - wine_server_set_reply( req, name, size ); - ret = wine_server_call( req ); - size = reply->name_len; - } - SERVER_END_REQ; - - if (!ret) - { - name[size] = 0; - *unix_name = name; - break; - } - free( name ); - if (ret != STATUS_BUFFER_OVERFLOW) break; - } - return ret; -} - static NTSTATUS fill_name_info( const char *unix_name, FILE_NAME_INFORMATION *info, LONG *name_len ) { WCHAR *nt_name; @@ -4362,21 +4368,13 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, switch (class) { case FileBasicInformation: - { - char *unix_name; - - if (server_get_unix_name( handle, &unix_name )) - unix_name = NULL; - - if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) - status = errno_to_status( errno ); - else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) - status = STATUS_INVALID_INFO_CLASS; - else - fill_file_info( &st, attr, ptr, class ); - free( unix_name ); - break; - } + if (fd_get_file_info( fd, NULL, handle, options, &st, &attr ) == -1) + status = errno_to_status( errno ); + else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) + status = STATUS_INVALID_INFO_CLASS; + else + fill_file_info( &st, attr, ptr, class ); + break; case FileStandardInformation: { FILE_STANDARD_INFORMATION *info = ptr; @@ -4417,7 +4415,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, char *unix_name;
status = server_get_unix_name( handle, &unix_name ); - if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) status = errno_to_status( errno ); + if (fd_get_file_info( fd, unix_name, NULL, options, &st, &attr ) == -1) status = errno_to_status( errno ); else if (status) break; else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) @@ -4541,24 +4539,16 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, } break; case FileAttributeTagInformation: + if (fd_get_file_info( fd, NULL, handle, options, &st, &attr ) == -1) status = errno_to_status( errno ); + else { - char *unix_name; - - if (server_get_unix_name( handle, &unix_name )) - unix_name = NULL; - - if (fd_get_file_info( fd, unix_name, options, &st, &attr ) == -1) status = errno_to_status( errno ); - else - { - FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr; - info->FileAttributes = attr; - info->ReparseTag = 0; /* FIXME */ - if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st )) - info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; - } - free( unix_name ); - break; + FILE_ATTRIBUTE_TAG_INFORMATION *info = ptr; + info->FileAttributes = attr; + info->ReparseTag = 0; /* FIXME */ + if ((options & FILE_OPEN_REPARSE_POINT) && fd_is_mount_point( fd, &st )) + info->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; } + break; default: FIXME("Unsupported class (%d)\n", class); status = STATUS_NOT_IMPLEMENTED; @@ -4575,22 +4565,18 @@ static NTSTATUS refresh_file_attrs( HANDLE handle, BOOL force_set_xattr ) unsigned int options; BOOL needs_close; NTSTATUS status; - char *unix_name; struct stat st; ULONG attrib; int fd;
if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, &options ))) return status; - if (server_get_unix_name( handle, &unix_name )) - unix_name = NULL;
- if (fd_get_file_info( fd, unix_name, options, &st, &attrib ) == -1) + if (fd_get_file_info( fd, NULL, handle, options, &st, &attrib ) == -1) status = errno_to_status( errno ); else status = fd_set_file_info( fd, attrib, force_set_xattr );
- free( unix_name ); if (needs_close) close( fd ); return status;
We don't want to have to retrieve the name from the server all the time just for the hidden flag.
Also this MR is changing way too many things. Please try to limit the changes to the minimum required to address the bug.
On Wed Nov 23 17:48:30 2022 +0000, Alexandre Julliard wrote:
We don't want to have to retrieve the name from the server all the time just for the hidden flag. Also this MR is changing way too many things. Please try to limit the changes to the minimum required to address the bug.
I think I can get this MR down to 4 commits to fix this specific bug (+ 1 if we want the tests too).
That would still leave Wine's hidden file handling inconsistent between different functions that use `fd_get_file_info` vs `get_file_info` vs `get_dir_data_entry` though. I can open another MR that will make those correct in the cases where we already retrieve the file name. But to make everything 100% consistent we either need to just remove the dotfile handling, or always have the file name available.
That would still leave Wine's hidden file handling inconsistent between different functions that use `fd_get_file_info` vs `get_file_info` vs `get_dir_data_entry` though. I can open another MR that will make those correct in the cases where we already retrieve the file name. But to make everything 100% consistent we either need to just remove the dotfile handling, or always have the file name available.
Yes it's inconsistent, but that doesn't seem to matter in practice. It's mostly only used when browsing Unix dirs, and then if the files are hidden, the user is not going to access them, and nothing will query the info from an open handle.