diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index 41e7115ec51..f7fa951b6b8 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -180,6 +180,18 @@ union file_directory_info FILE_ID_FULL_DIRECTORY_INFORMATION id_full; }; +struct dir_data +{ + struct file_identity id; /* directory file identity */ + UNICODE_STRING *mask; /* file search mask */ + UNICODE_STRING mask_str; +}; + +static const unsigned int dir_data_cache_initial_size = 256; + +static struct dir_data **dir_data_cache; +static unsigned int dir_data_cache_size; + static BOOL show_dot_files; static RTL_RUN_ONCE init_once = RTL_RUN_ONCE_INIT; @@ -188,7 +200,6 @@ static const BOOL is_case_sensitive = FALSE; UNICODE_STRING system_dir = { 0, 0, NULL }; /* system directory */ -static struct file_identity curdir; static struct file_identity windir; static RTL_CRITICAL_SECTION dir_section; @@ -278,6 +289,15 @@ static inline BOOL has_wildcard( const UNICODE_STRING *mask ) memchrW( mask->Buffer, '?', mask->Length / sizeof(WCHAR) )); } +/* free the cached directory data */ +static void free_dir_data( struct dir_data *data ) +{ + if (!data) return; + + RtlFreeUnicodeString( &data->mask_str ); + RtlFreeHeap( GetProcessHeap(), 0, data ); +} + /* support for a directory queue for filesystem searches */ @@ -1380,7 +1400,8 @@ static BOOLEAN match_filename( const UNICODE_STRING *name_str, const UNICODE_STR * * helper for NtQueryDirectoryFile */ -static union file_directory_info *append_entry( void *info_ptr, IO_STATUS_BLOCK *io, ULONG max_length, +static union file_directory_info *append_entry( struct dir_data *dir_data, void *info_ptr, + IO_STATUS_BLOCK *io, ULONG max_length, const char *long_name, const char *short_name, const UNICODE_STRING *mask, FILE_INFORMATION_CLASS class ) { @@ -1444,7 +1465,7 @@ static union file_directory_info *append_entry( void *info_ptr, IO_STATUS_BLOCK io->u.Status = STATUS_BUFFER_OVERFLOW; } info = (union file_directory_info *)((char *)info_ptr + io->Information); - if (st.st_dev != curdir.dev) st.st_ino = 0; /* ignore inode if on a different device */ + if (st.st_dev != dir_data->id.dev) st.st_ino = 0; /* ignore inode if on a different device */ /* all the structures start with a FileDirectoryInformation layout */ fill_file_info( &st, attributes, info, class ); info->dir.NextEntryOffset = total_len; @@ -1542,8 +1563,8 @@ static KERNEL_DIRENT *start_vfat_ioctl( int fd ) * * Read a directory using the VFAT ioctl; helper for NtQueryDirectoryFile. */ -static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static int read_directory_vfat( struct dir_data *dir_data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { @@ -1570,9 +1591,9 @@ static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG de[1].d_name[len] = 0; if (de[1].d_name[0]) - info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class ); + info = append_entry( dir_data, buffer, io, length, de[1].d_name, de[0].d_name, dir_data->mask, class ); else - info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class ); + info = append_entry( dir_data, buffer, io, length, de[0].d_name, NULL, dir_data->mask, class ); if (info) { last_info = info; @@ -1597,9 +1618,9 @@ static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG de[1].d_name[len] = 0; if (de[1].d_name[0]) - info = append_entry( buffer, io, length, de[1].d_name, de[0].d_name, mask, class ); + info = append_entry( dir_data, buffer, io, length, de[1].d_name, de[0].d_name, dir_data->mask, class ); else - info = append_entry( buffer, io, length, de[0].d_name, NULL, mask, class ); + info = append_entry( dir_data, buffer, io, length, de[0].d_name, NULL, dir_data->mask, class ); if (info) { last_info = info; @@ -1654,8 +1675,8 @@ static char *read_first_dent_name( int which, int fd, off_t second_offs, KERNEL_ * * Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile. */ -static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static int read_directory_getdents( struct dir_data *dir_data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { static off_t second_entry_pos; @@ -1692,14 +1713,14 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U /* if old_pos is not 0 we don't know how many entries have been returned already, * so maintain second_entry_pos to know when to return '..' */ - if (old_pos != 0 && (last_dir_id.dev != curdir.dev || last_dir_id.ino != curdir.ino)) + if (old_pos != 0 && (last_dir_id.dev != dir_data->id.dev || last_dir_id.ino != dir_data->id.ino)) { lseek( fd, 0, SEEK_SET ); res = getdents64( fd, data, size ); if (res > 0) { second_entry_pos = de->d_off; - last_dir_id = curdir; + last_dir_id = dir_data->id; } lseek( fd, old_pos, SEEK_SET ); } @@ -1718,7 +1739,7 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U if (old_pos == 0 && res > 0) { second_entry_pos = de->d_off; - last_dir_id = curdir; + last_dir_id = dir_data->id; if (res > de->d_reclen) de_first_two = de; } @@ -1754,7 +1775,7 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U else if (de->d_ino) filename = de->d_name; - if (filename && (info = append_entry( buffer, io, length, filename, NULL, mask, class ))) + if (filename && (info = append_entry( dir_data, buffer, io, length, filename, NULL, dir_data->mask, class ))) { last_info = info; if (io->u.Status == STATUS_BUFFER_OVERFLOW) @@ -1848,8 +1869,8 @@ static inline int dir_reclen(struct dirent *de) * * Read a directory using the BSD getdirentries system call; helper for NtQueryDirectoryFile. */ -static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static int read_directory_getdirentries( struct dir_data *dir_data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { long restart_pos; @@ -1907,9 +1928,9 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff if (fake_dot_dot) { - if ((info = append_entry( buffer, io, length, ".", NULL, mask, class ))) + if ((info = append_entry( dir_data, buffer, io, length, ".", NULL, dir_data->mask, class ))) last_info = info; - if ((info = append_entry( buffer, io, length, "..", NULL, mask, class ))) + if ((info = append_entry( dir_data, buffer, io, length, "..", NULL, dir_data->mask, class ))) last_info = info; restart_last_info = last_info; @@ -1929,7 +1950,7 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff res -= dir_reclen(de); if (de->d_fileno && !(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) && - ((info = append_entry( buffer, io, length, de->d_name, NULL, mask, class )))) + ((info = append_entry( dir_data, buffer, io, length, de->d_name, NULL, dir_data->mask, class )))) { last_info = info; if (io->u.Status == STATUS_BUFFER_OVERFLOW) @@ -1949,7 +1970,7 @@ static int read_directory_getdirentries( int fd, IO_STATUS_BLOCK *io, void *buff last_info = NULL; goto restart; } - if (!has_wildcard( mask )) break; + if (!has_wildcard( dir_data->->mask )) break; /* if we have to return but the buffer contains more data, restart with a smaller size */ if (res > 0 && (single_entry || io->Information + max_dir_info_size(class) > length)) { @@ -1995,8 +2016,8 @@ done: * * Read a directory using the POSIX readdir interface; helper for NtQueryDirectoryFile. */ -static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static void read_directory_readdir( struct dir_data *dir_data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { DIR *dir; @@ -2029,13 +2050,13 @@ static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, U for (;;) { if (old_pos == 0) - info = append_entry( buffer, io, length, ".", NULL, mask, class ); + info = append_entry( dir_data, buffer, io, length, ".", NULL, dir_data->mask, class ); else if (old_pos == 1) - info = append_entry( buffer, io, length, "..", NULL, mask, class ); + info = append_entry( dir_data, buffer, io, length, "..", NULL, dir_data->mask, class ); else if ((de = readdir( dir ))) { if (strcmp( de->d_name, "." ) && strcmp( de->d_name, ".." )) - info = append_entry( buffer, io, length, de->d_name, NULL, mask, class ); + info = append_entry( dir_data, buffer, io, length, de->d_name, NULL, dir_data->mask, class ); else info = NULL; } @@ -2069,10 +2090,11 @@ static void read_directory_readdir( int fd, IO_STATUS_BLOCK *io, void *buffer, U * Read a single file from a directory by determining whether the file * identified by mask exists using stat. */ -static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static int read_directory_stat( struct dir_data *dir_data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { + const UNICODE_STRING *mask = dir_data->mask; int unix_len, ret, used_default; char *unix_name; struct stat st; @@ -2105,7 +2127,7 @@ static int read_directory_stat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG ret = stat( unix_name, &st ); if (case_sensitive && !ret) { - union file_directory_info *info = append_entry( buffer, io, length, unix_name, NULL, NULL, class ); + union file_directory_info *info = append_entry( dir_data, buffer, io, length, unix_name, NULL, NULL, class ); if (info) { info->next = 0; @@ -2135,10 +2157,11 @@ done: * Read a single file from a directory by determining whether the file * identified by mask exists using getattrlist. */ -static int read_directory_getattrlist( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length, - BOOLEAN single_entry, const UNICODE_STRING *mask, +static int read_directory_getattrlist( struct dir_data *data, int fd, IO_STATUS_BLOCK *io, + void *buffer, ULONG length, BOOLEAN single_entry, BOOLEAN restart_scan, FILE_INFORMATION_CLASS class ) { + const UNICODE_STRING *mask = dir_data->mask; int unix_len, ret, used_default; char *unix_name; struct attrlist attrlist; @@ -2190,7 +2213,7 @@ static int read_directory_getattrlist( int fd, IO_STATUS_BLOCK *io, void *buffer } if (!ret) { - union file_directory_info *info = append_entry( buffer, io, length, attrlist_buffer.name, NULL, NULL, class ); + union file_directory_info *info = append_entry( dir_data, buffer, io, length, attrlist_buffer.name, NULL, NULL, class ); if (info) { info->next = 0; @@ -2211,6 +2234,94 @@ done: #endif +/*********************************************************************** + * init_cached_dir_data + * + * Initialize the cached directory contents. + */ +static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const UNICODE_STRING *mask ) +{ + struct dir_data *data; + struct stat st; + + if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) + return STATUS_NO_MEMORY; + + if (mask) + { + RtlDuplicateUnicodeString( 0, mask, &data->mask_str ); + data->mask = &data->mask_str; + } + + if (!fstat( fd, &st )) + { + data->id.dev = st.st_dev; + data->id.ino = st.st_ino; + } + + *data_ret = data; + return STATUS_SUCCESS; +} + + +/*********************************************************************** + * get_cached_dir_data + * + * Retrieve the cached directory data, or initialize it if necessary. + */ +static NTSTATUS get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd, + const UNICODE_STRING *mask ) +{ + unsigned int i; + int entry = -1, free_entries[16]; + NTSTATUS status; + + SERVER_START_REQ( get_directory_cache_entry ) + { + req->handle = wine_server_obj_handle( handle ); + wine_server_set_reply( req, free_entries, sizeof(free_entries) ); + if (!(status = wine_server_call( req ))) entry = reply->entry; + + for (i = 0; i < wine_server_reply_size( reply ) / sizeof(*free_entries); i++) + { + int free_idx = free_entries[i]; + if (free_idx < dir_data_cache_size) + { + free_dir_data( dir_data_cache[free_idx] ); + dir_data_cache[free_idx] = NULL; + } + } + } + SERVER_END_REQ; + + if (status) + { + if (status == STATUS_SHARING_VIOLATION) FIXME( "shared directory handle not supported yet\n" ); + return status; + } + + if (entry >= dir_data_cache_size) + { + unsigned int size = max( dir_data_cache_initial_size, dir_data_cache_size * 2 ); + struct dir_data **new_cache; + + if (dir_data_cache) + new_cache = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, dir_data_cache, + size * sizeof(*new_cache) ); + else + new_cache = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, size * sizeof(*new_cache) ); + if (!new_cache) return STATUS_NO_MEMORY; + dir_data_cache = new_cache; + dir_data_cache_size = size; + } + + if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask ); + + *data_ret = dir_data_cache[entry]; + return status; +} + + /****************************************************************************** * NtQueryDirectoryFile [NTDLL.@] * ZwQueryDirectoryFile [NTDLL.@] @@ -2225,6 +2336,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, BOOLEAN restart_scan ) { int cwd, fd, needs_close; + struct dir_data *data; NTSTATUS status; TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n", @@ -2264,31 +2376,29 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, cwd = open( ".", O_RDONLY ); if (fchdir( fd ) != -1) { - struct stat st; - fstat( fd, &st ); - curdir.dev = st.st_dev; - curdir.ino = st.st_ino; + if ((io->u.Status = get_cached_dir_data( handle, &data, fd, mask ))) goto done; + #ifdef VFAT_IOCTL_READDIR_BOTH - if ((read_directory_vfat( fd, io, buffer, length, single_entry, - mask, restart_scan, info_class )) != -1) goto done; + if ((read_directory_vfat( data, fd, io, buffer, length, single_entry, + restart_scan, info_class )) != -1) goto done; #endif - if (!has_wildcard( mask )) + if (!has_wildcard( data->mask )) { #ifdef HAVE_GETATTRLIST - if (read_directory_getattrlist( fd, io, buffer, length, single_entry, - mask, restart_scan, info_class ) != -1) goto done; + if (read_directory_getattrlist( data, fd, io, buffer, length, single_entry, + restart_scan, info_class ) != -1) goto done; #endif - if (read_directory_stat( fd, io, buffer, length, single_entry, - mask, restart_scan, info_class ) != -1) goto done; + if (read_directory_stat( data, fd, io, buffer, length, single_entry, + restart_scan, info_class ) != -1) goto done; } #ifdef USE_GETDENTS - if ((read_directory_getdents( fd, io, buffer, length, single_entry, - mask, restart_scan, info_class )) != -1) goto done; + if ((read_directory_getdents( data, fd, io, buffer, length, single_entry, + restart_scan, info_class )) != -1) goto done; #elif defined HAVE_GETDIRENTRIES - if ((read_directory_getdirentries( fd, io, buffer, length, single_entry, - mask, restart_scan, info_class )) != -1) goto done; + if ((read_directory_getdirentries( data, fd, io, buffer, length, single_entry, + restart_scan, info_class )) != -1) goto done; #endif - read_directory_readdir( fd, io, buffer, length, single_entry, mask, restart_scan, info_class ); + read_directory_readdir( data, fd, io, buffer, length, single_entry, restart_scan, info_class ); done: status = io->u.Status; diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index e801a09d3a2..dbd5bd35b8a 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -71,6 +71,8 @@ static struct testfile_s { }; static const int max_test_dir_size = 20; /* size of above plus some for .. etc */ +static const WCHAR dummyW[] = {'d','u','m','m','y',0}; + /* Create a test directory full of attribute test files, clear counts */ static void set_up_attribute_test(const char *testdirA) { @@ -162,7 +164,8 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char UNICODE_STRING *mask, BOOLEAN single_entry, BOOLEAN restart_flag) { - HANDLE dirh; + UNICODE_STRING dummy_mask; + HANDLE dirh, new_dirh; IO_STATUS_BLOCK io; UINT data_pos, data_size; UINT data_len; /* length of dir data */ @@ -173,6 +176,7 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char int i; reset_found_files(); + pRtlInitUnicodeString( &dummy_mask, dummyW ); data_size = mask ? offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ) : sizeof(data); @@ -193,6 +197,10 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char data_len = io.Information; ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n"); + DuplicateHandle( GetCurrentProcess(), dirh, GetCurrentProcess(), &new_dirh, + 0, FALSE, DUPLICATE_SAME_ACCESS ); + pNtClose(dirh); + data_pos = 0; numfiles = 0; while ((data_pos < data_len) && (numfiles < max_test_dir_size)) { @@ -202,8 +210,8 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char if (dir_info->NextEntryOffset == 0) { U(io).Status = 0xdeadbeef; - status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, - FileBothDirectoryInformation, single_entry, mask, FALSE ); + status = pNtQueryDirectoryFile( new_dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, single_entry, &dummy_mask, FALSE ); ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status); if (status == STATUS_NO_MORE_FILES) break; ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); @@ -228,7 +236,7 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char for (i=0; testfiles[i].name; i++) ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n", testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag); - pNtClose(dirh); + pNtClose(new_dirh); } static void test_NtQueryDirectoryFile(void) @@ -321,7 +329,7 @@ static void test_NtQueryDirectoryFile(void) FileBothDirectoryInformation, TRUE, &mask, TRUE); ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status ); ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + ok( U(io).Information == data_size || broken( U(io).Information == 0), "wrong info %lx\n", U(io).Information ); ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); ok( fbdi->FileNameLength == strlen(testfiles[0].name) * sizeof(WCHAR), @@ -343,6 +351,34 @@ static void test_NtQueryDirectoryFile(void) 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 ); + + /* mask may or may not be ignored when restarting the search */ + pRtlInitUnicodeString( &mask, dummyW ); + U(io).Status = 0xdeadbeef; + data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, &mask, TRUE); + ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %x\n", status ); + ok( U(io).Status == status, "wrong status %x / %x\n", U(io).Status, status ); + if (!status) + ok( fbdi->FileNameLength == strlen(testfiles[0].name)*sizeof(WCHAR) && + !memcmp(fbdi->FileName, testfiles[0].nameW, fbdi->FileNameLength), + "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + pNtClose(dirh); status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, @@ -428,6 +464,35 @@ static void test_NtQueryDirectoryFile(void) pNtClose(dirh); + /* create new handle to change mask */ + status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); + ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA); + + pRtlInitUnicodeString( &mask, dummyW ); + U(io).Status = 0xdeadbeef; + 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); + todo_wine + ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, FALSE); + ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); + ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + + U(io).Status = 0xdeadbeef; + status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, TRUE); + todo_wine + ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); + todo_wine + ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + + pNtClose(dirh); + U(io).Status = 0xdeadbeef; status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, NULL, TRUE );