kernel32: Rewrote toolhelp implementation only on top of ntdll functions.
[wine] / dlls / kernel / tests / change.c
1 /*
2  * Tests for file change notification functions
3  *
4  * Copyright (c) 2004 Hans Leidekker
5  * Copyright 2006 Mike McCormack for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /* TODO: - security attribute changes
23  *       - compound filter and multiple notifications
24  *       - subtree notifications
25  *       - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
26  *         FILE_NOTIFY_CHANGE_CREATION
27  */
28
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "wine/test.h"
35 #include <windef.h>
36 #include <winbase.h>
37 #include <winternl.h>
38
39 static DWORD CALLBACK NotificationThread(LPVOID arg)
40 {
41     HANDLE change = (HANDLE) arg;
42     BOOL ret = FALSE;
43     DWORD status;
44
45     status = WaitForSingleObject(change, 100);
46
47     if (status == WAIT_OBJECT_0 ) {
48         ret = FindNextChangeNotification(change);
49     }
50
51     ret = FindCloseChangeNotification(change);
52     ok( ret, "FindCloseChangeNotification error: %ld\n",
53        GetLastError());
54
55     ExitThread((DWORD)ret);
56 }
57
58 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
59 {
60     HANDLE change, thread;
61     DWORD threadId;
62
63     change = FindFirstChangeNotificationA(path, subtree, flags);
64     ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
65
66     thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
67                           0, &threadId);
68     ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
69
70     return thread;
71 }
72
73 static DWORD FinishNotificationThread(HANDLE thread)
74 {
75     DWORD status, exitcode;
76
77     status = WaitForSingleObject(thread, 5000);
78     ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
79
80     ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
81
82     return exitcode;
83 }
84
85 static void test_FindFirstChangeNotification(void)
86 {
87     HANDLE change, file, thread;
88     DWORD attributes, count;
89     BOOL ret;
90
91     char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
92     char filename1[MAX_PATH], filename2[MAX_PATH];
93     static const char prefix[] = "FCN";
94     char buffer[2048];
95
96     /* pathetic checks */
97
98     change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
99     ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
100        "FindFirstChangeNotification error: %ld\n", GetLastError());
101
102     if (0) /* This documents win2k behavior. It crashes on win98. */
103     { 
104         change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
105         ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
106         "FindFirstChangeNotification error: %ld\n", GetLastError());
107     }
108
109     ret = FindNextChangeNotification(NULL);
110     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
111        GetLastError());
112
113     ret = FindCloseChangeNotification(NULL);
114     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
115        GetLastError());
116
117     ret = GetTempPathA(MAX_PATH, workdir);
118     ok(ret, "GetTempPathA error: %ld\n", GetLastError());
119
120     lstrcatA(workdir, "testFileChangeNotification");
121
122     ret = CreateDirectoryA(workdir, NULL);
123     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
124
125     ret = GetTempFileNameA(workdir, prefix, 0, filename1);
126     ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
127
128     file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
129                        FILE_ATTRIBUTE_NORMAL, 0);
130     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
131     ret = CloseHandle(file);
132     ok( ret, "CloseHandle error: %ld\n", GetLastError());
133
134     /* Try to register notification for a file. win98 and win2k behave differently here */
135     change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
136     ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
137                                           GetLastError() == ERROR_FILE_NOT_FOUND),
138        "FindFirstChangeNotification error: %ld\n", GetLastError());
139
140     lstrcpyA(dirname1, filename1);
141     lstrcatA(dirname1, "dir");
142
143     lstrcpyA(dirname2, dirname1);
144     lstrcatA(dirname2, "new");
145
146     ret = CreateDirectoryA(dirname1, NULL);
147     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
148
149     /* What if we move the directory we registered notification for? */
150     thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
151     ret = MoveFileA(dirname1, dirname2);
152     ok(ret, "MoveFileA error: %ld\n", GetLastError());
153     ok(FinishNotificationThread(thread), "Missed notification\n");
154
155     /* What if we remove the directory we registered notification for? */
156     thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
157     ret = RemoveDirectoryA(dirname2);
158     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
159
160     /* win98 and win2k behave differently here */
161     ret = FinishNotificationThread(thread);
162     ok(ret || !ret, "You'll never read this\n");
163
164     /* functional checks */
165
166     /* Create a directory */
167     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
168     ret = CreateDirectoryA(dirname1, NULL);
169     ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
170     ok(FinishNotificationThread(thread), "Missed notification\n");
171
172     /* Rename a directory */
173     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
174     ret = MoveFileA(dirname1, dirname2);
175     ok(ret, "MoveFileA error: %ld\n", GetLastError());
176     ok(FinishNotificationThread(thread), "Missed notification\n");
177
178     /* Delete a directory */
179     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
180     ret = RemoveDirectoryA(dirname2);
181     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
182     ok(FinishNotificationThread(thread), "Missed notification\n");
183
184     lstrcpyA(filename2, filename1);
185     lstrcatA(filename2, "new");
186
187     /* Rename a file */
188     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
189     ret = MoveFileA(filename1, filename2);
190     ok(ret, "MoveFileA error: %ld\n", GetLastError());
191     ok(FinishNotificationThread(thread), "Missed notification\n");
192
193     /* Delete a file */
194     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
195     ret = DeleteFileA(filename2);
196     ok(ret, "DeleteFileA error: %ld\n", GetLastError());
197     ok(FinishNotificationThread(thread), "Missed notification\n");
198
199     /* Create a file */
200     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
201     file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
202                        FILE_ATTRIBUTE_NORMAL, 0);
203     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
204     ret = CloseHandle(file);
205     ok( ret, "CloseHandle error: %ld\n", GetLastError());
206     ok(FinishNotificationThread(thread), "Missed notification\n");
207
208     attributes = GetFileAttributesA(filename2);
209     ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
210     attributes &= FILE_ATTRIBUTE_READONLY;
211
212     /* Change file attributes */
213     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
214     ret = SetFileAttributesA(filename2, attributes);
215     ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
216     ok(FinishNotificationThread(thread), "Missed notification\n");
217
218     /* Change last write time by writing to a file */
219     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
220     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
221                        FILE_ATTRIBUTE_NORMAL, 0);
222     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
223     ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
224     ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
225     ret = CloseHandle(file);
226     ok( ret, "CloseHandle error: %ld\n", GetLastError());
227     ok(FinishNotificationThread(thread), "Missed notification\n");
228
229     /* Change file size by truncating a file */
230     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
231     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
232                        FILE_ATTRIBUTE_NORMAL, 0);
233     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
234     ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
235     ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
236     ret = CloseHandle(file);
237     ok( ret, "CloseHandle error: %ld\n", GetLastError());
238     ok(FinishNotificationThread(thread), "Missed notification\n");
239
240     /* clean up */
241     
242     ret = DeleteFileA(filename2);
243     ok(ret, "DeleteFileA error: %ld\n", GetLastError());
244
245     ret = RemoveDirectoryA(workdir);
246     ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
247 }
248
249 /* this test concentrates more on the wait behaviour of the handle */
250 static void test_ffcn(void)
251 {
252     DWORD filter;
253     HANDLE handle;
254     LONG r;
255     WCHAR path[MAX_PATH], subdir[MAX_PATH];
256     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
257     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
258
259     r = GetTempPathW( MAX_PATH, path );
260     ok( r != 0, "temp path failed\n");
261     if (!r)
262         return;
263
264     lstrcatW( path, szBoo );
265     lstrcpyW( subdir, path );
266     lstrcatW( subdir, szHoo );
267
268     RemoveDirectoryW( subdir );
269     RemoveDirectoryW( path );
270     
271     r = CreateDirectoryW(path, NULL);
272     ok( r == TRUE, "failed to create directory\n");
273
274     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
275     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
276
277     handle = FindFirstChangeNotificationW( path, 1, filter);
278     ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
279
280     r = WaitForSingleObject( handle, 0 );
281     ok( r == STATUS_TIMEOUT, "should time out\n");
282
283     r = CreateDirectoryW( subdir, NULL );
284     ok( r == TRUE, "failed to create subdir\n");
285
286     r = WaitForSingleObject( handle, 0 );
287     ok( r == WAIT_OBJECT_0, "should be ready\n");
288
289     r = WaitForSingleObject( handle, 0 );
290     ok( r == WAIT_OBJECT_0, "should be ready\n");
291
292     r = FindNextChangeNotification(handle);
293     ok( r == TRUE, "find next failed\n");
294
295     r = WaitForSingleObject( handle, 0 );
296     ok( r == STATUS_TIMEOUT, "should time out\n");
297
298     r = RemoveDirectoryW( subdir );
299     ok( r == TRUE, "failed to remove subdir\n");
300
301     r = WaitForSingleObject( handle, 0 );
302     ok( r == WAIT_OBJECT_0, "should be ready\n");
303
304     r = WaitForSingleObject( handle, 0 );
305     ok( r == WAIT_OBJECT_0, "should be ready\n");
306
307     r = FindNextChangeNotification(handle);
308     ok( r == TRUE, "find next failed\n");
309
310     r = FindNextChangeNotification(handle);
311     ok( r == TRUE, "find next failed\n");
312
313     r = FindCloseChangeNotification(handle);
314     ok( r == TRUE, "should succeed\n");
315
316     r = RemoveDirectoryW( path );
317     ok( r == TRUE, "failed to remove dir\n");
318 }
319
320 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
321                          LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
322 fnReadDirectoryChangesW pReadDirectoryChangesW;
323
324 static void test_readdirectorychanges(void)
325 {
326     HANDLE hdir;
327     char buffer[0x1000];
328     DWORD fflags, filter = 0, r, dwCount;
329     OVERLAPPED ov;
330     WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
331     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
332     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
333     static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
334     PFILE_NOTIFY_INFORMATION pfni;
335
336     if (!pReadDirectoryChangesW)
337         return;
338
339     r = GetTempPathW( MAX_PATH, path );
340     ok( r != 0, "temp path failed\n");
341     if (!r)
342         return;
343
344     lstrcatW( path, szBoo );
345     lstrcpyW( subdir, path );
346     lstrcatW( subdir, szHoo );
347
348     lstrcpyW( subsubdir, path );
349     lstrcatW( subsubdir, szGa );
350
351     RemoveDirectoryW( subsubdir );
352     RemoveDirectoryW( subdir );
353     RemoveDirectoryW( path );
354     
355     r = CreateDirectoryW(path, NULL);
356     ok( r == TRUE, "failed to create directory\n");
357
358     SetLastError(0xd0b00b00);
359     r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
360     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
361     ok(r==FALSE, "should return false\n");
362
363     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
364     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
365                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
366                         OPEN_EXISTING, fflags, NULL);
367     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
368
369     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
370
371     SetLastError(0xd0b00b00);
372     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
373     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
374     ok(r==FALSE, "should return false\n");
375
376     SetLastError(0xd0b00b00);
377     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
378     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
379     ok(r==FALSE, "should return false\n");
380
381     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
382     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
383     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
384     filter |= FILE_NOTIFY_CHANGE_SIZE;
385     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
386     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
387     filter |= FILE_NOTIFY_CHANGE_CREATION;
388     filter |= FILE_NOTIFY_CHANGE_SECURITY;
389
390     SetLastError(0xd0b00b00);
391     ov.Internal = 0;
392     ov.InternalHigh = 0;
393     memset( buffer, 0, sizeof buffer );
394
395     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
396     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
397     ok(r==FALSE, "should return false\n");
398
399     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
400     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
401     ok(r==FALSE, "should return false\n");
402
403     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
404     ok(r==TRUE, "should return true\n");
405
406     r = WaitForSingleObject( ov.hEvent, 10 );
407     ok( r == STATUS_TIMEOUT, "should timeout\n" );
408
409     r = CreateDirectoryW( subdir, NULL );
410     ok( r == TRUE, "failed to create directory\n");
411
412     r = WaitForSingleObject( ov.hEvent, 1000 );
413     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
414
415     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
416     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
417
418     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
419     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
420     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
421     ok( pfni->FileNameLength == 6, "len wrong\n" );
422     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
423
424     ResetEvent(ov.hEvent);
425     SetLastError(0xd0b00b00);
426     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
427     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
428     ok(r==FALSE, "should return false\n");
429
430     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
431     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
432     ok(r==FALSE, "should return false\n");
433
434     filter = FILE_NOTIFY_CHANGE_SIZE;
435
436     SetEvent(ov.hEvent);
437     ov.Internal = 1;
438     ov.InternalHigh = 1;
439     S(U(ov)).Offset = 0;
440     S(U(ov)).OffsetHigh = 0;
441     memset( buffer, 0, sizeof buffer );
442     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
443     ok(r==TRUE, "should return true\n");
444
445     ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
446     ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
447
448     r = WaitForSingleObject( ov.hEvent, 0 );
449     ok( r == STATUS_TIMEOUT, "should timeout\n" );
450
451     r = RemoveDirectoryW( subdir );
452     ok( r == TRUE, "failed to remove directory\n");
453
454     r = WaitForSingleObject( ov.hEvent, 1000 );
455     ok( r == WAIT_OBJECT_0, "should be ready\n" );
456
457     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
458     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
459
460     r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
461     ok( r == TRUE, "getoverlappedresult failed\n");
462     ok( dwCount == 0x12, "count wrong\n");
463
464     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
465     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
466     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
467     ok( pfni->FileNameLength == 6, "len wrong\n" );
468     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
469
470     /* what happens if the buffer is too small? */
471     r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
472     ok(r==TRUE, "should return true\n");
473
474     r = CreateDirectoryW( subdir, NULL );
475     ok( r == TRUE, "failed to create directory\n");
476
477     r = WaitForSingleObject( ov.hEvent, 1000 );
478     ok( r == WAIT_OBJECT_0, "should be ready\n" );
479
480     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
481     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
482
483     /* test the recursive watch */
484     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
485     ok(r==TRUE, "should return true\n");
486
487     r = CreateDirectoryW( subsubdir, NULL );
488     ok( r == TRUE, "failed to create directory\n");
489
490     r = WaitForSingleObject( ov.hEvent, 1000 );
491     ok( r == WAIT_OBJECT_0, "should be ready\n" );
492
493     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
494     ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
495
496     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
497     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
498     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
499     ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
500     ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
501
502     r = RemoveDirectoryW( subsubdir );
503     ok( r == TRUE, "failed to remove directory\n");
504
505     ov.Internal = 1;
506     ov.InternalHigh = 1;
507     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
508     ok(r==TRUE, "should return true\n");
509
510     r = RemoveDirectoryW( subdir );
511     ok( r == TRUE, "failed to remove directory\n");
512
513     r = WaitForSingleObject( ov.hEvent, 1000 );
514     ok( r == WAIT_OBJECT_0, "should be ready\n" );
515
516     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
517     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
518     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
519     ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
520     ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
521
522     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
523     ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
524
525     CloseHandle(hdir);
526
527     r = RemoveDirectoryW( path );
528     ok( r == TRUE, "failed to remove directory\n");
529 }
530
531 /* show the behaviour when a null buffer is passed */
532 static void test_readdirectorychanges_null(void)
533 {
534     NTSTATUS r;
535     HANDLE hdir;
536     char buffer[0x1000];
537     DWORD fflags, filter = 0;
538     OVERLAPPED ov;
539     WCHAR path[MAX_PATH], subdir[MAX_PATH];
540     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
541     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
542     PFILE_NOTIFY_INFORMATION pfni;
543
544     if (!pReadDirectoryChangesW)
545         return;
546
547     r = GetTempPathW( MAX_PATH, path );
548     ok( r != 0, "temp path failed\n");
549     if (!r)
550         return;
551
552     lstrcatW( path, szBoo );
553     lstrcpyW( subdir, path );
554     lstrcatW( subdir, szHoo );
555
556     RemoveDirectoryW( subdir );
557     RemoveDirectoryW( path );
558     
559     r = CreateDirectoryW(path, NULL);
560     ok( r == TRUE, "failed to create directory\n");
561
562     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
563     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
564                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
565                         OPEN_EXISTING, fflags, NULL);
566     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
567
568     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
569
570     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
571     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
572
573     SetLastError(0xd0b00b00);
574     ov.Internal = 0;
575     ov.InternalHigh = 0;
576     memset( buffer, 0, sizeof buffer );
577
578     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
579     ok(r==TRUE, "should return true\n");
580
581     r = WaitForSingleObject( ov.hEvent, 0 );
582     ok( r == STATUS_TIMEOUT, "should timeout\n" );
583
584     r = CreateDirectoryW( subdir, NULL );
585     ok( r == TRUE, "failed to create directory\n");
586
587     r = WaitForSingleObject( ov.hEvent, 0 );
588     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
589
590     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
591     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
592
593     ov.Internal = 0;
594     ov.InternalHigh = 0;
595     S(U(ov)).Offset = 0;
596     S(U(ov)).OffsetHigh = 0;
597     memset( buffer, 0, sizeof buffer );
598
599     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
600     ok(r==TRUE, "should return true\n");
601
602     r = WaitForSingleObject( ov.hEvent, 0 );
603     ok( r == STATUS_TIMEOUT, "should timeout\n" );
604
605     r = RemoveDirectoryW( subdir );
606     ok( r == TRUE, "failed to remove directory\n");
607
608     r = WaitForSingleObject( ov.hEvent, 1000 );
609     ok( r == WAIT_OBJECT_0, "should be ready\n" );
610
611     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
612     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
613
614     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
615     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
616
617     CloseHandle(hdir);
618
619     r = RemoveDirectoryW( path );
620     ok( r == TRUE, "failed to remove directory\n");
621 }
622
623 static void test_readdirectorychanges_filedir(void)
624 {
625     NTSTATUS r;
626     HANDLE hdir, hfile;
627     char buffer[0x1000];
628     DWORD fflags, filter = 0;
629     OVERLAPPED ov;
630     WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
631     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
632     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
633     static const WCHAR szFoo[] = { '\\','f','o','o',0 };
634     PFILE_NOTIFY_INFORMATION pfni;
635
636     r = GetTempPathW( MAX_PATH, path );
637     ok( r != 0, "temp path failed\n");
638     if (!r)
639         return;
640
641     lstrcatW( path, szBoo );
642     lstrcpyW( subdir, path );
643     lstrcatW( subdir, szHoo );
644
645     lstrcpyW( file, path );
646     lstrcatW( file, szFoo );
647
648     DeleteFileW( file );
649     RemoveDirectoryW( subdir );
650     RemoveDirectoryW( path );
651     
652     r = CreateDirectoryW(path, NULL);
653     ok( r == TRUE, "failed to create directory\n");
654
655     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
656     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
657                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
658                         OPEN_EXISTING, fflags, NULL);
659     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
660
661     ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
662
663     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
664
665     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
666     ok(r==TRUE, "should return true\n");
667
668     r = WaitForSingleObject( ov.hEvent, 10 );
669     ok( r == WAIT_TIMEOUT, "should timeout\n" );
670
671     r = CreateDirectoryW( subdir, NULL );
672     ok( r == TRUE, "failed to create directory\n");
673
674     hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
675     ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
676     ok( CloseHandle(hfile), "failed toc lose file\n");
677
678     r = WaitForSingleObject( ov.hEvent, 1000 );
679     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
680
681     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
682     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
683
684     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
685     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
686     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
687     ok( pfni->FileNameLength == 6, "len wrong\n" );
688     ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
689
690     r = DeleteFileW( file );
691     ok( r == TRUE, "failed to delete file\n");
692
693     r = RemoveDirectoryW( subdir );
694     ok( r == TRUE, "failed to remove directory\n");
695
696     CloseHandle(hdir);
697
698     r = RemoveDirectoryW( path );
699     ok( r == TRUE, "failed to remove directory\n");
700 }
701
702 START_TEST(change)
703 {
704     HMODULE hkernel32 = GetModuleHandle("kernel32");
705     pReadDirectoryChangesW = (fnReadDirectoryChangesW)
706         GetProcAddress(hkernel32, "ReadDirectoryChangesW");
707
708     test_FindFirstChangeNotification();
709     test_ffcn();
710     test_readdirectorychanges();
711     test_readdirectorychanges_null();
712     test_readdirectorychanges_filedir();
713 }