[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / cnotify.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/file/cnotify.c
5 * PURPOSE: Find functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * UPDATE HISTORY:
8 * Created 01/11/98
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <k32.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 CHAR staticchangebuff[sizeof(FILE_NOTIFY_INFORMATION) + 16];
20 IO_STATUS_BLOCK staticIoStatusBlock;
21
22 /* PRIVATE FUNCTIONS **********************************************************/
23
24 VOID
25 WINAPI
26 BasepIoCompletion(IN PVOID ApcContext,
27 IN PIO_STATUS_BLOCK IoStatusBlock,
28 IN DWORD Reserved)
29 {
30 PBASEP_ACTCTX_BLOCK ActivationBlock = ApcContext;
31 LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
32 DWORD BytesTransfered, Result;
33 RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
34 PVOID ActivationContext = NULL;
35
36 /* Setup the activation frame */
37 RtlZeroMemory(&ActCtx, sizeof(ActCtx));
38 ActCtx.Size = sizeof(ActCtx);
39 ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
40
41 /* Check if the routine returned an error */
42 if (NT_ERROR(IoStatusBlock->Status))
43 {
44 /* Convert the error code and don't copy anything */
45 Result = RtlNtStatusToDosError(IoStatusBlock->Status);
46 BytesTransfered = 0;
47 }
48 else
49 {
50 /* Set success code and copy the bytes transferred */
51 Result = ERROR_SUCCESS;
52 BytesTransfered = IoStatusBlock->Information;
53 }
54
55 /* Read context and routine out from the activation block */
56 ActivationContext = ActivationBlock->ActivationContext;
57 CompletionRoutine = ActivationBlock->CompletionRoutine;
58
59 /* Check if the block should be freed */
60 if (!(ActivationBlock->Flags & 1))
61 {
62 /* Free it */
63 BasepFreeActivationContextActivationBlock(ActivationBlock);
64 }
65
66 /* Activate the context, call the routine, and then deactivate the context */
67 RtlActivateActivationContextUnsafeFast(&ActCtx, ActivationContext);
68 CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock);
69 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
70 }
71
72 VOID
73 WINAPI
74 BasepIoCompletionSimple(IN PVOID ApcContext,
75 IN PIO_STATUS_BLOCK IoStatusBlock,
76 IN DWORD Reserved)
77 {
78 LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine = ApcContext;
79 DWORD Result, BytesTransfered;
80
81 /* Check if the routine returned an error */
82 if (NT_ERROR(IoStatusBlock->Status))
83 {
84 /* Convert the error code and don't copy anything */
85 Result = RtlNtStatusToDosError(IoStatusBlock->Status);
86 BytesTransfered = 0;
87 }
88 else
89 {
90 /* Set success code and copy the bytes transferred */
91 Result = ERROR_SUCCESS;
92 BytesTransfered = IoStatusBlock->Information;
93 }
94
95 /* Call the callback routine */
96 CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock);
97 }
98
99 /* PUBLIC FUNCTIONS ***********************************************************/
100
101 /*
102 * @implemented
103 */
104 BOOL
105 WINAPI
106 FindCloseChangeNotification(IN HANDLE hChangeHandle)
107 {
108 /* Just close the handle */
109 return CloseHandle(hChangeHandle);
110 }
111
112 /*
113 * @implemented
114 */
115 HANDLE
116 WINAPI
117 FindFirstChangeNotificationA(IN LPCSTR lpPathName,
118 IN BOOL bWatchSubtree,
119 IN DWORD dwNotifyFilter)
120 {
121 /* Call the W(ide) function */
122 ConvertWin32AnsiChangeApiToUnicodeApi(FindFirstChangeNotification,
123 lpPathName,
124 bWatchSubtree,
125 dwNotifyFilter);
126 }
127
128 /*
129 * @implemented
130 */
131 HANDLE
132 WINAPI
133 FindFirstChangeNotificationW(IN LPCWSTR lpPathName,
134 IN BOOL bWatchSubtree,
135 IN DWORD dwNotifyFilter)
136 {
137 NTSTATUS Status;
138 UNICODE_STRING NtPathU;
139 OBJECT_ATTRIBUTES ObjectAttributes;
140 HANDLE hDir;
141 RTL_RELATIVE_NAME_U RelativeName;
142 PWCHAR PathBuffer;
143 IO_STATUS_BLOCK IoStatusBlock;
144
145 /* Convert to NT path and get the relative name too */
146 if (!RtlDosPathNameToNtPathName_U(lpPathName,
147 &NtPathU,
148 NULL,
149 &RelativeName))
150 {
151 /* Bail out if the path name makes no sense */
152 SetLastError(ERROR_PATH_NOT_FOUND);
153 return INVALID_HANDLE_VALUE;
154 }
155
156 /* Save the path buffer in case we free it later */
157 PathBuffer = NtPathU.Buffer;
158
159 /* If we have a relative name... */
160 if (RelativeName.RelativeName.Length)
161 {
162 /* Do a relative open with only the relative path set */
163 NtPathU = RelativeName.RelativeName;
164 }
165 else
166 {
167 /* Do a full path open with no containing directory */
168 RelativeName.ContainingDirectory = NULL;
169 }
170
171 /* Now open the directory name that was passed in */
172 InitializeObjectAttributes(&ObjectAttributes,
173 &NtPathU,
174 OBJ_CASE_INSENSITIVE,
175 RelativeName.ContainingDirectory,
176 NULL);
177 Status = NtOpenFile(&hDir,
178 SYNCHRONIZE | FILE_LIST_DIRECTORY,
179 &ObjectAttributes,
180 &IoStatusBlock,
181 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
182 FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
183
184 /* Release our buffer and relative name structure */
185 RtlReleaseRelativeName(&RelativeName);
186 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
187
188 /* Check if the open failed */
189 if (!NT_SUCCESS(Status))
190 {
191 /* Bail out in that case */
192 BaseSetLastNTError(Status);
193 return INVALID_HANDLE_VALUE;
194 }
195
196 /* Now setup the notification on the directory as requested */
197 Status = NtNotifyChangeDirectoryFile(hDir,
198 NULL,
199 NULL,
200 NULL,
201 &staticIoStatusBlock,
202 staticchangebuff,
203 sizeof(staticchangebuff),
204 dwNotifyFilter,
205 (BOOLEAN)bWatchSubtree);
206 if (!NT_SUCCESS(Status))
207 {
208 /* We failed, close the handle and convert the error */
209 NtClose(hDir);
210 BaseSetLastNTError(Status);
211 hDir = INVALID_HANDLE_VALUE;
212 }
213
214 /* Return the directory handle on success, or invalid handle otherwise */
215 return hDir;
216 }
217
218 /*
219 * @implemented
220 */
221 BOOL
222 WINAPI
223 FindNextChangeNotification(IN HANDLE hChangeHandle)
224 {
225 NTSTATUS Status;
226
227 /* Just call the native API directly, dealing with the non-optional parameters */
228 Status = NtNotifyChangeDirectoryFile(hChangeHandle,
229 NULL,
230 NULL,
231 NULL,
232 &staticIoStatusBlock,
233 staticchangebuff,
234 sizeof(staticchangebuff),
235 FILE_NOTIFY_CHANGE_SECURITY,
236 TRUE);
237 if (!NT_SUCCESS(Status))
238 {
239 /* Convert the error code and fail */
240 BaseSetLastNTError(Status);
241 return FALSE;
242 }
243
244 /* All went well */
245 return TRUE;
246 }
247
248 /*
249 * @implemented
250 */
251 BOOL
252 WINAPI
253 ReadDirectoryChangesW(IN HANDLE hDirectory,
254 IN LPVOID lpBuffer OPTIONAL,
255 IN DWORD nBufferLength,
256 IN BOOL bWatchSubtree,
257 IN DWORD dwNotifyFilter,
258 OUT LPDWORD lpBytesReturned,
259 IN LPOVERLAPPED lpOverlapped OPTIONAL,
260 IN LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
261 {
262
263 NTSTATUS Status;
264 HANDLE EventHandle;
265 PVOID ApcContext;
266 PIO_APC_ROUTINE ApcRoutine;
267 PBASEP_ACTCTX_BLOCK ActivationContext = NULL;
268 BOOL Result = TRUE;
269 IO_STATUS_BLOCK IoStatusBlock;
270
271 /* Is the caller doing this synchronously? */
272 if (!lpOverlapped)
273 {
274 /* Great, just pass in the parameters */
275 Status = NtNotifyChangeDirectoryFile(hDirectory,
276 NULL,
277 NULL,
278 NULL,
279 &IoStatusBlock,
280 lpBuffer,
281 nBufferLength,
282 dwNotifyFilter,
283 bWatchSubtree);
284 if (Status == STATUS_PENDING)
285 {
286 /* Wait for completion since we are synchronous */
287 Status = NtWaitForSingleObject(hDirectory, FALSE, NULL);
288 if (!NT_SUCCESS(Status))
289 {
290 /* The wait failed, bail out */
291 BaseSetLastNTError(Status);
292 return FALSE;
293 }
294
295 /* Retrieve the final status code */
296 Status = IoStatusBlock.Status;
297 }
298
299 /* Did the operation succeed? */
300 if (NT_SUCCESS(Status))
301 {
302 /* Return the bytes transferd and success */
303 *lpBytesReturned = IoStatusBlock.Information;
304 return Result;
305 }
306
307 /* Convert error code and return failure */
308 BaseSetLastNTError(Status);
309 return FALSE;
310 }
311
312 /* Does the caller want an APC callback? */
313 if (lpCompletionRoutine)
314 {
315 /* Don't use an event in this case */
316 EventHandle = NULL;
317
318 /* Allocate a Fusion/SxS activation context for the callback routine */
319 Status = BasepAllocateActivationContextActivationBlock(1 | 2,
320 lpCompletionRoutine,
321 lpOverlapped,
322 &ActivationContext);
323 if (!NT_SUCCESS(Status))
324 {
325 /* This failed, so abandon the call */
326 BaseSetLastNTError(Status);
327 return FALSE;
328 }
329
330 /* Use the SxS context as the APC context */
331 ApcContext = ActivationContext;
332 if (ActivationContext)
333 {
334 /* And use a special stub routine that deals with activation */
335 ApcRoutine = BasepIoCompletion;
336 }
337 else
338 {
339 /* If there was no context, however, use the simple stub routine */
340 ApcContext = lpCompletionRoutine;
341 ApcRoutine = BasepIoCompletionSimple;
342 }
343 }
344 else
345 {
346 /* Use the even with no APC routine */
347 EventHandle = lpOverlapped->hEvent;
348 ApcRoutine = 0;
349
350 /* LPOVERLAPPED should be ignored if event is ORed with 1 */
351 ApcContext = (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped;
352 }
353
354 /* Set the initial status to pending and call the native API */
355 lpOverlapped->Internal = STATUS_PENDING;
356 Status = NtNotifyChangeDirectoryFile(hDirectory,
357 EventHandle,
358 ApcRoutine,
359 ApcContext,
360 (PIO_STATUS_BLOCK)lpOverlapped,
361 lpBuffer,
362 nBufferLength,
363 dwNotifyFilter,
364 (BOOLEAN)bWatchSubtree);
365 if (NT_ERROR(Status))
366 {
367 /* Normally we cleanup the context in the completion routine, but we failed */
368 if (ActivationContext) BasepFreeActivationContextActivationBlock(ActivationContext);
369
370 /* Convert the error and fail */
371 BaseSetLastNTError(Status);
372 return FALSE;
373 }
374
375 /* Return success */
376 return Result;
377 }
378
379 /* EOF */