From: Eugene McArdle eugene@tensorworks.com.au
--- dlls/ntdll/tests/directory.c | 3 -- dlls/ntdll/unix/file.c | 57 ++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index 6aec2ff6408..9d7ed2f4841 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -1031,7 +1031,6 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI
if (validate_only && status != expected_status) return FALSE;
- todo_wine 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 );
@@ -1042,9 +1041,7 @@ static BOOL test_NtQueryDirectoryFile_mask(HANDLE handle, BOOL restart_scan, UNI name = dir_info->FileName; name_len = dir_info->FileNameLength / sizeof(WCHAR);
- todo_wine ok( name_len == mask_len, "unexpected filename length %lu, expected %lu\n", name_len, mask_len ); - todo_wine ok( !memcmp(name, mask_value, mask_len), "unexpected filename %s, expected %s\n", wine_dbgstr_wn(name, name_len), wine_dbgstr_wn(mask_value, mask_len) ); } diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 13dc36b42ff..097aaaca4b9 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,35 @@ 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, + BOOLEAN *fresh_handle ) { unsigned int i; int entry = -1, free_entries[16]; unsigned int status; + *fresh_handle = TRUE;
SERVER_START_REQ( get_directory_cache_entry ) { @@ -2675,6 +2703,21 @@ static unsigned int get_cached_dir_data( HANDLE handle, struct dir_data **data_r dir_data_cache_size = size; }
+ /* 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 );
*data_ret = dir_data_cache[entry]; @@ -2694,6 +2737,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI enum server_fd_type type; struct dir_data *data; unsigned int status; + BOOLEAN fresh_handle = TRUE;
TRACE("(%p %p %p %p %p %p 0x%08x 0x%08x 0x%08x %s 0x%08x\n", handle, event, apc_routine, apc_context, io, buffer, @@ -2746,7 +2790,7 @@ 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 ))) + if (!(status = get_cached_dir_data( handle, &data, fd, mask, restart_scan, &fresh_handle ))) { union file_directory_info *last_info = NULL;
@@ -2768,6 +2812,13 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event, PIO_APC_ROUTI } else status = errno_to_status( errno );
+ /* 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; + io->Status = status; + } + mutex_unlock( &dir_mutex );
if (needs_close) close( fd );