xref: /reactos/dll/win32/kernel32/client/file/cnotify.c (revision 9393fc32)
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
BasepIoCompletion(IN PVOID ApcContext,IN PIO_STATUS_BLOCK IoStatusBlock,IN DWORD Reserved)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
BasepIoCompletionSimple(IN PVOID ApcContext,IN PIO_STATUS_BLOCK IoStatusBlock,IN DWORD Reserved)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
FindCloseChangeNotification(IN HANDLE hChangeHandle)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
FindFirstChangeNotificationA(IN LPCSTR lpPathName,IN BOOL bWatchSubtree,IN DWORD dwNotifyFilter)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
FindFirstChangeNotificationW(IN LPCWSTR lpPathName,IN BOOL bWatchSubtree,IN DWORD dwNotifyFilter)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
FindNextChangeNotification(IN HANDLE hChangeHandle)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
ReadDirectoryChangesW(IN HANDLE hDirectory,IN LPVOID lpBuffer OPTIONAL,IN DWORD nBufferLength,IN BOOL bWatchSubtree,IN DWORD dwNotifyFilter,OUT LPDWORD lpBytesReturned,IN LPOVERLAPPED lpOverlapped OPTIONAL,IN LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)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 */
380