1 //////////////////////////////////////////////////////////////////// 2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine 3 // All rights reserved 4 // This file was released under the GPLv2 on June 2015. 5 //////////////////////////////////////////////////////////////////// 6 /************************************************************************* 7 * 8 * File: DirCntrl.cpp 9 * 10 * Module: UDF File System Driver (Kernel mode execution only) 11 * 12 * Description: 13 * Contains code to handle the "directory control" dispatch entry point. 14 * 15 *************************************************************************/ 16 17 #include "udffs.h" 18 19 // define the file specific bug-check id 20 #define UDF_BUG_CHECK_ID UDF_FILE_DIR_CONTROL 21 22 /* 23 // Local support routine(s): 24 */ 25 26 #define UDF_FNM_FLAG_CAN_BE_8D3 0x01 27 #define UDF_FNM_FLAG_IGNORE_CASE 0x02 28 #define UDF_FNM_FLAG_CONTAINS_WC 0x04 29 30 NTSTATUS UDFFindNextMatch( 31 IN PVCB Vcb, 32 IN PDIR_INDEX_HDR hDirIndex, 33 IN PLONG CurrentNumber, // Must be modified 34 IN PUNICODE_STRING PtrSearchPattern, 35 IN UCHAR FNM_Flags, 36 IN PHASH_ENTRY hashes, 37 OUT PDIR_INDEX_ITEM* _DirNdx); 38 39 /************************************************************************* 40 * 41 * Function: UDFDirControl() 42 * 43 * Description: 44 * The I/O Manager will invoke this routine to handle a directory control 45 * request 46 * 47 * Expected Interrupt Level (for execution) : 48 * 49 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution 50 * to be deferred to a worker thread context) 51 * 52 * Return Value: STATUS_SUCCESS/Error 53 * 54 *************************************************************************/ 55 NTSTATUS 56 NTAPI 57 UDFDirControl( 58 PDEVICE_OBJECT DeviceObject, // the logical volume device object 59 PIRP Irp // I/O Request Packet 60 ) 61 { 62 NTSTATUS RC = STATUS_SUCCESS; 63 PtrUDFIrpContext PtrIrpContext = NULL; 64 BOOLEAN AreWeTopLevel = FALSE; 65 66 TmPrint(("UDFDirControl: \n")); 67 68 FsRtlEnterFileSystem(); 69 ASSERT(DeviceObject); 70 ASSERT(Irp); 71 72 // set the top level context 73 AreWeTopLevel = UDFIsIrpTopLevel(Irp); 74 ASSERT(!UDFIsFSDevObj(DeviceObject)); 75 76 _SEH2_TRY { 77 78 // get an IRP context structure and issue the request 79 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); 80 if(PtrIrpContext) { 81 RC = UDFCommonDirControl(PtrIrpContext, Irp); 82 } else { 83 RC = STATUS_INSUFFICIENT_RESOURCES; 84 Irp->IoStatus.Status = RC; 85 Irp->IoStatus.Information = 0; 86 // complete the IRP 87 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 88 } 89 90 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { 91 92 RC = UDFExceptionHandler(PtrIrpContext, Irp); 93 94 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); 95 } _SEH2_END; 96 97 if (AreWeTopLevel) { 98 IoSetTopLevelIrp(NULL); 99 } 100 101 FsRtlExitFileSystem(); 102 103 return(RC); 104 } // end UDFDirControl() 105 106 107 108 /************************************************************************* 109 * 110 * Function: UDFCommonDirControl() 111 * 112 * Description: 113 * The actual work is performed here. This routine may be invoked in one' 114 * of the two possible contexts: 115 * (a) in the context of a system worker thread 116 * (b) in the context of the original caller 117 * 118 * Expected Interrupt Level (for execution) : 119 * 120 * IRQL_PASSIVE_LEVEL 121 * 122 * Return Value: STATUS_SUCCESS/Error 123 * 124 *************************************************************************/ 125 NTSTATUS 126 NTAPI 127 UDFCommonDirControl( 128 PtrUDFIrpContext PtrIrpContext, 129 PIRP Irp 130 ) 131 { 132 NTSTATUS RC = STATUS_SUCCESS; 133 PIO_STACK_LOCATION IrpSp = NULL; 134 PFILE_OBJECT FileObject = NULL; 135 PtrUDFFCB Fcb = NULL; 136 PtrUDFCCB Ccb = NULL; 137 PVCB Vcb = NULL; 138 BOOLEAN AcquiredVcb = FALSE; 139 140 TmPrint(("UDFCommonDirControl: \n")); 141 // BrutePoint(); 142 143 _SEH2_TRY { 144 // First, get a pointer to the current I/O stack location 145 IrpSp = IoGetCurrentIrpStackLocation(Irp); 146 ASSERT(IrpSp); 147 148 FileObject = IrpSp->FileObject; 149 ASSERT(FileObject); 150 151 // Get the FCB and CCB pointers 152 Ccb = (PtrUDFCCB)(FileObject->FsContext2); 153 ASSERT(Ccb); 154 Fcb = Ccb->Fcb; 155 ASSERT(Fcb); 156 157 Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension); 158 ASSERT(Vcb); 159 ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); 160 // Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 161 162 UDFFlushTryBreak(Vcb); 163 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); 164 AcquiredVcb = TRUE; 165 // Get some of the parameters supplied to us 166 switch (IrpSp->MinorFunction) { 167 case IRP_MN_QUERY_DIRECTORY: 168 RC = UDFQueryDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb); 169 break; 170 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: 171 RC = UDFNotifyChangeDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb); 172 break; 173 default: 174 // This should not happen. 175 RC = STATUS_INVALID_DEVICE_REQUEST; 176 Irp->IoStatus.Status = RC; 177 Irp->IoStatus.Information = 0; 178 179 // Free up the Irp Context 180 UDFReleaseIrpContext(PtrIrpContext); 181 182 // complete the IRP 183 IoCompleteRequest(Irp, IO_NO_INCREMENT); 184 break; 185 } 186 187 //try_exit: NOTHING; 188 189 } _SEH2_FINALLY { 190 191 if(AcquiredVcb) { 192 UDFReleaseResource(&(Vcb->VCBResource)); 193 AcquiredVcb = FALSE; 194 } 195 } _SEH2_END; 196 return(RC); 197 } // end UDFCommonDirControl() 198 199 200 /************************************************************************* 201 * 202 * Function: UDFQueryDirectory() 203 * 204 * Description: 205 * Query directory request. 206 * 207 * Expected Interrupt Level (for execution) : 208 * 209 * IRQL_PASSIVE_LEVEL 210 * 211 * Return Value: STATUS_SUCCESS/Error 212 * 213 *************************************************************************/ 214 NTSTATUS 215 NTAPI 216 UDFQueryDirectory( 217 PtrUDFIrpContext PtrIrpContext, 218 PIRP Irp, 219 PIO_STACK_LOCATION IrpSp, 220 PFILE_OBJECT FileObject, 221 PtrUDFFCB Fcb, 222 PtrUDFCCB Ccb 223 ) 224 { 225 NTSTATUS RC = STATUS_SUCCESS; 226 BOOLEAN PostRequest = FALSE; 227 PtrUDFNTRequiredFCB NtReqFcb = NULL; 228 BOOLEAN CanWait = FALSE; 229 PVCB Vcb = NULL; 230 BOOLEAN AcquiredFCB = FALSE; 231 unsigned long BufferLength = 0; 232 UNICODE_STRING SearchPattern; 233 PUNICODE_STRING PtrSearchPattern; 234 FILE_INFORMATION_CLASS FileInformationClass; 235 BOOLEAN ReturnSingleEntry = FALSE; 236 PUCHAR Buffer = NULL; 237 BOOLEAN FirstTimeQuery = FALSE; 238 LONG NextMatch; 239 LONG PrevMatch = -1; 240 ULONG CurrentOffset; 241 ULONG BaseLength; 242 ULONG FileNameBytes; 243 ULONG Information = 0; 244 ULONG LastOffset = 0; 245 BOOLEAN AtLeastOneFound = FALSE; 246 PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; 247 PUDF_FILE_INFO DirFileInfo = NULL; 248 PDIR_INDEX_HDR hDirIndex = NULL; 249 PFILE_BOTH_DIR_INFORMATION DirInformation = NULL; // Returned from udf_info module 250 PFILE_BOTH_DIR_INFORMATION BothDirInformation = NULL; // Pointer in callers buffer 251 PFILE_NAMES_INFORMATION NamesInfo; 252 ULONG BytesRemainingInBuffer; 253 UCHAR FNM_Flags = 0; 254 PHASH_ENTRY cur_hashes = NULL; 255 PDIR_INDEX_ITEM DirNdx; 256 // do some pre-init... 257 SearchPattern.Buffer = NULL; 258 259 UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext)); 260 261 #define CanBe8dot3 (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3) 262 #define IgnoreCase (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE) 263 #define ContainsWC (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC) 264 265 _SEH2_TRY 266 { 267 268 // Validate the sent-in FCB 269 if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { 270 // We will only allow notify requests on directories. 271 try_return(RC = STATUS_INVALID_PARAMETER); 272 } 273 274 // Obtain the callers parameters 275 NtReqFcb = Fcb->NTRequiredFCB; 276 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; 277 Vcb = Fcb->Vcb; 278 //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 279 FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? 0 : UDF_FNM_FLAG_IGNORE_CASE; 280 DirFileInfo = Fcb->FileInfo; 281 BufferLength = pStackLocation->Parameters.QueryDirectory.Length; 282 283 // If the caller does not want to block, it would be easier to 284 // simply post the request now. 285 if (!CanWait) { 286 PostRequest = TRUE; 287 try_return(RC = STATUS_PENDING); 288 } 289 290 // Continue obtaining the callers parameters... 291 if(IgnoreCase && pStackLocation->Parameters.QueryDirectory.FileName) { 292 PtrSearchPattern = &SearchPattern; 293 if(!NT_SUCCESS(RC = RtlUpcaseUnicodeString(PtrSearchPattern, (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName), TRUE))) 294 try_return(RC); 295 } else { 296 PtrSearchPattern = (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName); 297 } 298 FileInformationClass = pStackLocation->Parameters.QueryDirectory.FileInformationClass; 299 300 // Calculate baselength (without name) for each InfoClass 301 switch (FileInformationClass) { 302 303 case FileDirectoryInformation: 304 BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] ); 305 break; 306 case FileFullDirectoryInformation: 307 BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] ); 308 break; 309 case FileNamesInformation: 310 BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); 311 break; 312 case FileBothDirectoryInformation: 313 BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); 314 break; 315 default: 316 try_return(RC = STATUS_INVALID_INFO_CLASS); 317 } 318 319 // Some additional arguments that affect the FSD behavior 320 ReturnSingleEntry = (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) ? TRUE : FALSE; 321 322 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 323 UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE); 324 AcquiredFCB = TRUE; 325 326 // We must determine the buffer pointer to be used. Since this 327 // routine could either be invoked directly in the context of the 328 // calling thread, or in the context of a worker thread, here is 329 // a general way of determining what we should use. 330 if(Irp->MdlAddress) { 331 Buffer = (PUCHAR) MmGetSystemAddressForMdlSafer(Irp->MdlAddress); 332 if(!Buffer) 333 try_return(RC = STATUS_INSUFFICIENT_RESOURCES); 334 } else { 335 Buffer = (PUCHAR) Irp->UserBuffer; 336 if(!Buffer) 337 try_return(RC = STATUS_INVALID_USER_BUFFER); 338 } 339 340 // The method of determining where to look from and what to look for is 341 // unfortunately extremely confusing. However, here is a methodology 342 // we broadly adopt: 343 // (a) We have to maintain a search buffer per CCB structure. 344 // (b) This search buffer is initialized the very first time 345 // a query directory operation is performed using the file object. 346 // (For the UDF FSD, the search buffer is stored in the 347 // DirectorySearchPattern field) 348 // However, the caller still has the option of "overriding" this stored 349 // search pattern by supplying a new one in a query directory operation. 350 if(PtrSearchPattern && 351 PtrSearchPattern->Buffer && 352 !(PtrSearchPattern->Buffer[PtrSearchPattern->Length/sizeof(WCHAR) - 1])) { 353 PtrSearchPattern->Length -= sizeof(WCHAR); 354 } 355 356 if(IrpSp->Flags & SL_INDEX_SPECIFIED) { 357 // Good idea from M$: we should continue search from NEXT item 358 // when FileIndex specified... 359 // Strange idea from M$: we should do it with EMPTY pattern... 360 PtrSearchPattern = NULL; 361 Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; 362 } else if(PtrSearchPattern && 363 PtrSearchPattern->Buffer && 364 !UDFIsMatchAllMask(PtrSearchPattern, NULL) ) { 365 366 Ccb->CCBFlags &= ~(UDF_CCB_MATCH_ALL | 367 UDF_CCB_WILDCARD_PRESENT | 368 UDF_CCB_CAN_BE_8_DOT_3); 369 // Once we have validated the search pattern, we must 370 // check whether we need to store this search pattern in 371 // the CCB. 372 if(Ccb->DirectorySearchPattern) { 373 MyFreePool__(Ccb->DirectorySearchPattern->Buffer); 374 MyFreePool__(Ccb->DirectorySearchPattern); 375 Ccb->DirectorySearchPattern = NULL; 376 } 377 // This must be the very first query request. 378 FirstTimeQuery = TRUE; 379 380 // Now, allocate enough memory to contain the caller 381 // supplied search pattern and fill in the DirectorySearchPattern 382 // field in the CCB 383 Ccb->DirectorySearchPattern = (PUNICODE_STRING)MyAllocatePool__(NonPagedPool,sizeof(UNICODE_STRING)); 384 if(!(Ccb->DirectorySearchPattern)) { 385 try_return(RC = STATUS_INSUFFICIENT_RESOURCES); 386 } 387 Ccb->DirectorySearchPattern->Length = PtrSearchPattern->Length; 388 Ccb->DirectorySearchPattern->MaximumLength = PtrSearchPattern->MaximumLength; 389 Ccb->DirectorySearchPattern->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool,PtrSearchPattern->MaximumLength); 390 if(!(Ccb->DirectorySearchPattern->Buffer)) { 391 try_return(RC = STATUS_INSUFFICIENT_RESOURCES); 392 } 393 RtlCopyMemory(Ccb->DirectorySearchPattern->Buffer,PtrSearchPattern->Buffer, 394 PtrSearchPattern->MaximumLength); 395 if(FsRtlDoesNameContainWildCards(PtrSearchPattern)) { 396 Ccb->CCBFlags |= UDF_CCB_WILDCARD_PRESENT; 397 } else { 398 UDFBuildHashEntry(Vcb, PtrSearchPattern, cur_hashes = &(Ccb->hashes), HASH_POSIX | HASH_ULFN); 399 } 400 if(UDFCanNameBeA8dot3(PtrSearchPattern)) 401 Ccb->CCBFlags |= UDF_CCB_CAN_BE_8_DOT_3; 402 403 } else if(!Ccb->DirectorySearchPattern && 404 !(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) ) { 405 406 // If the filename is not specified or is a single '*' then we will 407 // match all names. 408 FirstTimeQuery = TRUE; 409 PtrSearchPattern = NULL; 410 Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; 411 412 } else { 413 // The caller has not supplied any search pattern that we are 414 // forced to use. However, the caller had previously supplied 415 // a pattern (or we must have invented one) and we will use it. 416 // This is definitely not the first query operation on this 417 // directory using this particular file object. 418 if(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) { 419 PtrSearchPattern = NULL; 420 /* if(Ccb->CurrentIndex) 421 Ccb->CurrentIndex++;*/ 422 } else { 423 PtrSearchPattern = Ccb->DirectorySearchPattern; 424 if(!(Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT)) { 425 cur_hashes = &(Ccb->hashes); 426 } 427 } 428 } 429 430 if(IrpSp->Flags & SL_INDEX_SPECIFIED) { 431 // Caller has told us wherefrom to begin. 432 // We may need to round this to an appropriate directory entry 433 // entry alignment value. 434 NextMatch = pStackLocation->Parameters.QueryDirectory.FileIndex + 1; 435 } else if(IrpSp->Flags & SL_RESTART_SCAN) { 436 NextMatch = 0; 437 } else { 438 // Get the starting offset from the CCB. 439 // Remember to update this value on our way out from this function. 440 // But, do not update the CCB CurrentByteOffset field if our reach 441 // the end of the directory (or get an error reading the directory) 442 // while performing the search. 443 NextMatch = Ccb->CurrentIndex + 1; // Last good index 444 } 445 446 FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT) ? UDF_FNM_FLAG_CONTAINS_WC : 0; 447 // this is used only when mask is supplied 448 FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CAN_BE_8_DOT_3) ? UDF_FNM_FLAG_CAN_BE_8D3 : 0; 449 450 // This is an additional verifying 451 if(!UDFIsADirectory(DirFileInfo)) { 452 try_return(RC = STATUS_INVALID_PARAMETER); 453 } 454 455 hDirIndex = DirFileInfo->Dloc->DirIndex; 456 if(!hDirIndex) { 457 try_return(RC = STATUS_INVALID_PARAMETER); 458 } 459 460 RC = STATUS_SUCCESS; 461 // Allocate buffer enough to save both DirInformation and FileName 462 DirInformation = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, 463 sizeof(FILE_BOTH_DIR_INFORMATION)+((ULONG)UDF_NAME_LEN*sizeof(WCHAR)) ); 464 if(!DirInformation) { 465 try_return(RC = STATUS_INSUFFICIENT_RESOURCES); 466 } 467 CurrentOffset=0; 468 BytesRemainingInBuffer = pStackLocation->Parameters.QueryDirectory.Length; 469 RtlZeroMemory(Buffer,BytesRemainingInBuffer); 470 471 if((!FirstTimeQuery) && !UDFDirIndex(hDirIndex, (uint_di)NextMatch) ) { 472 try_return( RC = STATUS_NO_MORE_FILES); 473 } 474 475 // One final note though: 476 // If we do not find a directory entry OR while searching we reach the 477 // end of the directory, then the return code should be set as follows: 478 479 // (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE 480 // and we did find at least one match), then return STATUS_SUCCESS 481 // (b) If no entry is being returned then: 482 // (i) If this is the first query i.e. FirstTimeQuery is TRUE 483 // then return STATUS_NO_SUCH_FILE 484 // (ii) Otherwise, return STATUS_NO_MORE_FILES 485 486 while(TRUE) { 487 // If the user had requested only a single match and we have 488 // returned that, then we stop at this point. 489 if(ReturnSingleEntry && AtLeastOneFound) { 490 try_return(RC); 491 } 492 // We call UDFFindNextMatch to look down the next matching dirent. 493 RC = UDFFindNextMatch(Vcb, hDirIndex,&NextMatch,PtrSearchPattern, FNM_Flags, cur_hashes, &DirNdx); 494 // If we didn't receive next match, then we are at the end of the 495 // directory. If we have returned any files, we exit with 496 // success, otherwise we return STATUS_NO_MORE_FILES. 497 if(!NT_SUCCESS(RC)) { 498 RC = AtLeastOneFound ? STATUS_SUCCESS : 499 (FirstTimeQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES); 500 try_return(RC); 501 } 502 // We found at least one matching file entry 503 AtLeastOneFound = TRUE; 504 if(!NT_SUCCESS(RC = UDFFileDirInfoToNT(Vcb, DirNdx, DirInformation))) { 505 // this happends when we can't allocate tmp buffers 506 try_return(RC); 507 } 508 DirInformation->FileIndex = NextMatch; 509 FileNameBytes = DirInformation->FileNameLength; 510 511 if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { 512 // We haven't successfully transfered current data & 513 // later NextMatch will be incremented. Thus we should 514 // prevent loosing information in such a way: 515 if(NextMatch) NextMatch --; 516 // If this won't fit and we have returned a previous entry then just 517 // return STATUS_SUCCESS. Otherwise 518 // use a status code of STATUS_BUFFER_OVERFLOW. 519 if(CurrentOffset) { 520 try_return(RC = STATUS_SUCCESS); 521 } 522 // strange policy... 523 ReturnSingleEntry = TRUE; 524 FileNameBytes = BaseLength + FileNameBytes - BytesRemainingInBuffer; 525 RC = STATUS_BUFFER_OVERFLOW; 526 } 527 // Now we have an entry to return to our caller. 528 // We'll case on the type of information requested and fill up 529 // the user buffer if everything fits. 530 switch (FileInformationClass) { 531 532 case FileBothDirectoryInformation: 533 case FileFullDirectoryInformation: 534 case FileDirectoryInformation: 535 536 BothDirInformation = (PFILE_BOTH_DIR_INFORMATION)(Buffer + CurrentOffset); 537 RtlCopyMemory(BothDirInformation,DirInformation,BaseLength); 538 BothDirInformation->FileIndex = NextMatch; 539 BothDirInformation->FileNameLength = FileNameBytes; 540 break; 541 542 case FileNamesInformation: 543 544 NamesInfo = (PFILE_NAMES_INFORMATION)(Buffer + CurrentOffset); 545 NamesInfo->FileIndex = NextMatch; 546 NamesInfo->FileNameLength = FileNameBytes; 547 break; 548 549 default: 550 break; 551 } 552 if (FileNameBytes) { 553 // This is a Unicode name, we can copy the bytes directly. 554 RtlCopyMemory( (PVOID)(Buffer + CurrentOffset + BaseLength), 555 DirInformation->FileName, FileNameBytes ); 556 } 557 558 Information = CurrentOffset + BaseLength + FileNameBytes; 559 560 // ((..._INFORMATION)(PointerToPreviousEntryInBuffer))->NextEntryOffset = CurrentOffset - LastOffset; 561 *((PULONG)(Buffer+LastOffset)) = CurrentOffset - LastOffset; 562 // Set up our variables for the next dirent. 563 FirstTimeQuery = FALSE; 564 565 LastOffset = CurrentOffset; 566 PrevMatch = NextMatch; 567 NextMatch++; 568 CurrentOffset = UDFQuadAlign(Information); 569 BytesRemainingInBuffer = BufferLength - CurrentOffset; 570 } 571 572 try_exit: NOTHING; 573 574 575 } _SEH2_FINALLY { 576 577 if (PostRequest) { 578 579 if (AcquiredFCB) { 580 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 581 UDFReleaseResource(&(NtReqFcb->MainResource)); 582 } 583 // Map the users buffer and then post the request. 584 RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, BufferLength); 585 ASSERT(NT_SUCCESS(RC)); 586 587 RC = UDFPostRequest(PtrIrpContext, Irp); 588 589 } else { 590 #ifdef UDF_DBG 591 if(!NT_SUCCESS(RC)) { 592 UDFPrint((" Not found\n")); 593 } 594 #endif // UDF_DBG 595 // Remember to update the CurrentByteOffset field in the CCB if required. 596 if(Ccb) Ccb->CurrentIndex = PrevMatch; 597 598 if (AcquiredFCB) { 599 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 600 UDFReleaseResource(&(NtReqFcb->MainResource)); 601 } 602 if (!_SEH2_AbnormalTermination()) { 603 // complete the IRP 604 Irp->IoStatus.Status = RC; 605 Irp->IoStatus.Information = Information; 606 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 607 // Free up the Irp Context 608 UDFReleaseIrpContext(PtrIrpContext); 609 } 610 } 611 612 if(SearchPattern.Buffer) RtlFreeUnicodeString(&SearchPattern); 613 if(DirInformation) MyFreePool__(DirInformation); 614 } _SEH2_END; 615 616 return(RC); 617 } // end UDFQueryDirectory() 618 619 /* 620 Return: STATUS_NO_SUCH_FILE if no more files found 621 */ 622 NTSTATUS 623 UDFFindNextMatch( 624 IN PVCB Vcb, 625 IN PDIR_INDEX_HDR hDirIndex, 626 IN PLONG CurrentNumber, // Must be modified in case, when we found next match 627 IN PUNICODE_STRING PtrSearchPattern, 628 IN UCHAR FNM_Flags, 629 IN PHASH_ENTRY hashes, 630 OUT PDIR_INDEX_ITEM* _DirNdx 631 ) 632 { 633 LONG EntryNumber = (*CurrentNumber); 634 PDIR_INDEX_ITEM DirNdx; 635 636 #define CanBe8dot3 (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3) 637 #define IgnoreCase (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE) 638 #define ContainsWC (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC) 639 640 for(;(DirNdx = UDFDirIndex(hDirIndex, EntryNumber));EntryNumber++) { 641 if(!DirNdx->FName.Buffer || 642 UDFIsDeleted(DirNdx)) 643 continue; 644 if(hashes && 645 (DirNdx->hashes.hLfn != hashes->hLfn) && 646 (DirNdx->hashes.hPosix != hashes->hPosix) && 647 (!CanBe8dot3 || ((DirNdx->hashes.hDos != hashes->hLfn) && (DirNdx->hashes.hDos != hashes->hPosix))) ) 648 continue; 649 if(UDFIsNameInExpression(Vcb, &(DirNdx->FName),PtrSearchPattern, NULL,IgnoreCase, 650 ContainsWC, CanBe8dot3 && !(DirNdx->FI_Flags & UDF_FI_FLAG_DOS), 651 EntryNumber < 2) && 652 !(DirNdx->FI_Flags & UDF_FI_FLAG_FI_INTERNAL)) 653 break; 654 } 655 656 if(DirNdx) { 657 // Modify CurrentNumber to appropriate value 658 *CurrentNumber = EntryNumber; 659 *_DirNdx = DirNdx; 660 return STATUS_SUCCESS; 661 } else { 662 // Do not modify CurrentNumber because we have not found next match entry 663 return STATUS_NO_MORE_FILES; 664 } 665 } // end UDFFindNextMatch() 666 667 /************************************************************************* 668 * 669 * Function: UDFNotifyChangeDirectory() 670 * 671 * Description: 672 * Handle the notify request. 673 * 674 * Expected Interrupt Level (for execution) : 675 * 676 * IRQL_PASSIVE_LEVEL 677 * 678 * Return Value: STATUS_SUCCESS/Error 679 * 680 *************************************************************************/ 681 NTSTATUS 682 NTAPI 683 UDFNotifyChangeDirectory( 684 PtrUDFIrpContext PtrIrpContext, 685 PIRP Irp, 686 PIO_STACK_LOCATION IrpSp, 687 PFILE_OBJECT FileObject, 688 PtrUDFFCB Fcb, 689 PtrUDFCCB Ccb 690 ) 691 { 692 NTSTATUS RC = STATUS_SUCCESS; 693 BOOLEAN CompleteRequest = FALSE; 694 BOOLEAN PostRequest = FALSE; 695 PtrUDFNTRequiredFCB NtReqFcb = NULL; 696 BOOLEAN CanWait = FALSE; 697 ULONG CompletionFilter = 0; 698 BOOLEAN WatchTree = FALSE; 699 PVCB Vcb = NULL; 700 BOOLEAN AcquiredFCB = FALSE; 701 PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; 702 703 UDFPrint(("UDFNotifyChangeDirectory\n")); 704 705 _SEH2_TRY { 706 707 // Validate the sent-in FCB 708 if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || 709 !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { 710 711 CompleteRequest = TRUE; 712 try_return(RC = STATUS_INVALID_PARAMETER); 713 } 714 715 NtReqFcb = Fcb->NTRequiredFCB; 716 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; 717 Vcb = Fcb->Vcb; 718 719 // Acquire the FCB resource shared 720 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 721 if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { 722 PostRequest = TRUE; 723 try_return(RC = STATUS_PENDING); 724 } 725 AcquiredFCB = TRUE; 726 727 // If the file is marked as DELETE_PENDING then complete this 728 // request immediately. 729 if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) { 730 ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); 731 try_return(RC = STATUS_DELETE_PENDING); 732 } 733 734 // Obtain some parameters sent by the caller 735 CompletionFilter = pStackLocation ->Parameters.NotifyDirectory.CompletionFilter; 736 WatchTree = (IrpSp->Flags & SL_WATCH_TREE) ? TRUE : FALSE; 737 738 // If we wish to capture the subject context, we can do so as 739 // follows: 740 // { 741 // PSECURITY_SUBJECT_CONTEXT SubjectContext; 742 // SubjectContext = MyAllocatePool__(PagedPool, 743 // sizeof(SECURITY_SUBJECT_CONTEXT)); 744 // SeCaptureSubjectContext(SubjectContext); 745 // } 746 747 FsRtlNotifyFullChangeDirectory(Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb, 748 (Fcb->FileInfo->ParentFile) ? (PSTRING)&(Fcb->FCBName->ObjectName) : (PSTRING)&(UDFGlobalData.UnicodeStrRoot), 749 WatchTree, FALSE, CompletionFilter, Irp, 750 NULL, // UDFTraverseAccessCheck(...) ? 751 NULL); // SubjectContext ? 752 753 RC = STATUS_PENDING; 754 755 try_exit: NOTHING; 756 757 } _SEH2_FINALLY { 758 759 if (PostRequest) { 760 // Perform appropriate related post processing here 761 if (AcquiredFCB) { 762 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 763 UDFReleaseResource(&(NtReqFcb->MainResource)); 764 AcquiredFCB = FALSE; 765 } 766 RC = UDFPostRequest(PtrIrpContext, Irp); 767 } else if (CompleteRequest) { 768 769 if (!_SEH2_AbnormalTermination()) { 770 Irp->IoStatus.Status = RC; 771 Irp->IoStatus.Information = 0; 772 // Free up the Irp Context 773 UDFReleaseIrpContext(PtrIrpContext); 774 // complete the IRP 775 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 776 } 777 778 } else { 779 // Simply free up the IrpContext since the IRP has been queued 780 if (!_SEH2_AbnormalTermination()) 781 UDFReleaseIrpContext(PtrIrpContext); 782 } 783 784 // Release the FCB resources if acquired. 785 if (AcquiredFCB) { 786 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 787 UDFReleaseResource(&(NtReqFcb->MainResource)); 788 AcquiredFCB = FALSE; 789 } 790 791 } _SEH2_END; 792 793 return(RC); 794 } // end UDFNotifyChangeDirectory() 795