~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/kernel32/tests/change.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 = arg;
 42     BOOL notified = FALSE;
 43     BOOL ret = FALSE;
 44     DWORD status;
 45 
 46     status = WaitForSingleObject(change, 100);
 47 
 48     if (status == WAIT_OBJECT_0 ) {
 49         notified = TRUE;
 50         ret = FindNextChangeNotification(change);
 51     }
 52 
 53     ret = FindCloseChangeNotification(change);
 54     ok( ret, "FindCloseChangeNotification error: %d\n",
 55        GetLastError());
 56 
 57     ExitThread((DWORD)notified);
 58 }
 59 
 60 static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
 61 {
 62     HANDLE change, thread;
 63     DWORD threadId;
 64 
 65     change = FindFirstChangeNotificationA(path, subtree, flags);
 66     ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
 67 
 68     thread = CreateThread(NULL, 0, NotificationThread, change, 0, &threadId);
 69     ok(thread != NULL, "CreateThread error: %d\n", GetLastError());
 70 
 71     return thread;
 72 }
 73 
 74 static DWORD FinishNotificationThread(HANDLE thread)
 75 {
 76     DWORD status, exitcode;
 77 
 78     status = WaitForSingleObject(thread, 5000);
 79     ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %d error %d\n", status, GetLastError());
 80 
 81     ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
 82 
 83     return exitcode;
 84 }
 85 
 86 static void test_FindFirstChangeNotification(void)
 87 {
 88     HANDLE change, file, thread;
 89     DWORD attributes, count;
 90     BOOL ret;
 91 
 92     char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
 93     char filename1[MAX_PATH], filename2[MAX_PATH];
 94     static const char prefix[] = "FCN";
 95     char buffer[2048];
 96 
 97     /* pathetic checks */
 98 
 99     change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
100     ok(change == INVALID_HANDLE_VALUE, "Expected INVALID_HANDLE_VALUE, got %p\n", change);
101     ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
102        GetLastError() == ERROR_NO_MORE_FILES, /* win95 */
103        "FindFirstChangeNotification error: %d\n", GetLastError());
104 
105     if (0) /* This documents win2k behavior. It crashes on win98. */
106     { 
107         change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
108         ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
109         "FindFirstChangeNotification error: %d\n", GetLastError());
110     }
111 
112     ret = FindNextChangeNotification(NULL);
113     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %d\n",
114        GetLastError());
115 
116     ret = FindCloseChangeNotification(NULL);
117     ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %d\n",
118        GetLastError());
119 
120     ret = GetTempPathA(MAX_PATH, workdir);
121     ok(ret, "GetTempPathA error: %d\n", GetLastError());
122 
123     lstrcatA(workdir, "testFileChangeNotification");
124 
125     ret = CreateDirectoryA(workdir, NULL);
126     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
127 
128     ret = GetTempFileNameA(workdir, prefix, 0, filename1);
129     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
130 
131     file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
132                        FILE_ATTRIBUTE_NORMAL, 0);
133     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
134     ret = CloseHandle(file);
135     ok( ret, "CloseHandle error: %d\n", GetLastError());
136 
137     /* Try to register notification for a file. win98 and win2k behave differently here */
138     change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
139     ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
140                                           GetLastError() == ERROR_FILE_NOT_FOUND),
141        "FindFirstChangeNotification error: %d\n", GetLastError());
142 
143     lstrcpyA(dirname1, filename1);
144     lstrcatA(dirname1, "dir");
145 
146     lstrcpyA(dirname2, dirname1);
147     lstrcatA(dirname2, "new");
148 
149     ret = CreateDirectoryA(dirname1, NULL);
150     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
151 
152     /* What if we move the directory we registered notification for? */
153     thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
154     ret = MoveFileA(dirname1, dirname2);
155     ok(ret, "MoveFileA error: %d\n", GetLastError());
156     /* win9x and win2k behave differently here, don't check result */
157     FinishNotificationThread(thread);
158 
159     /* What if we remove the directory we registered notification for? */
160     thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
161     ret = RemoveDirectoryA(dirname2);
162     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
163     /* win9x and win2k behave differently here, don't check result */
164     FinishNotificationThread(thread);
165 
166     /* functional checks */
167 
168     /* Create a directory */
169     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
170     ret = CreateDirectoryA(dirname1, NULL);
171     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
172     ok(FinishNotificationThread(thread), "Missed notification\n");
173 
174     /* Rename a directory */
175     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
176     ret = MoveFileA(dirname1, dirname2);
177     ok(ret, "MoveFileA error: %d\n", GetLastError());
178     ok(FinishNotificationThread(thread), "Missed notification\n");
179 
180     /* Delete a directory */
181     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
182     ret = RemoveDirectoryA(dirname2);
183     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
184     ok(FinishNotificationThread(thread), "Missed notification\n");
185 
186     lstrcpyA(filename2, filename1);
187     lstrcatA(filename2, "new");
188 
189     /* Rename a file */
190     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
191     ret = MoveFileA(filename1, filename2);
192     ok(ret, "MoveFileA error: %d\n", GetLastError());
193     ok(FinishNotificationThread(thread), "Missed notification\n");
194 
195     /* Delete a file */
196     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
197     ret = DeleteFileA(filename2);
198     ok(ret, "DeleteFileA error: %d\n", GetLastError());
199     ok(FinishNotificationThread(thread), "Missed notification\n");
200 
201     /* Create a file */
202     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
203     file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
204                        FILE_ATTRIBUTE_NORMAL, 0);
205     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
206     ret = CloseHandle(file);
207     ok( ret, "CloseHandle error: %d\n", GetLastError());
208     ok(FinishNotificationThread(thread), "Missed notification\n");
209 
210     attributes = GetFileAttributesA(filename2);
211     ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %d\n", GetLastError());
212     attributes &= FILE_ATTRIBUTE_READONLY;
213 
214     /* Change file attributes */
215     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
216     ret = SetFileAttributesA(filename2, attributes);
217     ok(ret, "SetFileAttributesA error: %d\n", GetLastError());
218     ok(FinishNotificationThread(thread), "Missed notification\n");
219 
220     /* Change last write time by writing to a file */
221     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
222     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
223                        FILE_ATTRIBUTE_NORMAL, 0);
224     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
225     memset(buffer, 0, sizeof(buffer));
226     ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
227     ok(ret && count == sizeof(buffer), "WriteFile error: %d\n", GetLastError());
228     ret = CloseHandle(file);
229     ok( ret, "CloseHandle error: %d\n", GetLastError());
230     ok(FinishNotificationThread(thread), "Missed notification\n");
231 
232     /* Change file size by truncating a file */
233     thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
234     file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
235                        FILE_ATTRIBUTE_NORMAL, 0);
236     ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %d\n", GetLastError());
237     ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
238     ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %d\n", GetLastError());
239     ret = CloseHandle(file);
240     ok( ret, "CloseHandle error: %d\n", GetLastError());
241     ok(FinishNotificationThread(thread), "Missed notification\n");
242 
243     /* clean up */
244     
245     ret = DeleteFileA(filename2);
246     ok(ret, "DeleteFileA error: %d\n", GetLastError());
247 
248     ret = RemoveDirectoryA(workdir);
249     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
250 }
251 
252 /* this test concentrates more on the wait behaviour of the handle */
253 static void test_ffcn(void)
254 {
255     DWORD filter;
256     HANDLE handle;
257     LONG r;
258     WCHAR path[MAX_PATH], subdir[MAX_PATH];
259     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
260     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
261 
262     SetLastError(0xdeadbeef);
263     r = GetTempPathW( MAX_PATH, path );
264     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
265     {
266         win_skip("GetTempPathW is not implemented\n");
267         return;
268     }
269     ok( r != 0, "temp path failed\n");
270     if (!r)
271         return;
272 
273     lstrcatW( path, szBoo );
274     lstrcpyW( subdir, path );
275     lstrcatW( subdir, szHoo );
276 
277     RemoveDirectoryW( subdir );
278     RemoveDirectoryW( path );
279     
280     r = CreateDirectoryW(path, NULL);
281     ok( r == TRUE, "failed to create directory\n");
282 
283     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
284     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
285 
286     handle = FindFirstChangeNotificationW( path, 1, filter);
287     ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
288 
289     r = WaitForSingleObject( handle, 0 );
290     ok( r == STATUS_TIMEOUT, "should time out\n");
291 
292     r = CreateDirectoryW( subdir, NULL );
293     ok( r == TRUE, "failed to create subdir\n");
294 
295     r = WaitForSingleObject( handle, 0 );
296     ok( r == WAIT_OBJECT_0, "should be ready\n");
297 
298     r = WaitForSingleObject( handle, 0 );
299     ok( r == WAIT_OBJECT_0, "should be ready\n");
300 
301     r = FindNextChangeNotification(handle);
302     ok( r == TRUE, "find next failed\n");
303 
304     r = WaitForSingleObject( handle, 0 );
305     ok( r == STATUS_TIMEOUT, "should time out\n");
306 
307     r = RemoveDirectoryW( subdir );
308     ok( r == TRUE, "failed to remove subdir\n");
309 
310     r = WaitForSingleObject( handle, 0 );
311     ok( r == WAIT_OBJECT_0, "should be ready\n");
312 
313     r = WaitForSingleObject( handle, 0 );
314     ok( r == WAIT_OBJECT_0, "should be ready\n");
315 
316     r = FindNextChangeNotification(handle);
317     ok( r == TRUE, "find next failed\n");
318 
319     r = FindNextChangeNotification(handle);
320     ok( r == TRUE, "find next failed\n");
321 
322     r = FindCloseChangeNotification(handle);
323     ok( r == TRUE, "should succeed\n");
324 
325     r = RemoveDirectoryW( path );
326     ok( r == TRUE, "failed to remove dir\n");
327 }
328 
329 /* this test concentrates on the wait behavior when multiple threads are
330  * waiting on a change notification handle. */
331 static void test_ffcnMultipleThreads(void)
332 {
333     LONG r;
334     DWORD filter, threadId, status, exitcode;
335     HANDLE handles[2];
336     char path[MAX_PATH];
337 
338     r = GetTempPathA(MAX_PATH, path);
339     ok(r, "GetTempPathA error: %d\n", GetLastError());
340 
341     lstrcatA(path, "ffcnTestMultipleThreads");
342 
343     RemoveDirectoryA(path);
344 
345     r = CreateDirectoryA(path, NULL);
346     ok(r, "CreateDirectoryA error: %d\n", GetLastError());
347 
348     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
349     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
350 
351     handles[0] = FindFirstChangeNotificationA(path, FALSE, filter);
352     ok(handles[0] != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %d\n", GetLastError());
353 
354     /* Test behavior if a waiting thread holds the last reference to a change
355      * directory object with an empty wine user APC queue for this thread (bug #7286) */
356 
357     /* Create our notification thread */
358     handles[1] = CreateThread(NULL, 0, NotificationThread, handles[0], 0,
359                               &threadId);
360     ok(handles[1] != NULL, "CreateThread error: %d\n", GetLastError());
361 
362     status = WaitForMultipleObjects(2, handles, FALSE, 5000);
363     ok(status == WAIT_OBJECT_0 || status == WAIT_OBJECT_0+1, "WaitForMultipleObjects status %d error %d\n", status, GetLastError());
364     ok(GetExitCodeThread(handles[1], &exitcode), "Could not retrieve thread exit code\n");
365 
366     /* Clean up */
367     r = RemoveDirectoryA( path );
368     ok( r == TRUE, "failed to remove dir\n");
369 }
370 
371 typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
372                          LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
373 fnReadDirectoryChangesW pReadDirectoryChangesW;
374 
375 static void test_readdirectorychanges(void)
376 {
377     HANDLE hdir;
378     char buffer[0x1000];
379     DWORD fflags, filter = 0, r, dwCount;
380     OVERLAPPED ov;
381     WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
382     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
383     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
384     static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
385     PFILE_NOTIFY_INFORMATION pfni;
386 
387     if (!pReadDirectoryChangesW)
388     {
389         win_skip("ReadDirectoryChangesW is not available\n");
390         return;
391     }
392 
393     SetLastError(0xdeadbeef);
394     r = GetTempPathW( MAX_PATH, path );
395     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
396     {
397         win_skip("GetTempPathW is not implemented\n");
398         return;
399     }
400     ok( r != 0, "temp path failed\n");
401     if (!r)
402         return;
403 
404     lstrcatW( path, szBoo );
405     lstrcpyW( subdir, path );
406     lstrcatW( subdir, szHoo );
407 
408     lstrcpyW( subsubdir, path );
409     lstrcatW( subsubdir, szGa );
410 
411     RemoveDirectoryW( subsubdir );
412     RemoveDirectoryW( subdir );
413     RemoveDirectoryW( path );
414     
415     r = CreateDirectoryW(path, NULL);
416     ok( r == TRUE, "failed to create directory\n");
417 
418     SetLastError(0xd0b00b00);
419     r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
420     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
421     ok(r==FALSE, "should return false\n");
422 
423     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
424     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
425                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
426                         OPEN_EXISTING, fflags, NULL);
427     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
428 
429     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
430 
431     SetLastError(0xd0b00b00);
432     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
433     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
434     ok(r==FALSE, "should return false\n");
435 
436     SetLastError(0xd0b00b00);
437     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
438     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
439     ok(r==FALSE, "should return false\n");
440 
441     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
442     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
443     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
444     filter |= FILE_NOTIFY_CHANGE_SIZE;
445     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
446     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
447     filter |= FILE_NOTIFY_CHANGE_CREATION;
448     filter |= FILE_NOTIFY_CHANGE_SECURITY;
449 
450     SetLastError(0xd0b00b00);
451     ov.Internal = 0;
452     ov.InternalHigh = 0;
453     memset( buffer, 0, sizeof buffer );
454 
455     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
456     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
457     ok(r==FALSE, "should return false\n");
458 
459     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
460     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
461     ok(r==FALSE, "should return false\n");
462 
463     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
464     ok(r==TRUE, "should return true\n");
465 
466     r = WaitForSingleObject( ov.hEvent, 10 );
467     ok( r == STATUS_TIMEOUT, "should timeout\n" );
468 
469     r = CreateDirectoryW( subdir, NULL );
470     ok( r == TRUE, "failed to create directory\n");
471 
472     r = WaitForSingleObject( ov.hEvent, 1000 );
473     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
474 
475     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
476     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
477 
478     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
479     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
480     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
481     ok( pfni->FileNameLength == 6, "len wrong\n" );
482     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
483 
484     ResetEvent(ov.hEvent);
485     SetLastError(0xd0b00b00);
486     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
487     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
488     ok(r==FALSE, "should return false\n");
489 
490     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
491     ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
492     ok(r==FALSE, "should return false\n");
493 
494     filter = FILE_NOTIFY_CHANGE_SIZE;
495 
496     SetEvent(ov.hEvent);
497     ov.Internal = 1;
498     ov.InternalHigh = 1;
499     S(U(ov)).Offset = 0;
500     S(U(ov)).OffsetHigh = 0;
501     memset( buffer, 0, sizeof buffer );
502     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
503     ok(r==TRUE, "should return true\n");
504 
505     ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
506     ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
507 
508     r = WaitForSingleObject( ov.hEvent, 0 );
509     ok( r == STATUS_TIMEOUT, "should timeout\n" );
510 
511     r = RemoveDirectoryW( subdir );
512     ok( r == TRUE, "failed to remove directory\n");
513 
514     r = WaitForSingleObject( ov.hEvent, 1000 );
515     ok( r == WAIT_OBJECT_0, "should be ready\n" );
516 
517     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
518     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
519 
520     if (ov.Internal == STATUS_SUCCESS)
521     {
522         r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
523         ok( r == TRUE, "getoverlappedresult failed\n");
524         ok( dwCount == 0x12, "count wrong\n");
525     }
526 
527     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
528     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
529     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
530     ok( pfni->FileNameLength == 6, "len wrong\n" );
531     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
532 
533     /* what happens if the buffer is too small? */
534     r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
535     ok(r==TRUE, "should return true\n");
536 
537     r = CreateDirectoryW( subdir, NULL );
538     ok( r == TRUE, "failed to create directory\n");
539 
540     r = WaitForSingleObject( ov.hEvent, 1000 );
541     ok( r == WAIT_OBJECT_0, "should be ready\n" );
542 
543     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
544     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
545 
546     /* test the recursive watch */
547     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
548     ok(r==TRUE, "should return true\n");
549 
550     r = CreateDirectoryW( subsubdir, NULL );
551     ok( r == TRUE, "failed to create directory\n");
552 
553     r = WaitForSingleObject( ov.hEvent, 1000 );
554     ok( r == WAIT_OBJECT_0, "should be ready\n" );
555 
556     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
557     ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
558 
559     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
560     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
561     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
562     ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong\n" );
563     ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
564 
565     r = RemoveDirectoryW( subsubdir );
566     ok( r == TRUE, "failed to remove directory\n");
567 
568     ov.Internal = 1;
569     ov.InternalHigh = 1;
570     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
571     ok(r==TRUE, "should return true\n");
572 
573     r = RemoveDirectoryW( subdir );
574     ok( r == TRUE, "failed to remove directory\n");
575 
576     r = WaitForSingleObject( ov.hEvent, 1000 );
577     ok( r == WAIT_OBJECT_0, "should be ready\n" );
578 
579     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
580     /* we may get a notification for the parent dir too */
581     if (pfni->Action == FILE_ACTION_MODIFIED && pfni->NextEntryOffset)
582     {
583         ok( pfni->FileNameLength == 3*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
584         ok( !memcmp(pfni->FileName,&szGa[1],3*sizeof(WCHAR)), "name wrong\n" );
585         pfni = (PFILE_NOTIFY_INFORMATION)((char *)pfni + pfni->NextEntryOffset);
586     }
587     ok( pfni->NextEntryOffset == 0, "offset wrong %u\n", pfni->NextEntryOffset );
588     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong %u\n", pfni->Action );
589     ok( pfni->FileNameLength == 6*sizeof(WCHAR), "len wrong %u\n", pfni->FileNameLength );
590     ok( !memcmp(pfni->FileName,&szGa[1],6*sizeof(WCHAR)), "name wrong\n" );
591 
592     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
593     dwCount = (char *)&pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] - buffer;
594     ok( ov.InternalHigh == dwCount, "ov.InternalHigh wrong %lu/%u\n",ov.InternalHigh, dwCount );
595 
596     CloseHandle(hdir);
597 
598     r = RemoveDirectoryW( path );
599     ok( r == TRUE, "failed to remove directory\n");
600 }
601 
602 /* show the behaviour when a null buffer is passed */
603 static void test_readdirectorychanges_null(void)
604 {
605     NTSTATUS r;
606     HANDLE hdir;
607     char buffer[0x1000];
608     DWORD fflags, filter = 0;
609     OVERLAPPED ov;
610     WCHAR path[MAX_PATH], subdir[MAX_PATH];
611     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
612     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
613     PFILE_NOTIFY_INFORMATION pfni;
614 
615     if (!pReadDirectoryChangesW)
616     {
617         win_skip("ReadDirectoryChangesW is not available\n");
618         return;
619     }
620     SetLastError(0xdeadbeef);
621     r = GetTempPathW( MAX_PATH, path );
622     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
623     {
624         win_skip("GetTempPathW is not implemented\n");
625         return;
626     }
627     ok( r != 0, "temp path failed\n");
628     if (!r)
629         return;
630 
631     lstrcatW( path, szBoo );
632     lstrcpyW( subdir, path );
633     lstrcatW( subdir, szHoo );
634 
635     RemoveDirectoryW( subdir );
636     RemoveDirectoryW( path );
637     
638     r = CreateDirectoryW(path, NULL);
639     ok( r == TRUE, "failed to create directory\n");
640 
641     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
642     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
643                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
644                         OPEN_EXISTING, fflags, NULL);
645     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
646 
647     ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
648 
649     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
650     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
651 
652     SetLastError(0xd0b00b00);
653     ov.Internal = 0;
654     ov.InternalHigh = 0;
655     memset( buffer, 0, sizeof buffer );
656 
657     r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
658     ok(r==TRUE, "should return true\n");
659 
660     r = WaitForSingleObject( ov.hEvent, 0 );
661     ok( r == STATUS_TIMEOUT, "should timeout\n" );
662 
663     r = CreateDirectoryW( subdir, NULL );
664     ok( r == TRUE, "failed to create directory\n");
665 
666     r = WaitForSingleObject( ov.hEvent, 0 );
667     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
668 
669     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
670     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
671 
672     ov.Internal = 0;
673     ov.InternalHigh = 0;
674     S(U(ov)).Offset = 0;
675     S(U(ov)).OffsetHigh = 0;
676     memset( buffer, 0, sizeof buffer );
677 
678     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
679     ok(r==TRUE, "should return true\n");
680 
681     r = WaitForSingleObject( ov.hEvent, 0 );
682     ok( r == STATUS_TIMEOUT, "should timeout\n" );
683 
684     r = RemoveDirectoryW( subdir );
685     ok( r == TRUE, "failed to remove directory\n");
686 
687     r = WaitForSingleObject( ov.hEvent, 1000 );
688     ok( r == WAIT_OBJECT_0, "should be ready\n" );
689 
690     ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
691     ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
692 
693     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
694     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
695 
696     CloseHandle(hdir);
697 
698     r = RemoveDirectoryW( path );
699     ok( r == TRUE, "failed to remove directory\n");
700 }
701 
702 static void test_readdirectorychanges_filedir(void)
703 {
704     NTSTATUS r;
705     HANDLE hdir, hfile;
706     char buffer[0x1000];
707     DWORD fflags, filter = 0;
708     OVERLAPPED ov;
709     WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
710     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
711     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
712     static const WCHAR szFoo[] = { '\\','f','o','o',0 };
713     PFILE_NOTIFY_INFORMATION pfni;
714 
715     SetLastError(0xdeadbeef);
716     r = GetTempPathW( MAX_PATH, path );
717     if (!r && (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
718     {
719         win_skip("GetTempPathW is not implemented\n");
720         return;
721     }
722     ok( r != 0, "temp path failed\n");
723     if (!r)
724         return;
725 
726     lstrcatW( path, szBoo );
727     lstrcpyW( subdir, path );
728     lstrcatW( subdir, szHoo );
729 
730     lstrcpyW( file, path );
731     lstrcatW( file, szFoo );
732 
733     DeleteFileW( file );
734     RemoveDirectoryW( subdir );
735     RemoveDirectoryW( path );
736     
737     r = CreateDirectoryW(path, NULL);
738     ok( r == TRUE, "failed to create directory\n");
739 
740     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
741     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY, 
742                         FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 
743                         OPEN_EXISTING, fflags, NULL);
744     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
745 
746     ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
747 
748     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
749 
750     r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
751     ok(r==TRUE, "should return true\n");
752 
753     r = WaitForSingleObject( ov.hEvent, 10 );
754     ok( r == WAIT_TIMEOUT, "should timeout\n" );
755 
756     r = CreateDirectoryW( subdir, NULL );
757     ok( r == TRUE, "failed to create directory\n");
758 
759     hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
760     ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
761     ok( CloseHandle(hfile), "failed toc lose file\n");
762 
763     r = WaitForSingleObject( ov.hEvent, 1000 );
764     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
765 
766     ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
767     ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
768 
769     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
770     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
771     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
772     ok( pfni->FileNameLength == 6, "len wrong\n" );
773     ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
774 
775     r = DeleteFileW( file );
776     ok( r == TRUE, "failed to delete file\n");
777 
778     r = RemoveDirectoryW( subdir );
779     ok( r == TRUE, "failed to remove directory\n");
780 
781     CloseHandle(hdir);
782 
783     r = RemoveDirectoryW( path );
784     ok( r == TRUE, "failed to remove directory\n");
785 }
786 
787 static void test_ffcn_directory_overlap(void)
788 {
789     HANDLE parent_watch, child_watch, parent_thread, child_thread;
790     char workdir[MAX_PATH], parentdir[MAX_PATH], childdir[MAX_PATH];
791     char tempfile[MAX_PATH];
792     DWORD threadId;
793     BOOL ret;
794 
795     /* Setup directory hierarchy */
796     ret = GetTempPathA(MAX_PATH, workdir);
797     ok((ret > 0) && (ret <= MAX_PATH),
798        "GetTempPathA error: %d\n", GetLastError());
799 
800     ret = GetTempFileNameA(workdir, "fcn", 0, tempfile);
801     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
802     ret = DeleteFileA(tempfile);
803     ok(ret, "DeleteFileA error: %d\n", GetLastError());
804 
805     lstrcpyA(parentdir, tempfile);
806     ret = CreateDirectoryA(parentdir, NULL);
807     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
808 
809     lstrcpyA(childdir, parentdir);
810     lstrcatA(childdir, "\\c");
811     ret = CreateDirectoryA(childdir, NULL);
812     ok(ret, "CreateDirectoryA error: %d\n", GetLastError());
813 
814 
815     /* When recursively watching overlapping directories, changes in child
816      * should trigger notifications for both child and parent */
817     parent_thread = StartNotificationThread(parentdir, TRUE,
818                                             FILE_NOTIFY_CHANGE_FILE_NAME);
819     child_thread = StartNotificationThread(childdir, TRUE,
820                                             FILE_NOTIFY_CHANGE_FILE_NAME);
821 
822     /* Create a file in child */
823     ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
824     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
825 
826     /* Both watches should trigger */
827     ret = FinishNotificationThread(parent_thread);
828     ok(ret, "Missed parent notification\n");
829     ret = FinishNotificationThread(child_thread);
830     ok(ret, "Missed child notification\n");
831 
832     ret = DeleteFileA(tempfile);
833     ok(ret, "DeleteFileA error: %d\n", GetLastError());
834 
835 
836     /* Removing a recursive parent watch should not affect child watches. Doing
837      * so used to crash wineserver. */
838     parent_watch = FindFirstChangeNotificationA(parentdir, TRUE,
839                                                 FILE_NOTIFY_CHANGE_FILE_NAME);
840     ok(parent_watch != INVALID_HANDLE_VALUE,
841        "FindFirstChangeNotification error: %d\n", GetLastError());
842     child_watch = FindFirstChangeNotificationA(childdir, TRUE,
843                                                FILE_NOTIFY_CHANGE_FILE_NAME);
844     ok(child_watch != INVALID_HANDLE_VALUE,
845        "FindFirstChangeNotification error: %d\n", GetLastError());
846 
847     ret = FindCloseChangeNotification(parent_watch);
848     ok(ret, "FindCloseChangeNotification error: %d\n", GetLastError());
849 
850     child_thread = CreateThread(NULL, 0, NotificationThread, child_watch, 0,
851                                 &threadId);
852     ok(child_thread != NULL, "CreateThread error: %d\n", GetLastError());
853 
854     /* Create a file in child */
855     ret = GetTempFileNameA(childdir, "fcn", 0, tempfile);
856     ok(ret, "GetTempFileNameA error: %d\n", GetLastError());
857 
858     /* Child watch should trigger */
859     ret = FinishNotificationThread(child_thread);
860     ok(ret, "Missed child notification\n");
861 
862     /* clean up */
863     ret = DeleteFileA(tempfile);
864     ok(ret, "DeleteFileA error: %d\n", GetLastError());
865 
866     ret = RemoveDirectoryA(childdir);
867     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
868 
869     ret = RemoveDirectoryA(parentdir);
870     ok(ret, "RemoveDirectoryA error: %d\n", GetLastError());
871 }
872 
873 START_TEST(change)
874 {
875     HMODULE hkernel32 = GetModuleHandle("kernel32");
876     pReadDirectoryChangesW = (fnReadDirectoryChangesW)
877         GetProcAddress(hkernel32, "ReadDirectoryChangesW");
878 
879     test_ffcnMultipleThreads();
880     /* The above function runs a test that must occur before FindCloseChangeNotification is run in the
881        current thread to preserve the emptiness of the wine user APC queue. To ensure this it should be
882        placed first. */
883     test_FindFirstChangeNotification();
884     test_ffcn();
885     test_readdirectorychanges();
886     test_readdirectorychanges_null();
887     test_readdirectorychanges_filedir();
888     test_ffcn_directory_overlap();
889 }
890 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.