From: Eugene McArdle eugene@tensorworks.com.au
--- dlls/ntdll/tests/directory.c | 11 +++--- dlls/ntdll/unix/file.c | 67 ++++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index 6d0ef1d31ef..0121a089720 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -1020,7 +1020,7 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI ULONG mask_len = 0; WCHAR *match_value = {0}; ULONG match_len = 0; - + if (mask) { mask_len = mask->Length / sizeof(WCHAR); @@ -1030,10 +1030,9 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI /* Perform the query */ status = pNtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, data, data_size, FileDirectoryInformation, TRUE, mask, restart_scan ); - + 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 );
@@ -1045,10 +1044,8 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI name_len = dir_info->FileNameLength / sizeof(WCHAR); 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) ); } @@ -1137,7 +1134,7 @@ static void test_NtQueryDirectoryFile_change_mask(void) test_NtQueryDirectoryFile_mask(dirh, TRUE, &atestfile, STATUS_SUCCESS, &atestfile, FALSE); test_NtQueryDirectoryFile_mask(dirh, TRUE, &anothertestfile, STATUS_SUCCESS, &anothertestfile, FALSE); test_NtQueryDirectoryFile_mask(dirh, TRUE, ¬atestfile, STATUS_NO_MORE_FILES, ¬atestfile, FALSE); - + test_NtQueryDirectoryFile_mask(dirh, TRUE, &atestfile, STATUS_SUCCESS, &atestfile, FALSE); test_NtQueryDirectoryFile_mask(dirh, FALSE, ¬atestfile, STATUS_NO_MORE_FILES, ¬atestfile, FALSE);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 13dc36b42ff..9265a2e3b11 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 ); }
@@ -1595,7 +1597,7 @@ static BOOL append_entry( struct dir_data *data, const char *long_name, TRACE( "long %s short %s mask %s\n", debugstr_w( long_nameW ), debugstr_w( short_nameW ), debugstr_us( mask ));
- if (mask && !match_filename( long_nameW, long_len, mask )) + if (mask && mask->Length != 0 && !match_filename( long_nameW, long_len, mask )) { if (!short_len) return TRUE; /* no short name to match */ if (!match_filename( short_nameW, short_len, mask )) return TRUE; @@ -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 && mask->Length) + { + 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 = TRUE;
SERVER_START_REQ( get_directory_cache_entry ) { @@ -2675,7 +2702,30 @@ 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 */ + if (dir_data_cache[entry]) fresh_handle = FALSE; + + /* 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 && mask->Length != 0 && + !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]; return status; @@ -2746,7 +2796,8 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI 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;
@@ -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 );