1 /*
2  * File change notification tests
3  *
4  * Copyright 2006 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "ntdll_test.h"
22 
23 static NTSTATUS (WINAPI *pNtNotifyChangeDirectoryFile)(
24                           HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,
25                           PIO_STATUS_BLOCK,PVOID,ULONG,ULONG,BOOLEAN);
26 
27 static NTSTATUS (WINAPI *pNtCancelIoFile)(HANDLE,PIO_STATUS_BLOCK);
28 
29 
30 static void test_ntncdf(void)
31 {
32     NTSTATUS r;
33     HANDLE hdir, hEvent;
34     char buffer[0x1000];
35     DWORD fflags, filter = 0;
36     IO_STATUS_BLOCK iosb;
37     WCHAR path[MAX_PATH], subdir[MAX_PATH];
38     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
39     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
40     PFILE_NOTIFY_INFORMATION pfni;
41 
42     r = GetTempPathW( MAX_PATH, path );
43     ok( r != 0, "temp path failed\n");
44     if (!r)
45         return;
46 
47     lstrcatW( path, szBoo );
48     lstrcpyW( subdir, path );
49     lstrcatW( subdir, szHoo );
50 
51     RemoveDirectoryW( subdir );
52     RemoveDirectoryW( path );
53 
54     r = CreateDirectoryW(path, NULL);
55     ok( r == TRUE, "failed to create directory\n");
56 
57     r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
58     ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
59 
60     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
61     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
62                         OPEN_EXISTING, fflags, NULL);
63     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
64 
65     hEvent = CreateEventA( NULL, 0, 0, NULL );
66 
67     r = pNtNotifyChangeDirectoryFile(hdir,NULL,NULL,NULL,&iosb,NULL,0,0,0);
68     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
69 
70     r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,NULL,0,0,0);
71     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
72 
73     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
74     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
75     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
76     filter |= FILE_NOTIFY_CHANGE_SIZE;
77     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
78     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
79     filter |= FILE_NOTIFY_CHANGE_CREATION;
80     filter |= FILE_NOTIFY_CHANGE_SECURITY;
81 
82     U(iosb).Status = 1;
83     iosb.Information = 1;
84     r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,-1,0);
85     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
86 
87     ok( U(iosb).Status == 1, "information wrong\n");
88     ok( iosb.Information == 1, "information wrong\n");
89 
90     U(iosb).Status = 1;
91     iosb.Information = 0;
92     r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
93     ok(r==STATUS_PENDING, "should return status pending\n");
94 
95     r = WaitForSingleObject( hEvent, 100 );
96     ok( r == STATUS_TIMEOUT, "should timeout\n" );
97 
98     r = WaitForSingleObject( hdir, 100 );
99     ok( r == STATUS_TIMEOUT, "should timeout\n" );
100 
101     r = CreateDirectoryW( subdir, NULL );
102     ok( r == TRUE, "failed to create directory\n");
103 
104     r = WaitForSingleObject( hdir, 100 );
105     ok( r == STATUS_TIMEOUT, "should timeout\n" );
106 
107     r = WaitForSingleObject( hEvent, 100 );
108     ok( r == WAIT_OBJECT_0, "event should be ready\n" );
109 
110     ok( U(iosb).Status == STATUS_SUCCESS, "information wrong\n");
111     ok( iosb.Information == 0x12, "information wrong\n");
112 
113     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
114     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
115     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
116     ok( pfni->FileNameLength == 6, "len wrong\n" );
117     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
118 
119     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,0,0);
120     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
121 
122     r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,0,0);
123     ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
124 
125     filter = FILE_NOTIFY_CHANGE_SIZE;
126 
127     U(iosb).Status = 1;
128     iosb.Information = 1;
129     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
130     ok(r==STATUS_PENDING, "should status pending\n");
131 
132     ok( U(iosb).Status == 1, "information wrong\n");
133     ok( iosb.Information == 1, "information wrong\n");
134 
135     r = WaitForSingleObject( hdir, 0 );
136     ok( r == STATUS_TIMEOUT, "should timeout\n" );
137 
138     r = RemoveDirectoryW( subdir );
139     ok( r == TRUE, "failed to remove directory\n");
140 
141     r = WaitForSingleObject( hdir, 100 );
142     ok( r == WAIT_OBJECT_0, "should be ready\n" );
143 
144     r = WaitForSingleObject( hdir, 100 );
145     ok( r == WAIT_OBJECT_0, "should be ready\n" );
146 
147     ok( U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "information wrong\n");
148     ok( iosb.Information == 0, "information wrong\n");
149 
150     CloseHandle(hdir);
151     CloseHandle(hEvent);
152 
153     r = RemoveDirectoryW( path );
154     ok( r == TRUE, "failed to remove directory\n");
155 }
156 
157 
158 static void test_ntncdf_async(void)
159 {
160     NTSTATUS r;
161     HANDLE hdir, hEvent;
162     char buffer[0x1000];
163     DWORD fflags, filter = 0;
164     IO_STATUS_BLOCK iosb, iosb2;
165     WCHAR path[MAX_PATH], subdir[MAX_PATH];
166     static const WCHAR szBoo[] = { '\\','b','o','o',0 };
167     static const WCHAR szHoo[] = { '\\','h','o','o',0 };
168     PFILE_NOTIFY_INFORMATION pfni;
169 
170     r = GetTempPathW( MAX_PATH, path );
171     ok( r != 0, "temp path failed\n");
172     if (!r)
173         return;
174 
175     lstrcatW( path, szBoo );
176     lstrcpyW( subdir, path );
177     lstrcatW( subdir, szHoo );
178 
179     RemoveDirectoryW( subdir );
180     RemoveDirectoryW( path );
181 
182     r = CreateDirectoryW(path, NULL);
183     ok( r == TRUE, "failed to create directory\n");
184 
185     r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
186     ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
187 
188     fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
189     hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
190                         OPEN_EXISTING, fflags, NULL);
191     ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
192 
193     hEvent = CreateEventA( NULL, 0, 0, NULL );
194 
195     filter = FILE_NOTIFY_CHANGE_FILE_NAME;
196     filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
197     filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
198     filter |= FILE_NOTIFY_CHANGE_SIZE;
199     filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
200     filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
201     filter |= FILE_NOTIFY_CHANGE_CREATION;
202     filter |= FILE_NOTIFY_CHANGE_SECURITY;
203 
204 
205     U(iosb).Status   = 0x01234567;
206     iosb.Information = 0x12345678;
207     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
208     ok(r==STATUS_PENDING, "should status pending\n");
209     ok(U(iosb).Status == 0x01234567, "status set too soon\n");
210     ok(iosb.Information == 0x12345678, "info set too soon\n");
211 
212     r = CreateDirectoryW( subdir, NULL );
213     ok( r == TRUE, "failed to create directory\n");
214 
215     r = WaitForSingleObject( hdir, 100 );
216     ok( r == WAIT_OBJECT_0, "should be ready\n" );
217 
218     ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
219     ok(iosb.Information == 0x12, "info not set\n");
220 
221     pfni = (PFILE_NOTIFY_INFORMATION) buffer;
222     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
223     ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
224     ok( pfni->FileNameLength == 6, "len wrong\n" );
225     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
226 
227     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
228     ok(r==STATUS_PENDING, "should status pending\n");
229 
230     r = RemoveDirectoryW( subdir );
231     ok( r == TRUE, "failed to remove directory\n");
232 
233     r = WaitForSingleObject( hdir, 0 );
234     ok( r == WAIT_OBJECT_0, "should be ready\n" );
235 
236     ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
237     ok(iosb.Information == 0x12, "info not set\n");
238 
239     ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
240     ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
241     ok( pfni->FileNameLength == 6, "len wrong\n" );
242     ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
243 
244     /* check APCs */
245     U(iosb).Status = 0;
246     iosb.Information = 0;
247 
248     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
249     ok(r==STATUS_PENDING, "should status pending\n");
250 
251     r = CreateDirectoryW( subdir, NULL );
252     ok( r == TRUE, "failed to create directory\n");
253 
254     r = WaitForSingleObject( hdir, 0 );
255     ok( r == WAIT_OBJECT_0, "should be ready\n" );
256 
257     ok(U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "status not successful\n");
258     ok(iosb.Information == 0, "info not set\n");
259 
260     U(iosb).Status = 0;
261     iosb.Information = 0;
262 
263     r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
264     ok(r==STATUS_PENDING, "should status pending\n");
265 
266     r = RemoveDirectoryW( subdir );
267     ok( r == TRUE, "failed to remove directory\n");
268 
269     r = WaitForSingleObject( hEvent, 0 );
270     ok( r == WAIT_OBJECT_0, "should be ready\n" );
271 
272     ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
273     ok(iosb.Information == 0x12, "info not set\n");
274 
275 
276     U(iosb).Status   = 0x01234567;
277     iosb.Information = 0x12345678;
278     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
279     ok(r==STATUS_PENDING, "should status pending\n");
280 
281     U(iosb2).Status   = 0x01234567;
282     iosb2.Information = 0x12345678;
283     r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb2,buffer,sizeof buffer,filter,0);
284     ok(r==STATUS_PENDING, "should status pending\n");
285 
286     ok(U(iosb).Status == 0x01234567, "status set too soon\n");
287     ok(iosb.Information == 0x12345678, "info set too soon\n");
288 
289     r = pNtCancelIoFile(hdir, &iosb);
290     ok( r == STATUS_SUCCESS, "cancel failed\n");
291 
292     CloseHandle(hdir);
293 
294     ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n");
295     ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong %x\n",U(iosb2).Status);
296 
297     ok(iosb.Information == 0, "info wrong\n");
298     ok(iosb2.Information == 0, "info wrong\n");
299 
300     r = RemoveDirectoryW( path );
301     ok( r == TRUE, "failed to remove directory\n");
302 
303     CloseHandle(hEvent);
304 }
305 
306 START_TEST(change)
307 {
308     HMODULE hntdll = GetModuleHandleA("ntdll");
309     if (!hntdll)
310     {
311         win_skip("not running on NT, skipping test\n");
312         return;
313     }
314 
315     pNtNotifyChangeDirectoryFile = (void *)GetProcAddress(hntdll, "NtNotifyChangeDirectoryFile");
316     pNtCancelIoFile = (void *)GetProcAddress(hntdll, "NtCancelIoFile");
317 
318     if (!pNtNotifyChangeDirectoryFile || !pNtCancelIoFile)
319     {
320         win_skip("missing functions, skipping test\n");
321         return;
322     }
323 
324     test_ntncdf();
325     test_ntncdf_async();
326 }
327