From: Eugene McArdle eugene@tensorworks.com.au
--- dlls/ntdll/tests/directory.c | 3 -- dlls/ntdll/unix/file.c | 69 ++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index cd7c21ceeba..f60b1f217f0 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -1033,7 +1033,6 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI
if (validate_only && status != expected_status) return FALSE;
- todo_wine_if(status != expected_status) ok( status == expected_status, "unexpected status : 0x%lx Test settings: file mask: '%s', restart: %d, expected status: 0x%lx\n", status, wine_dbgstr_wn(mask_value, mask_len), restart_scan, expected_status );
@@ -1046,9 +1045,7 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI match_len = match->Length / sizeof(WCHAR); match_value = match->Buffer;
- todo_wine_if(name_len != match_len) ok( name_len == match_len, "unexpected filename length %lu, expected %lu\n", name_len, match_len ); - todo_wine_if(name_len != match_len) ok( !memcmp(name, match_value, match_len * sizeof(WCHAR)), "unexpected filename %s, expected %s\n", wine_dbgstr_wn(name, name_len), wine_dbgstr_wn(match_value, match_len) ); } diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 13dc36b42ff..273bff7485b 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -225,6 +225,7 @@ struct dir_data struct file_identity id; /* directory file identity */ struct dir_data_names *names; /* directory file names */ struct dir_data_buffer *buffer; /* head of data buffers list */ + UNICODE_STRING mask; /* the mask used when creating the cache entry */ };
static const unsigned int dir_data_buffer_initial_size = 4096; @@ -565,6 +566,7 @@ static void free_dir_data( struct dir_data *data ) free( buffer ); } free( data->names ); + if (data->mask.Buffer) free( data->mask.Buffer ); free( data ); }
@@ -2606,6 +2608,14 @@ static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const return status; }
+ /* if a mask was specified then copy it into the cache entry */ + if (mask) + { + data->mask.Length = data->mask.MaximumLength = mask->Length; + if (!(data->mask.Buffer = calloc( 1, mask->Length ))) return STATUS_NO_MEMORY; + memcpy(data->mask.Buffer, mask->Buffer, mask->Length); + } + /* sort filenames, but not "." and ".." */ i = 0; if (i < data->count && !strcmp( data->names[i].unix_name, "." )) i++; @@ -2628,17 +2638,34 @@ static NTSTATUS init_cached_dir_data( struct dir_data **data_ret, int fd, const }
+/*********************************************************************** + * ustring_equal + * + * Simplified version of RtlEqualUnicodeString that performs only case-sensitive comparisons. + */ +static BOOLEAN ustring_equal( const UNICODE_STRING *a, const UNICODE_STRING *b ) +{ + USHORT length_a = (a ? a->Length : 0); + USHORT length_b = (b ? b->Length : 0); + if (length_a != length_b) return FALSE; + + if (length_a == 0 && length_b == 0) return TRUE; + return (!memcmp(a->Buffer, b->Buffer, a->Length)); +} + + /*********************************************************************** * get_cached_dir_data * * Retrieve the cached directory data, or initialize it if necessary. */ static unsigned int get_cached_dir_data( HANDLE handle, struct dir_data **data_ret, int fd, - const UNICODE_STRING *mask ) + const UNICODE_STRING *mask, BOOLEAN restart_scan ) { unsigned int i; int entry = -1, free_entries[16]; unsigned int status; + BOOLEAN fresh_handle;
SERVER_START_REQ( get_directory_cache_entry ) { @@ -2675,9 +2702,33 @@ static unsigned int get_cached_dir_data( HANDLE handle, struct dir_data **data_r dir_data_cache_size = size; }
- if (!dir_data_cache[entry]) status = init_cached_dir_data( &dir_data_cache[entry], fd, mask ); + /* If we have an existing cache entry then set the flag to indicate that the handle is not fresh */ + fresh_handle = (dir_data_cache[entry] == NULL); + + /* If we have an existing cache entry, but restart_scan is true and a new non-empty mask + was specified then we need to invalidate the existing cache entry and create a new one + */ + if (dir_data_cache[entry] && restart_scan && mask && + !ustring_equal(&dir_data_cache[entry]->mask, mask)) + { + TRACE( "invalidating existing cache entry for handle %p, old mask: "%s", new mask: "%s"\n", + handle, debugstr_us(&(dir_data_cache[entry]->mask)), debugstr_us(mask)); + free_dir_data( dir_data_cache[entry] ); + dir_data_cache[entry] = NULL; + } + + if (!dir_data_cache[entry]) + { + status = init_cached_dir_data( &dir_data_cache[entry], fd, mask ); + /* Return STATUS_NO_MORE_FILES if a mask did not match a file and the handle had previously been used */ + if (status == STATUS_NO_SUCH_FILE && !fresh_handle) + { + status = STATUS_NO_MORE_FILES; + } + }
*data_ret = dir_data_cache[entry]; + if (restart_scan) (*data_ret)->pos = 0; return status; }
@@ -2740,18 +2791,18 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI }
io->Information = 0; + if (mask && mask->Length == 0) mask = NULL;
mutex_lock( &dir_mutex );
cwd = open( ".", O_RDONLY ); if (fchdir( fd ) != -1) { - if (!(status = get_cached_dir_data( handle, &data, fd, mask ))) + status = get_cached_dir_data( handle, &data, fd, mask, restart_scan ); + if (!status) { union file_directory_info *last_info = NULL;
- if (restart_scan) data->pos = 0; - while (!status && data->pos < data->count) { status = get_dir_data_entry( data, buffer, io, length, info_class, &last_info ); @@ -2761,13 +2812,17 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI
if (!last_info) status = STATUS_NO_MORE_FILES; else if (status == STATUS_MORE_ENTRIES) status = STATUS_SUCCESS; - - io->Status = status; } if (cwd == -1 || fchdir( cwd ) == -1) chdir( "/" ); } else status = errno_to_status( errno );
+ /* io->Status should only update if status isn't STATUS_NO_SUCH_FILE */ + if (status != STATUS_NO_SUCH_FILE) + { + io->Status = status; + } + mutex_unlock( &dir_mutex );
if (needs_close) close( fd );