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 */ 380