1 /* Unit test suite for Ntdll directory functions
2  *
3  * Copyright 2007 Jeff Latimer
4  * Copyright 2007 Andrey Turkin
5  * Copyright 2008 Jeff Zaroyko
6  * Copyright 2009 Dan Kegel
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  * NOTES
23  * We use function pointers here as there is no import library for NTDLL on
24  * windows.
25  */
26 
27 #include <stdio.h>
28 #include <stdarg.h>
29 
30 #include "ntstatus.h"
31 /* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
32  * definition errors when we get to winnt.h
33  */
34 #define WIN32_NO_STATUS
35 
36 #include "wine/test.h"
37 #include "winnls.h"
38 #include "winternl.h"
39 
40 static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
41 static NTSTATUS (WINAPI *pNtOpenFile)    ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
42 static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
43                                                 PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
44 static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,LONG,FILE_INFORMATION_CLASS);
45 static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FILE_INFORMATION_CLASS);
46 static BOOLEAN  (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR);
47 static BOOL     (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
48 static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
49 static VOID     (WINAPI *pRtlFreeUnicodeString)( PUNICODE_STRING );
50 static LONG     (WINAPI *pRtlCompareUnicodeString)( const UNICODE_STRING*, const UNICODE_STRING*,BOOLEAN );
51 static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
52                                                    LPCSTR src, DWORD srclen );
53 static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirection)( BOOLEAN enable );
54 static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG *old_value );
55 
56 /* The attribute sets to test */
57 static struct testfile_s {
58     BOOL attr_done;           /* set if attributes were tested for this file already */
59     const DWORD attr;         /* desired attribute */
60     WCHAR name[20];           /* filename to use */
61     const char *target;       /* what to point to (only for reparse pts) */
62     const char *description;  /* for error messages */
63     int nfound;               /* How many were found (expect 1) */
64 } testfiles[] = {
65     { 0, FILE_ATTRIBUTE_NORMAL,    {'l','o','n','g','f','i','l','e','n','a','m','e','.','t','m','p'}, "normal" },
66     { 0, FILE_ATTRIBUTE_NORMAL,    {'n','.','t','m','p',}, "normal" },
67     { 0, FILE_ATTRIBUTE_HIDDEN,    {'h','.','t','m','p',}, "hidden" },
68     { 0, FILE_ATTRIBUTE_SYSTEM,    {'s','.','t','m','p',}, "system" },
69     { 0, FILE_ATTRIBUTE_DIRECTORY, {'d','.','t','m','p',}, "directory" },
70     { 0, FILE_ATTRIBUTE_NORMAL,    {0xe9,'a','.','t','m','p'}, "normal" },
71     { 0, FILE_ATTRIBUTE_NORMAL,    {0xc9,'b','.','t','m','p'}, "normal" },
72     { 0, FILE_ATTRIBUTE_NORMAL,    {'e','a','.','t','m','p'},  "normal" },
73     { 0, FILE_ATTRIBUTE_DIRECTORY, {'.'},                  ". directory" },
74     { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'},              ".. directory" }
75 };
76 static const int test_dir_count = sizeof(testfiles) / sizeof(testfiles[0]);
77 static const int max_test_dir_size = sizeof(testfiles) / sizeof(testfiles[0]) + 5;  /* size of above plus some for .. etc */
78 
79 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
80 static const WCHAR dotW[] = {'.',0};
81 static const WCHAR dotdotW[] = {'.','.',0};
82 static const WCHAR backslashW[] = {'\\',0};
83 
84 /* Create a test directory full of attribute test files, clear counts */
85 static void set_up_attribute_test(const WCHAR *testdir)
86 {
87     int i;
88     BOOL ret;
89 
90     ret = CreateDirectoryW(testdir, NULL);
91     ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(testdir), GetLastError());
92 
93     for (i=0; i < test_dir_count; i++) {
94         WCHAR buf[MAX_PATH];
95 
96         if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0)
97             continue;
98         lstrcpyW( buf, testdir );
99         lstrcatW( buf, backslashW );
100         lstrcatW( buf, testfiles[i].name );
101         if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
102             ret = CreateDirectoryW(buf, NULL);
103             ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
104         } else {
105             HANDLE h = CreateFileW(buf,
106                                    GENERIC_READ|GENERIC_WRITE,
107                                    0, NULL, CREATE_ALWAYS,
108                                    testfiles[i].attr, 0);
109             ok( h != INVALID_HANDLE_VALUE, "failed to create temp file %s\n", wine_dbgstr_w(buf) );
110             CloseHandle(h);
111         }
112     }
113 }
114 
115 static void reset_found_files(void)
116 {
117     int i;
118 
119     for (i = 0; i < test_dir_count; i++)
120         testfiles[i].nfound = 0;
121 }
122 
123 /* Remove the given test directory and the attribute test files, if any */
124 static void tear_down_attribute_test(const WCHAR *testdir)
125 {
126     int i;
127 
128     for (i = 0; i < test_dir_count; i++) {
129         int ret;
130         WCHAR buf[MAX_PATH];
131         if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0)
132             continue;
133         lstrcpyW( buf, testdir );
134         lstrcatW( buf, backslashW );
135         lstrcatW( buf, testfiles[i].name );
136         if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
137             ret = RemoveDirectoryW(buf);
138             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
139                "Failed to rmdir %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
140         } else {
141             ret = DeleteFileW(buf);
142             ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
143                "Failed to rm %s, error %d\n", wine_dbgstr_w(buf), GetLastError());
144         }
145     }
146     RemoveDirectoryW(testdir);
147 }
148 
149 /* Match one found file against testfiles[], increment count if found */
150 static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info)
151 {
152     int i;
153     DWORD attribmask =
154       (FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT);
155     DWORD attrib = dir_info->FileAttributes & attribmask;
156     WCHAR *nameW = dir_info->FileName;
157     int namelen = dir_info->FileNameLength / sizeof(WCHAR);
158 
159     for (i = 0; i < test_dir_count; i++) {
160         int len = lstrlenW(testfiles[i].name);
161         if (namelen != len || memcmp(nameW, testfiles[i].name, len*sizeof(WCHAR)))
162             continue;
163         if (!testfiles[i].attr_done) {
164             ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", wine_dbgstr_w(testfiles[i].name), testfiles[i].description, testfiles[i].attr, attrib);
165             testfiles[i].attr_done = TRUE;
166         }
167         testfiles[i].nfound++;
168         break;
169     }
170     ok(i < test_dir_count, "unexpected file found %s\n", wine_dbgstr_wn(dir_info->FileName, namelen));
171 }
172 
173 static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA,
174                                             UNICODE_STRING *mask,
175                                             BOOLEAN single_entry, BOOLEAN restart_flag)
176 {
177     UNICODE_STRING dummy_mask;
178     HANDLE dirh, new_dirh;
179     IO_STATUS_BLOCK io;
180     UINT data_pos, data_size;
181     UINT data_len;    /* length of dir data */
182     BYTE data[8192];  /* directory data */
183     FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
184     NTSTATUS status;
185     int numfiles;
186     int i;
187 
188     reset_found_files();
189     pRtlInitUnicodeString( &dummy_mask, dummyW );
190 
191     data_size = mask ? offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ) : sizeof(data);
192 
193     /* Read the directory and note which files are found */
194     status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, attr, &io, FILE_SHARE_READ,
195                          FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE);
196     ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError());
197     if (status != STATUS_SUCCESS) {
198        skip("can't test if we can't open the directory\n");
199        return;
200     }
201 
202     U(io).Status = 0xdeadbeef;
203     status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size,
204                                     FileBothDirectoryInformation, single_entry, mask, restart_flag );
205     ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
206     ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
207     data_len = io.Information;
208     ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");
209 
210     DuplicateHandle( GetCurrentProcess(), dirh, GetCurrentProcess(), &new_dirh,
211                      0, FALSE, DUPLICATE_SAME_ACCESS );
212     pNtClose(dirh);
213 
214     data_pos = 0;
215     numfiles = 0;
216     while ((data_pos < data_len) && (numfiles < max_test_dir_size)) {
217         dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
218 
219         tally_test_file(dir_info);
220 
221         if (dir_info->NextEntryOffset == 0) {
222             U(io).Status = 0xdeadbeef;
223             status = pNtQueryDirectoryFile( new_dirh, 0, NULL, NULL, &io, data, data_size,
224                                             FileBothDirectoryInformation, single_entry, &dummy_mask, FALSE );
225             ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
226             if (status == STATUS_NO_MORE_FILES) break;
227             ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
228             data_len = io.Information;
229             if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
230                 break;
231             data_pos = 0;
232         } else {
233             data_pos += dir_info->NextEntryOffset;
234         }
235         numfiles++;
236     }
237     ok(numfiles < max_test_dir_size, "too many loops\n");
238 
239     if (mask)
240         for (i = 0; i < test_dir_count; i++)
241             ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer),
242                "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n",
243                testfiles[i].nfound, testfiles[i].description, single_entry,
244                wine_dbgstr_wn(mask->Buffer, mask->Length/sizeof(WCHAR) ));
245     else
246         for (i = 0; i < test_dir_count; i++)
247             ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n",
248                testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag);
249     pNtClose(new_dirh);
250 }
251 
252 static void test_directory_sort( const WCHAR *testdir )
253 {
254     OBJECT_ATTRIBUTES attr;
255     UNICODE_STRING ntdirname;
256     IO_STATUS_BLOCK io;
257     UINT data_pos, data_len, count;
258     BYTE data[8192];
259     WCHAR prev[MAX_PATH], name[MAX_PATH];
260     UNICODE_STRING prev_str, name_str;
261     FILE_BOTH_DIRECTORY_INFORMATION *info;
262     NTSTATUS status;
263     HANDLE handle;
264     int res;
265 
266     if (!pRtlDosPathNameToNtPathName_U( testdir, &ntdirname, NULL, NULL ))
267     {
268         ok(0, "RtlDosPathNametoNtPathName_U failed\n");
269         return;
270     }
271     InitializeObjectAttributes( &attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL );
272     status = pNtOpenFile( &handle, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
273                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE );
274     ok(status == STATUS_SUCCESS, "failed to open dir %s\n", wine_dbgstr_w(testdir) );
275 
276     U(io).Status = 0xdeadbeef;
277     status = pNtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, data, sizeof(data),
278                                     FileBothDirectoryInformation, FALSE, NULL, TRUE );
279     ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status );
280     ok( U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status );
281     data_len = io.Information;
282     ok( data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n" );
283     data_pos = 0;
284     count = 0;
285 
286     while (data_pos < data_len)
287     {
288         info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
289 
290         memcpy( name, info->FileName, info->FileNameLength );
291         name[info->FileNameLength / sizeof(WCHAR)] = 0;
292         switch (count)
293         {
294         case 0:  /* first entry must be '.' */
295             ok( !lstrcmpW( name, dotW ), "wrong name %s\n", wine_dbgstr_w( name ));
296             break;
297         case 1:  /* second entry must be '..' */
298             ok( !lstrcmpW( name, dotdotW ), "wrong name %s\n", wine_dbgstr_w( name ));
299             break;
300         case 2:  /* nothing to compare against */
301             break;
302         default:
303             pRtlInitUnicodeString( &prev_str, prev );
304             pRtlInitUnicodeString( &name_str, name );
305             res = pRtlCompareUnicodeString( &prev_str, &name_str, TRUE );
306             ok( res < 0, "wrong result %d %s %s\n", res, wine_dbgstr_w( prev ), wine_dbgstr_w( name ));
307             break;
308         }
309         count++;
310         lstrcpyW( prev, name );
311 
312         if (info->NextEntryOffset == 0)
313         {
314             U(io).Status = 0xdeadbeef;
315             status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, sizeof(data),
316                                             FileBothDirectoryInformation, FALSE, NULL, FALSE );
317             ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status);
318             if (status == STATUS_NO_MORE_FILES) break;
319             ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status );
320             data_len = io.Information;
321             data_pos = 0;
322         }
323         else data_pos += info->NextEntryOffset;
324     }
325 
326     pNtClose( handle );
327     pRtlFreeUnicodeString( &ntdirname );
328 }
329 
330 static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *mask )
331 {
332     IO_STATUS_BLOCK io;
333     UINT data_size;
334     ULONG data[256];
335     NTSTATUS status;
336     int class;
337 
338     for (class = 0; class < FileMaximumInformation; class++)
339     {
340         U(io).Status = 0xdeadbeef;
341         U(io).Information = 0xdeadbeef;
342         data_size = 0;
343         memset( data, 0x55, sizeof(data) );
344 
345         status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size,
346                                         class, FALSE, mask, TRUE );
347         ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status );
348         ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information );
349         ok(data[0] == 0x55555555, "%u: wrong offset %x\n",  class, data[0] );
350 
351         switch (class)
352         {
353         case FileIdGlobalTxDirectoryInformation:
354         case FileIdExtdDirectoryInformation:
355         case FileIdExtdBothDirectoryInformation:
356             if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) continue;
357             /* fall through */
358         case FileDirectoryInformation:
359         case FileFullDirectoryInformation:
360         case FileBothDirectoryInformation:
361         case FileNamesInformation:
362         case FileIdBothDirectoryInformation:
363         case FileIdFullDirectoryInformation:
364         case FileObjectIdInformation:
365         case FileQuotaInformation:
366         case FileReparsePointInformation:
367             ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %x\n", class, status );
368             break;
369         default:
370             ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED,
371                 "%u: wrong status %x\n", class, status );
372             continue;
373         }
374 
375         for (data_size = 1; data_size < sizeof(data); data_size++)
376         {
377             status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size,
378                                             class, FALSE, mask, TRUE );
379             if (status == STATUS_BUFFER_OVERFLOW)
380             {
381                 ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, U(io).Status );
382                 ok( U(io).Information == data_size, "%u: wrong info %lx\n", class, U(io).Information );
383                 ok(data[0] == 0, "%u: wrong offset %x\n",  class, data[0] );
384             }
385             else
386             {
387                 ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status );
388                 ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information );
389                 ok(data[0] == 0x55555555, "%u: wrong offset %x\n",  class, data[0] );
390             }
391             if (status != STATUS_INFO_LENGTH_MISMATCH) break;
392         }
393 
394         switch (class)
395         {
396         case FileDirectoryInformation:
397             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
398             ok( data_size == ((offsetof( FILE_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
399                 "%u: wrong size %u\n", class, data_size );
400             break;
401         case FileFullDirectoryInformation:
402             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
403             ok( data_size == ((offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
404                 "%u: wrong size %u\n", class, data_size );
405             break;
406         case FileBothDirectoryInformation:
407             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
408             ok( data_size == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
409                 "%u: wrong size %u\n", class, data_size );
410             break;
411         case FileNamesInformation:
412             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
413             ok( data_size == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7),
414                 "%u: wrong size %u\n", class, data_size );
415             break;
416         case FileIdBothDirectoryInformation:
417             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
418             ok( data_size == ((offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
419                 "%u: wrong size %u\n", class, data_size );
420             break;
421         case FileIdFullDirectoryInformation:
422             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
423             ok( data_size == ((offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
424                 "%u: wrong size %u\n", class, data_size );
425             break;
426         case FileIdGlobalTxDirectoryInformation:
427             ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status );
428             ok( data_size == ((offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[1] ) + 7) & ~7),
429                 "%u: wrong size %u\n", class, data_size );
430             break;
431         case FileObjectIdInformation:
432             ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
433             ok( data_size == sizeof(FILE_OBJECTID_INFORMATION), "%u: wrong size %u\n", class, data_size );
434             break;
435         case FileQuotaInformation:
436             ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
437             ok( data_size == sizeof(FILE_QUOTA_INFORMATION), "%u: wrong size %u\n", class, data_size );
438             break;
439         case FileReparsePointInformation:
440             ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status );
441             ok( data_size == sizeof(FILE_REPARSE_POINT_INFORMATION), "%u: wrong size %u\n", class, data_size );
442             break;
443         }
444     }
445 }
446 
447 static void test_NtQueryDirectoryFile(void)
448 {
449     OBJECT_ATTRIBUTES attr;
450     UNICODE_STRING ntdirname, mask;
451     char testdirA[MAX_PATH];
452     WCHAR testdirW[MAX_PATH];
453     int i;
454     IO_STATUS_BLOCK io;
455     WCHAR short_name[12];
456     UINT data_size;
457     BYTE data[8192];
458     FILE_BOTH_DIRECTORY_INFORMATION *next, *fbdi = (FILE_BOTH_DIRECTORY_INFORMATION*)data;
459     FILE_POSITION_INFORMATION pos_info;
460     FILE_NAMES_INFORMATION *names;
461     const WCHAR *filename = fbdi->FileName;
462     NTSTATUS status;
463     HANDLE dirh;
464 
465     /* Clean up from prior aborted run, if any, then set up test files */
466     ok(GetTempPathA(MAX_PATH, testdirA), "couldn't get temp dir\n");
467     strcat(testdirA, "NtQueryDirectoryFile.tmp");
468     pRtlMultiByteToUnicodeN(testdirW, sizeof(testdirW), NULL, testdirA, strlen(testdirA)+1);
469     tear_down_attribute_test(testdirW);
470     set_up_attribute_test(testdirW);
471 
472     if (!pRtlDosPathNameToNtPathName_U(testdirW, &ntdirname, NULL, NULL))
473     {
474         ok(0, "RtlDosPathNametoNtPathName_U failed\n");
475         goto done;
476     }
477     InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL);
478 
479     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, TRUE);
480     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, FALSE);
481     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE);
482     test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE);
483 
484     for (i = 0; i < test_dir_count; i++)
485     {
486         if (testfiles[i].name[0] == '.') continue;  /* . and .. as masks are broken on Windows */
487         mask.Buffer = testfiles[i].name;
488         mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].name) * sizeof(WCHAR);
489         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE);
490         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE);
491         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE);
492         test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE);
493     }
494 
495     /* short path passed as mask */
496     status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
497             FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
498     ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
499     if (status != STATUS_SUCCESS) {
500         skip("can't test if we can't open the directory\n");
501         return;
502     }
503     status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
504     ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
505     ok( pos_info.CurrentByteOffset.QuadPart == 0, "wrong pos %s\n",
506         wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart));
507 
508     pos_info.CurrentByteOffset.QuadPart = 0xbeef;
509     status = pNtSetInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
510     ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
511 
512     status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
513     ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
514     ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
515         wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart));
516 
517     mask.Buffer = testfiles[0].name;
518     mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].name) * sizeof(WCHAR);
519     data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]);
520     U(io).Status = 0xdeadbeef;
521     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
522                                    FileBothDirectoryInformation, TRUE, &mask, FALSE);
523     ok(status == STATUS_SUCCESS, "failed to query directory; status %x\n", status);
524     ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
525     ok(fbdi->ShortName[0], "ShortName is empty\n");
526 
527     status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
528     ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
529     ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
530         wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) );
531 
532     mask.Length = mask.MaximumLength = fbdi->ShortNameLength;
533     memcpy(short_name, fbdi->ShortName, mask.Length);
534     mask.Buffer = short_name;
535     U(io).Status = 0xdeadbeef;
536     U(io).Information = 0xdeadbeef;
537     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
538                                    FileBothDirectoryInformation, TRUE, &mask, TRUE);
539     ok(status == STATUS_SUCCESS, "failed to query directory status %x\n", status);
540     ok(U(io).Status == STATUS_SUCCESS, "failed to query directory status %x\n", U(io).Status);
541     ok(U(io).Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[lstrlenW(testfiles[0].name)]),
542        "wrong info %lx\n", U(io).Information);
543     ok(fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) &&
544             !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength),
545             "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName,
546                 fbdi->FileNameLength/sizeof(WCHAR)));
547 
548     status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation );
549     ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status );
550     ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n",
551         wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) );
552 
553     /* tests with short buffer */
554     memset( data, 0x55, data_size );
555     U(io).Status = 0xdeadbeef;
556     U(io).Information = 0xdeadbeef;
557     data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] );
558     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
559                                    FileBothDirectoryInformation, TRUE, &mask, TRUE);
560     ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status );
561     ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status );
562     ok( U(io).Information == data_size || broken( U(io).Information == 0),
563         "wrong info %lx\n", U(io).Information );
564     ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
565     ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name) * sizeof(WCHAR),
566         "wrong length %x\n", fbdi->FileNameLength );
567     ok( filename[0] == testfiles[0].name[0], "incorrect long file name: %s\n",
568         wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
569     ok( filename[1] == 0x5555, "incorrect long file name: %s\n",
570         wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
571 
572     test_NtQueryDirectoryFile_classes( dirh, &mask );
573 
574     /* mask may or may not be ignored when restarting the search */
575     pRtlInitUnicodeString( &mask, dummyW );
576     U(io).Status = 0xdeadbeef;
577     data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] );
578     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
579                                    FileBothDirectoryInformation, TRUE, &mask, TRUE);
580     ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %x\n", status );
581     ok( U(io).Status == status, "wrong status %x / %x\n", U(io).Status, status );
582     if (!status)
583         ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) &&
584             !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength),
585             "incorrect long file name: %s\n",
586             wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
587 
588     pNtClose(dirh);
589 
590     status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
591             FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
592     ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
593 
594     memset( data, 0x55, data_size );
595     data_size = sizeof(data);
596     U(io).Status = 0xdeadbeef;
597     U(io).Information = 0xdeadbeef;
598     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
599                                    FileBothDirectoryInformation, FALSE, NULL, TRUE);
600     ok(status == STATUS_SUCCESS, "wrong status %x\n", status);
601     ok(U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status);
602     ok(U(io).Information > 0 && U(io).Information < data_size, "wrong info %lx\n", U(io).Information);
603     ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
604         "wrong offset %x\n",  fbdi->NextEntryOffset );
605     ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
606     ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
607         wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
608     next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
609     ok( next->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ) + 7) & ~7),
610         "wrong offset %x\n",  next->NextEntryOffset );
611     ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
612     filename = next->FileName;
613     ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
614         wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
615 
616     data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
617     memset( data, 0x55, data_size );
618     U(io).Status = 0xdeadbeef;
619     U(io).Information = 0xdeadbeef;
620     status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
621                                     FileBothDirectoryInformation, FALSE, NULL, TRUE );
622     ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
623     ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
624     ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
625         "wrong info %lx\n", U(io).Information );
626     ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
627     ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
628     ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
629         wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
630     next = (FILE_BOTH_DIRECTORY_INFORMATION *)&fbdi->FileName[1];
631     ok( next->NextEntryOffset == 0x55555555, "wrong offset %x\n",  next->NextEntryOffset );
632 
633     data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ),
634     memset( data, 0x55, data_size );
635     U(io).Status = 0xdeadbeef;
636     U(io).Information = 0xdeadbeef;
637     status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
638                                     FileBothDirectoryInformation, FALSE, NULL, TRUE );
639     ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
640     ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
641     ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ),
642         "wrong info %lx\n", U(io).Information );
643     ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n",  fbdi->NextEntryOffset );
644 
645     data_size = ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7) +
646                   offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] );
647     memset( data, 0x55, data_size );
648     U(io).Status = 0xdeadbeef;
649     U(io).Information = 0xdeadbeef;
650     status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
651                                     FileBothDirectoryInformation, FALSE, NULL, TRUE );
652     ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
653     ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
654     ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size );
655     ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7),
656         "wrong offset %x\n",  fbdi->NextEntryOffset );
657     ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength );
658     ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n",
659         wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR)));
660     next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset);
661     ok( next->NextEntryOffset == 0, "wrong offset %x\n",  next->NextEntryOffset );
662     ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength );
663     filename = next->FileName;
664     ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
665         wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR)));
666 
667     data_size = ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7) +
668                   offsetof( FILE_NAMES_INFORMATION, FileName[2] );
669     memset( data, 0x55, data_size );
670     U(io).Status = 0xdeadbeef;
671     U(io).Information = 0xdeadbeef;
672     status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size,
673                                     FileNamesInformation, FALSE, NULL, TRUE );
674     ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
675     ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status );
676     ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size );
677     names = (FILE_NAMES_INFORMATION *)data;
678     ok( names->NextEntryOffset == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7),
679         "wrong offset %x\n",  names->NextEntryOffset );
680     ok( names->FileNameLength == sizeof(WCHAR), "wrong length %x\n", names->FileNameLength );
681     ok( names->FileName[0] == '.', "incorrect long file name: %s\n",
682         wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR)));
683     names = (FILE_NAMES_INFORMATION *)(data + names->NextEntryOffset);
684     ok( names->NextEntryOffset == 0, "wrong offset %x\n",  names->NextEntryOffset );
685     ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", names->FileNameLength );
686     filename = names->FileName;
687     ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n",
688         wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR)));
689 
690     pNtClose(dirh);
691 
692     /* create new handle to change mask */
693     status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
694             FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
695     ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA);
696 
697     pRtlInitUnicodeString( &mask, dummyW );
698     U(io).Status = 0xdeadbeef;
699     data_size = sizeof(data);
700     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
701                                    FileBothDirectoryInformation, TRUE, &mask, TRUE);
702     ok(status == STATUS_NO_SUCH_FILE, "wrong status %x\n", status);
703     ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status);
704 
705     U(io).Status = 0xdeadbeef;
706     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
707                                    FileBothDirectoryInformation, TRUE, NULL, FALSE);
708     ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status);
709     ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status);
710 
711     U(io).Status = 0xdeadbeef;
712     status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size,
713                                    FileBothDirectoryInformation, TRUE, NULL, TRUE);
714     ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status);
715     ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status);
716 
717     pNtClose(dirh);
718 
719     U(io).Status = 0xdeadbeef;
720     status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size,
721                                     FileBothDirectoryInformation, TRUE, NULL, TRUE );
722     ok(status == STATUS_INVALID_HANDLE, "wrong status %x\n", status);
723     ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status);
724 
725 done:
726     test_directory_sort( testdirW );
727     tear_down_attribute_test( testdirW );
728     pRtlFreeUnicodeString(&ntdirname);
729 }
730 
731 static void set_up_case_test(const char *testdir)
732 {
733     BOOL ret;
734     char buf[MAX_PATH];
735     HANDLE h;
736 
737     ret = CreateDirectoryA(testdir, NULL);
738     ok(ret, "couldn't create dir '%s', error %d\n", testdir, GetLastError());
739 
740     sprintf(buf, "%s\\%s", testdir, "TesT");
741     h = CreateFileA(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
742                     FILE_ATTRIBUTE_NORMAL, 0);
743     ok(h != INVALID_HANDLE_VALUE, "failed to create temp file '%s'\n", buf);
744     CloseHandle(h);
745 }
746 
747 static void tear_down_case_test(const char *testdir)
748 {
749     int ret;
750     char buf[MAX_PATH];
751 
752     sprintf(buf, "%s\\%s", testdir, "TesT");
753     ret = DeleteFileA(buf);
754     ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND),
755        "Failed to rm %s, error %d\n", buf, GetLastError());
756     RemoveDirectoryA(testdir);
757 }
758 
759 static void test_NtQueryDirectoryFile_case(void)
760 {
761     static const char testfile[] = "TesT";
762     static const WCHAR testfile_w[] = {'T','e','s','T'};
763     static int testfile_len = sizeof(testfile) - 1;
764     static WCHAR testmask[] = {'t','e','s','t'};
765     OBJECT_ATTRIBUTES attr;
766     UNICODE_STRING ntdirname;
767     char testdir[MAX_PATH];
768     WCHAR testdir_w[MAX_PATH];
769     HANDLE dirh;
770     UNICODE_STRING mask;
771     IO_STATUS_BLOCK io;
772     UINT data_size, data_len;
773     BYTE data[8192];
774     FILE_BOTH_DIRECTORY_INFORMATION *dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)data;
775     DWORD status;
776     WCHAR *name;
777     ULONG name_len;
778 
779     /* Clean up from prior aborted run, if any, then set up test files */
780     ok(GetTempPathA(MAX_PATH, testdir), "couldn't get temp dir\n");
781     strcat(testdir, "case.tmp");
782     tear_down_case_test(testdir);
783     set_up_case_test(testdir);
784 
785     pRtlMultiByteToUnicodeN(testdir_w, sizeof(testdir_w), NULL, testdir, strlen(testdir) + 1);
786     if (!pRtlDosPathNameToNtPathName_U(testdir_w, &ntdirname, NULL, NULL))
787     {
788         ok(0, "RtlDosPathNametoNtPathName_U failed\n");
789         goto done;
790     }
791     InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL);
792 
793     data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]);
794 
795     status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ,
796                          FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE);
797     ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdir, status, GetLastError());
798     if (status != STATUS_SUCCESS)
799     {
800        skip("can't test if we can't open the directory\n");
801        return;
802     }
803 
804     mask.Buffer = testmask;
805     mask.Length = mask.MaximumLength = sizeof(testmask);
806     pNtQueryDirectoryFile(dirh, NULL, NULL, NULL, &io, data, data_size,
807                           FileBothDirectoryInformation, TRUE, &mask, FALSE);
808     ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status);
809     data_len = io.Information;
810     ok(data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");
811 
812     name = dir_info->FileName;
813     name_len = dir_info->FileNameLength / sizeof(WCHAR);
814 
815     ok(name_len == testfile_len, "unexpected filename length %u\n", name_len);
816     ok(!memcmp(name, testfile_w, testfile_len * sizeof(WCHAR)), "unexpected filename %s\n",
817        wine_dbgstr_wn(name, name_len));
818 
819     pNtClose(dirh);
820 
821 done:
822     tear_down_case_test(testdir);
823     pRtlFreeUnicodeString(&ntdirname);
824 }
825 
826 static void test_redirection(void)
827 {
828     ULONG old, cur;
829     NTSTATUS status;
830 
831     if (!pRtlWow64EnableFsRedirection || !pRtlWow64EnableFsRedirectionEx)
832     {
833         skip( "Wow64 redirection not supported\n" );
834         return;
835     }
836     status = pRtlWow64EnableFsRedirectionEx( FALSE, &old );
837     if (status == STATUS_NOT_IMPLEMENTED)
838     {
839         skip( "Wow64 redirection not supported\n" );
840         return;
841     }
842     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
843 
844     status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
845     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
846     ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
847 
848     status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
849     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
850     status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
851     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
852     ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
853 
854     status = pRtlWow64EnableFsRedirection( TRUE );
855     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
856     status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur );
857     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
858     ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
859 
860     status = pRtlWow64EnableFsRedirectionEx( TRUE, NULL );
861     ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
862     status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)1 );
863     ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
864     status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)0xDEADBEEF );
865     ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status );
866 
867     status = pRtlWow64EnableFsRedirection( FALSE );
868     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
869     status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur );
870     ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status );
871     ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur );
872 
873     pRtlWow64EnableFsRedirectionEx( old, &cur );
874 }
875 
876 START_TEST(directory)
877 {
878     WCHAR sysdir[MAX_PATH];
879     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
880     if (!hntdll)
881     {
882         skip("not running on NT, skipping test\n");
883         return;
884     }
885 
886     pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
887     pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
888     pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
889     pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile");
890     pNtSetInformationFile   = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
891     pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
892     pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
893     pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
894     pRtlFreeUnicodeString   = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
895     pRtlCompareUnicodeString = (void *)GetProcAddress(hntdll, "RtlCompareUnicodeString");
896     pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
897     pRtlWow64EnableFsRedirection = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirection");
898     pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll,"RtlWow64EnableFsRedirectionEx");
899 
900     GetSystemDirectoryW( sysdir, MAX_PATH );
901     test_directory_sort( sysdir );
902     test_NtQueryDirectoryFile();
903     test_NtQueryDirectoryFile_case();
904     test_redirection();
905 }
906