diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index 223b842ed59..dab657ab500 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -159,6 +159,8 @@ union file_directory_info FILE_FULL_DIRECTORY_INFORMATION full; FILE_ID_BOTH_DIRECTORY_INFORMATION id_both; FILE_ID_FULL_DIRECTORY_INFORMATION id_full; + FILE_ID_GLOBAL_TX_DIR_INFORMATION id_tx; + FILE_NAMES_INFORMATION names; }; struct dir_data_buffer @@ -277,6 +279,10 @@ static inline unsigned int dir_info_size( FILE_INFORMATION_CLASS class, unsigned return offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[len] ); case FileIdFullDirectoryInformation: return offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[len] ); + case FileIdGlobalTxDirectoryInformation: + return offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[len] ); + case FileNamesInformation: + return offsetof( FILE_NAMES_INFORMATION, FileName[len] ); default: assert(0); return 0; @@ -1548,12 +1554,6 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I TRACE( "ignoring file %s\n", names->unix_name ); return STATUS_SUCCESS; } - if (!show_dot_files && names->long_name[0] == '.' && names->long_name[1] && - (names->long_name[1] != '.' || names->long_name[2])) - attributes |= FILE_ATTRIBUTE_HIDDEN; - - if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */ - start = dir_info_align( io->Information ); dir_size = dir_info_size( class, 0 ); if (start + dir_size > max_length) return STATUS_MORE_ENTRIES; @@ -1564,12 +1564,21 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I if (*last_info && name_len > max_length) return STATUS_MORE_ENTRIES; info = (union file_directory_info *)((char *)info_ptr + start); - - /* all the structures start with a FileDirectoryInformation layout */ - fill_file_info( &st, attributes, info, class ); info->dir.NextEntryOffset = 0; info->dir.FileIndex = 0; /* NTFS always has 0 here, so let's not bother with it */ + /* all the structures except FileNamesInformation start with a FileDirectoryInformation layout */ + if (class != FileNamesInformation) + { + 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])) + attributes |= FILE_ATTRIBUTE_HIDDEN; + + fill_file_info( &st, attributes, info, class ); + } + switch (class) { case FileDirectoryInformation: @@ -1600,6 +1609,15 @@ static NTSTATUS get_dir_data_entry( struct dir_data *dir_data, void *info_ptr, I info->id_both.FileNameLength = name_len; break; + case FileIdGlobalTxDirectoryInformation: + info->id_tx.TxInfoFlags = 0; + info->id_tx.FileNameLength = name_len; + break; + + case FileNamesInformation: + info->names.FileNameLength = name_len; + break; + default: assert(0); return 0; @@ -1990,13 +2008,23 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, case FileFullDirectoryInformation: case FileIdBothDirectoryInformation: case FileIdFullDirectoryInformation: + case FileIdGlobalTxDirectoryInformation: + case FileNamesInformation: if (length < dir_info_align( dir_info_size( info_class, 1 ))) return STATUS_INFO_LENGTH_MISMATCH; - if (!buffer) return STATUS_ACCESS_VIOLATION; break; + case FileObjectIdInformation: + if (length != sizeof(FILE_OBJECTID_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + return STATUS_INVALID_INFO_CLASS; + case FileQuotaInformation: + if (length != sizeof(FILE_QUOTA_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + return STATUS_INVALID_INFO_CLASS; + case FileReparsePointInformation: + if (length != sizeof(FILE_REPARSE_POINT_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; + return STATUS_INVALID_INFO_CLASS; default: - FIXME( "Unsupported file info class %d\n", info_class ); - return STATUS_NOT_IMPLEMENTED; + return STATUS_INVALID_INFO_CLASS; } + if (!buffer) return STATUS_ACCESS_VIOLATION; if ((status = server_get_unix_fd( handle, FILE_LIST_DIRECTORY, &fd, &needs_close, NULL, NULL )) != STATUS_SUCCESS) return status; diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index b3bd9d68c97..55bdac0eb2a 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2198,6 +2198,13 @@ NTSTATUS fill_file_info( const struct stat *st, ULONG attr, void *ptr, fill_file_info( st, attr, info, FileDirectoryInformation ); } break; + case FileIdGlobalTxDirectoryInformation: + { + FILE_ID_GLOBAL_TX_DIR_INFORMATION *info = ptr; + info->FileId.QuadPart = st->st_ino; + fill_file_info( st, attr, info, FileDirectoryInformation ); + } + break; default: return STATUS_INVALID_INFO_CLASS; diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index 431471a4d57..a135d4fb591 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -329,6 +329,121 @@ static void test_directory_sort( const WCHAR *testdir ) pRtlFreeUnicodeString( &ntdirname ); } +static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *mask ) +{ + IO_STATUS_BLOCK io; + UINT data_size; + ULONG data[256]; + NTSTATUS status; + int class; + + for (class = 0; class < FileMaximumInformation; class++) + { + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + data_size = 0; + memset( data, 0x55, sizeof(data) ); + + status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size, + class, FALSE, mask, TRUE ); + ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + + switch (class) + { + case FileIdGlobalTxDirectoryInformation: + if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) continue; + /* fall through */ + case FileDirectoryInformation: + case FileFullDirectoryInformation: + case FileBothDirectoryInformation: + case FileNamesInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + case FileObjectIdInformation: + case FileQuotaInformation: + case FileReparsePointInformation: + ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %x\n", class, status ); + break; + default: + ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED, + "%u: wrong status %x\n", class, status ); + continue; + } + + for (data_size = 1; data_size < sizeof(data); data_size++) + { + status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size, + class, FALSE, mask, TRUE ); + if (status == STATUS_BUFFER_OVERFLOW) + { + ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == data_size, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0, "%u: wrong offset %x\n", class, data[0] ); + } + else + { + ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); + ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + } + if (status != STATUS_INFO_LENGTH_MISMATCH) break; + } + + switch (class) + { + case FileDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileFullDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileBothDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileNamesInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdBothDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdFullDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileIdGlobalTxDirectoryInformation: + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( data_size == ((offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[1] ) + 7) & ~7), + "%u: wrong size %u\n", class, data_size ); + break; + case FileObjectIdInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_OBJECTID_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + case FileQuotaInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_QUOTA_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + case FileReparsePointInformation: + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( data_size == sizeof(FILE_REPARSE_POINT_INFORMATION), "%u: wrong size %u\n", class, data_size ); + break; + } + } +} + static void test_NtQueryDirectoryFile(void) { OBJECT_ATTRIBUTES attr; @@ -342,6 +457,7 @@ static void test_NtQueryDirectoryFile(void) BYTE data[8192]; FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data; FILE_POSITION_INFORMATION pos_info; + FILE_NAMES_INFORMATION *names; const WCHAR *filename = fbdi->FileName; NTSTATUS status; HANDLE dirh; @@ -453,30 +569,7 @@ static void test_NtQueryDirectoryFile(void) ok( filename[1] == 0x5555, "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); - memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; - data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[0] ); - status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, - FileBothDirectoryInformation, FALSE, &mask, TRUE); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "weong status %x\n", status ); - ok( U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == 0xdeadbeef, "wrong info %lx\n", U(io).Information ); - ok( fbdi->NextEntryOffset == 0x55555555, "wrong offset %x\n", fbdi->NextEntryOffset ); - - data_size = offsetof( FILE_DIRECTORY_INFORMATION, FileName[3] ); - status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, - FileDirectoryInformation, FALSE, &mask, TRUE); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "weong status %x\n", status ); - ok( U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == 0xdeadbeef, "wrong info %lx\n", U(io).Information ); - - data_size = offsetof( FILE_DIRECTORY_INFORMATION, FileName[4] ); - status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, - FileDirectoryInformation, FALSE, &mask, TRUE); - ok( status == STATUS_BUFFER_OVERFLOW, "weong status %x\n", status ); - ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == data_size, "wrong info %lx\n", U(io).Information ); + test_NtQueryDirectoryFile_classes( dirh, &mask ); /* mask may or may not be ignored when restarting the search */ pRtlInitUnicodeString( &mask, dummyW ); @@ -571,6 +664,29 @@ static void test_NtQueryDirectoryFile(void) ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR))); + data_size = ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7) + + offsetof( FILE_NAMES_INFORMATION, FileName[2] ); + memset( data, 0x55, data_size ); + U(io).Status = 0xdeadbeef; + U(io).Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + FileNamesInformation, FALSE, NULL, TRUE ); + ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); + ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); + ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size ); + names = (FILE_NAMES_INFORMATION *)data; + ok( names->NextEntryOffset == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), + "wrong offset %x\n", names->NextEntryOffset ); + ok( names->FileNameLength == sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + ok( names->FileName[0] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); + names = (FILE_NAMES_INFORMATION *)(data + names->NextEntryOffset); + ok( names->NextEntryOffset == 0, "wrong offset %x\n", names->NextEntryOffset ); + ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + filename = names->FileName; + ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", + wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); + pNtClose(dirh); /* create new handle to change mask */ @@ -580,6 +696,7 @@ static void test_NtQueryDirectoryFile(void) pRtlInitUnicodeString( &mask, dummyW ); U(io).Status = 0xdeadbeef; + data_size = sizeof(data); status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, TRUE); ok(status == STATUS_NO_SUCH_FILE, "wrong status %x\n", status); diff --git a/include/winternl.h b/include/winternl.h index 339d807fbdc..048939507d9 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -527,6 +527,23 @@ typedef struct _FILE_ID_BOTH_DIRECTORY_INFORMATION { WCHAR FileName[ANYSIZE_ARRAY]; } FILE_ID_BOTH_DIRECTORY_INFORMATION, *PFILE_ID_BOTH_DIRECTORY_INFORMATION; +typedef struct _FILE_ID_GLOBAL_TX_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + LARGE_INTEGER FileId; + GUID LockingTransactionId; + ULONG TxInfoFlags; + WCHAR FileName[ANYSIZE_ARRAY]; +} FILE_ID_GLOBAL_TX_DIR_INFORMATION, *PFILE_ID_GLOBAL_TX_DIR_INFORMATION; + typedef struct _FILE_BASIC_INFORMATION { LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; @@ -668,6 +685,34 @@ typedef struct _FILE_PIPE_LOCAL_INFORMATION { ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; +typedef struct _FILE_OBJECTID_INFORMATION { + LONGLONG FileReference; + UCHAR ObjectId[16]; + union { + struct { + UCHAR BirthVolumeId[16]; + UCHAR BirthObjectId[16]; + UCHAR DomainId[16]; + } DUMMYSTRUCTNAME; + UCHAR ExtendedInfo[48]; + } DUMMYUNIONNAME; +} FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION; + +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + +typedef struct _FILE_REPARSE_POINT_INFORMATION { + LONGLONG FileReference; + ULONG Tag; +} FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; + typedef struct _FILE_ALL_INFORMATION { FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation;