1 /* 2 * COPYRIGHT: See COPYRIGHT.TXT 3 * PROJECT: Ext2 File System Driver for WinNT/2K/XP 4 * FILE: dirctl.c 5 * PROGRAMMER: Matt Wu <mattwu@163.com> 6 * HOMEPAGE: http://www.ext2fsd.com 7 * UPDATE HISTORY: 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include "ext2fs.h" 13 14 /* GLOBALS ***************************************************************/ 15 16 extern PEXT2_GLOBAL Ext2Global; 17 18 /* DEFINITIONS *************************************************************/ 19 20 #ifdef ALLOC_PRAGMA 21 #pragma alloc_text(PAGE, Ext2GetInfoLength) 22 #pragma alloc_text(PAGE, Ext2ProcessEntry) 23 #pragma alloc_text(PAGE, Ext2QueryDirectory) 24 #pragma alloc_text(PAGE, Ext2NotifyChangeDirectory) 25 #pragma alloc_text(PAGE, Ext2DirectoryControl) 26 #pragma alloc_text(PAGE, Ext2IsDirectoryEmpty) 27 #endif 28 29 ULONG 30 Ext2GetInfoLength(IN FILE_INFORMATION_CLASS FileInformationClass) 31 { 32 switch (FileInformationClass) { 33 34 case FileDirectoryInformation: 35 return FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName[0]); 36 37 case FileFullDirectoryInformation: 38 return FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName[0]); 39 40 case FileBothDirectoryInformation: 41 return FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName[0]); 42 43 case FileNamesInformation: 44 return FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName[0]); 45 46 case FileIdFullDirectoryInformation: 47 return FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION, FileName[0]); 48 49 case FileIdBothDirectoryInformation: 50 return FIELD_OFFSET(FILE_ID_BOTH_DIR_INFORMATION, FileName[0]); 51 52 default: 53 break; 54 } 55 56 return 0; 57 } 58 59 NTSTATUS 60 Ext2ProcessEntry( 61 IN PEXT2_IRP_CONTEXT IrpContext, 62 IN PEXT2_VCB Vcb, 63 IN PEXT2_FCB Dcb, 64 IN FILE_INFORMATION_CLASS FileInformationClass, 65 IN ULONG in, 66 IN PVOID Buffer, 67 IN ULONG UsedLength, 68 IN ULONG Length, 69 IN ULONG FileIndex, 70 IN PUNICODE_STRING pName, 71 OUT PULONG EntrySize, 72 IN BOOLEAN Single 73 ) 74 { 75 PFILE_DIRECTORY_INFORMATION FDI = NULL; 76 PFILE_FULL_DIR_INFORMATION FFI = NULL; 77 PFILE_ID_FULL_DIR_INFORMATION FIF = NULL; 78 79 PFILE_BOTH_DIR_INFORMATION FBI = NULL; 80 PFILE_ID_BOTH_DIR_INFORMATION FIB = NULL; 81 82 PFILE_NAMES_INFORMATION FNI = NULL; 83 84 PEXT2_MCB Mcb = NULL; 85 PEXT2_MCB Target = NULL; 86 87 NTSTATUS Status = STATUS_SUCCESS; 88 struct inode Inode = { 0 }; 89 90 ULONG InfoLength = 0; 91 ULONG NameLength = 0; 92 #ifndef __REACTOS__ 93 ULONG dwBytes = 0; 94 #endif 95 LONGLONG FileSize = 0; 96 LONGLONG AllocationSize; 97 ULONG FileAttributes = 0; 98 99 BOOLEAN IsEntrySymlink = FALSE; 100 101 *EntrySize = 0; 102 NameLength = pName->Length; 103 ASSERT((UsedLength & 7) == 0); 104 105 InfoLength = Ext2GetInfoLength(FileInformationClass); 106 if (InfoLength == 0) { 107 DEBUG(DL_ERR, ("Ext2ProcessDirEntry: Invalid Info Class %xh for %wZ in %wZ\n", 108 FileInformationClass, pName, &Dcb->Mcb->FullName )); 109 return STATUS_INVALID_INFO_CLASS; 110 } 111 112 if (InfoLength + NameLength > Length) { 113 DEBUG(DL_INF, ( "Ext2PricessDirEntry: Buffer is not enough.\n")); 114 Status = STATUS_BUFFER_OVERFLOW; 115 if (UsedLength || InfoLength > Length) { 116 DEBUG(DL_CP, ("Ext2ProcessDirEntry: Buffer overflows for %wZ in %wZ\n", 117 pName, &Dcb->Mcb->FullName )); 118 return Status; 119 } 120 } 121 122 DEBUG(DL_CP, ("Ext2ProcessDirEntry: %wZ in %wZ\n", pName, &Dcb->Mcb->FullName )); 123 124 Mcb = Ext2SearchMcb(Vcb, Dcb->Mcb, pName); 125 if (NULL != Mcb) { 126 if (S_ISLNK(Mcb->Inode.i_mode) && NULL == Mcb->Target) { 127 Ext2FollowLink( IrpContext, Vcb, Dcb->Mcb, Mcb, 0); 128 } 129 130 } else { 131 132 Inode.i_ino = in; 133 Inode.i_sb = &Vcb->sb; 134 if (!Ext2LoadInode(Vcb, &Inode)) { 135 DEBUG(DL_ERR, ("Ext2PricessDirEntry: Loading inode %xh (%wZ) error.\n", 136 in, pName )); 137 DbgBreak(); 138 Status = STATUS_SUCCESS; 139 goto errorout; 140 } 141 142 if (S_ISDIR(Inode.i_mode) || S_ISREG(Inode.i_mode)) { 143 } else if (S_ISLNK(Inode.i_mode)) { 144 DEBUG(DL_RES, ("Ext2ProcessDirEntry: SymLink: %wZ\\%wZ\n", 145 &Dcb->Mcb->FullName, pName)); 146 Ext2LookupFile(IrpContext, Vcb, pName, Dcb->Mcb, &Mcb,0); 147 148 if (Mcb && IsMcbSpecialFile(Mcb)) { 149 Ext2DerefMcb(Mcb); 150 Mcb = NULL; 151 } 152 } else { 153 Inode.i_size = 0; 154 } 155 } 156 157 if (Mcb != NULL) { 158 159 FileAttributes = Mcb->FileAttr; 160 if (IsMcbSymLink(Mcb)) { 161 Target = Mcb->Target; 162 ASSERT(Target); 163 ASSERT(!IsMcbSymLink(Target)); 164 if (IsMcbDirectory(Target)) { 165 FileSize = 0; 166 FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; 167 } else { 168 FileSize = Target->Inode.i_size; 169 } 170 if (IsFileDeleted(Target)) { 171 ClearFlag(FileAttributes, FILE_ATTRIBUTE_DIRECTORY); 172 FileSize = 0; 173 } 174 } else { 175 if (IsMcbDirectory(Mcb)) { 176 FileSize = 0; 177 } else { 178 FileSize = Mcb->Inode.i_size; 179 } 180 } 181 182 if (IsInodeSymLink(&Mcb->Inode)) { 183 IsEntrySymlink = TRUE; 184 } 185 186 } else { 187 188 if (S_ISDIR(Inode.i_mode)) { 189 FileSize = 0; 190 } else { 191 FileSize = Inode.i_size; 192 } 193 194 if (S_ISDIR(Inode.i_mode)) { 195 FileAttributes = FILE_ATTRIBUTE_DIRECTORY; 196 } else if (S_ISLNK(Inode.i_mode)) { 197 FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT; 198 IsEntrySymlink = TRUE; 199 } else { 200 FileAttributes = FILE_ATTRIBUTE_NORMAL; 201 } 202 203 if (!Ext2CheckInodeAccess(Vcb, &Inode, Ext2FileCanWrite)) { 204 SetFlag(FileAttributes, FILE_ATTRIBUTE_READONLY); 205 } 206 } 207 208 if (FileAttributes == 0) 209 FileAttributes = FILE_ATTRIBUTE_NORMAL; 210 211 AllocationSize = CEILING_ALIGNED(ULONGLONG, FileSize, BLOCK_SIZE); 212 213 /* process special files under root directory */ 214 if (IsRoot(Dcb)) { 215 /* set hidden and system attributes for Recycled / 216 RECYCLER / pagefile.sys */ 217 BOOLEAN IsDirectory = IsFlagOn(FileAttributes, FILE_ATTRIBUTE_DIRECTORY); 218 if (Ext2IsSpecialSystemFile(pName, IsDirectory)) { 219 SetFlag(FileAttributes, FILE_ATTRIBUTE_HIDDEN); 220 SetFlag(FileAttributes, FILE_ATTRIBUTE_SYSTEM); 221 } 222 } 223 224 /* set hidden attribute for all entries starting with '.' */ 225 if (( pName->Length >= 4 && pName->Buffer[0] == L'.') && 226 ((pName->Length == 4 && pName->Buffer[1] != L'.') || 227 pName->Length >= 6 )) { 228 SetFlag(FileAttributes, FILE_ATTRIBUTE_HIDDEN); 229 } 230 231 switch (FileInformationClass) { 232 233 case FileIdFullDirectoryInformation: 234 FIF = (PFILE_ID_FULL_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength); 235 case FileFullDirectoryInformation: 236 FFI = (PFILE_FULL_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength); 237 case FileDirectoryInformation: 238 FDI = (PFILE_DIRECTORY_INFORMATION) ((PUCHAR)Buffer + UsedLength); 239 240 if (!Single) { 241 FDI->NextEntryOffset = CEILING_ALIGNED(ULONG, InfoLength + NameLength, 8); 242 } 243 244 FDI->FileIndex = FileIndex; 245 246 if (Mcb) { 247 248 FDI->CreationTime = Mcb->CreationTime; 249 FDI->LastAccessTime = Mcb->LastAccessTime; 250 FDI->LastWriteTime = Mcb->LastWriteTime; 251 FDI->ChangeTime = Mcb->ChangeTime; 252 253 } else { 254 255 FDI->CreationTime = Ext2NtTime(Inode.i_ctime); 256 FDI->LastAccessTime = Ext2NtTime(Inode.i_atime); 257 FDI->LastWriteTime = Ext2NtTime(Inode.i_mtime); 258 FDI->ChangeTime = Ext2NtTime(Inode.i_mtime); 259 } 260 261 FDI->FileAttributes = FileAttributes; 262 FDI->EndOfFile.QuadPart = FileSize; 263 FDI->AllocationSize.QuadPart = AllocationSize; 264 265 FDI->FileNameLength = NameLength; 266 if (InfoLength + NameLength > Length) { 267 NameLength = Length - InfoLength; 268 } 269 270 if (FIF) { 271 FIF->FileId.QuadPart = (LONGLONG) in; 272 if (IsEntrySymlink) { 273 FIF->EaSize = IO_REPARSE_TAG_SYMLINK; 274 } 275 RtlCopyMemory(&FIF->FileName[0], &pName->Buffer[0], NameLength); 276 } else if (FFI) { 277 if (IsEntrySymlink) { 278 FFI->EaSize = IO_REPARSE_TAG_SYMLINK; 279 } 280 RtlCopyMemory(&FFI->FileName[0], &pName->Buffer[0], NameLength); 281 } else { 282 RtlCopyMemory(&FDI->FileName[0], &pName->Buffer[0], NameLength); 283 } 284 285 *EntrySize = InfoLength + NameLength; 286 break; 287 288 289 case FileIdBothDirectoryInformation: 290 FIB = (PFILE_ID_BOTH_DIR_INFORMATION)((PUCHAR)Buffer + UsedLength); 291 case FileBothDirectoryInformation: 292 FBI = (PFILE_BOTH_DIR_INFORMATION) ((PUCHAR)Buffer + UsedLength); 293 294 if (!Single) { 295 FBI->NextEntryOffset = CEILING_ALIGNED(ULONG, InfoLength + NameLength, 8); 296 } 297 298 FBI->FileIndex = FileIndex; 299 FBI->EndOfFile.QuadPart = FileSize; 300 FBI->AllocationSize.QuadPart = AllocationSize; 301 302 if (Mcb) { 303 304 FBI->CreationTime = Mcb->CreationTime; 305 FBI->LastAccessTime = Mcb->LastAccessTime; 306 FBI->LastWriteTime = Mcb->LastWriteTime; 307 FBI->ChangeTime = Mcb->ChangeTime; 308 309 } else { 310 311 FBI->CreationTime = Ext2NtTime(Inode.i_ctime); 312 FBI->LastAccessTime = Ext2NtTime(Inode.i_atime); 313 FBI->LastWriteTime = Ext2NtTime(Inode.i_mtime); 314 FBI->ChangeTime = Ext2NtTime(Inode.i_mtime); 315 } 316 317 FBI->FileAttributes = FileAttributes; 318 319 FBI->FileNameLength = NameLength; 320 if (InfoLength + NameLength > Length) { 321 NameLength = Length - InfoLength; 322 } 323 324 if (FIB) { 325 FIB->FileId.QuadPart = (LONGLONG)in; 326 if (IsEntrySymlink) { 327 FIB->EaSize = IO_REPARSE_TAG_SYMLINK; 328 } 329 RtlCopyMemory(&FIB->FileName[0], &pName->Buffer[0], NameLength); 330 } else { 331 RtlCopyMemory(&FBI->FileName[0], &pName->Buffer[0], NameLength); 332 } 333 334 *EntrySize = InfoLength + NameLength; 335 break; 336 337 case FileNamesInformation: 338 339 FNI = (PFILE_NAMES_INFORMATION) ((PUCHAR)Buffer + UsedLength); 340 if (!Single) { 341 FNI->NextEntryOffset = CEILING_ALIGNED(ULONG, InfoLength + NameLength, 8); 342 } 343 344 FNI->FileNameLength = NameLength; 345 if (InfoLength + NameLength > Length) { 346 NameLength = Length - InfoLength; 347 } 348 RtlCopyMemory(&FNI->FileName[0], &pName->Buffer[0], NameLength); 349 350 *EntrySize = InfoLength + NameLength; 351 break; 352 353 default: 354 Status = STATUS_INVALID_INFO_CLASS; 355 break; 356 } 357 358 if (Mcb) { 359 Ext2DerefMcb(Mcb); 360 } 361 362 errorout: 363 364 DEBUG(DL_CP, ("Ext2ProcessDirEntry: Status = %xh for %wZ in %wZ\n", 365 Status, pName, &Dcb->Mcb->FullName )); 366 367 return Status; 368 } 369 370 371 BOOLEAN 372 Ext2IsWearingCloak( 373 IN PEXT2_VCB Vcb, 374 IN POEM_STRING OemName 375 ) 376 { 377 size_t PatLen = 0; 378 379 /* we could not filter the files: "." and ".." */ 380 if (OemName->Length >= 1 && OemName->Buffer[0] == '.') { 381 382 if ( OemName->Length == 2 && OemName->Buffer[1] == '.') { 383 return FALSE; 384 } else if (OemName->Length == 1) { 385 return FALSE; 386 } 387 } 388 389 /* checking name prefix */ 390 if (Vcb->bHidingPrefix) { 391 PatLen = strlen(&Vcb->sHidingPrefix[0]); 392 if (PatLen > 0 && PatLen <= OemName->Length) { 393 if ( _strnicmp( OemName->Buffer, 394 Vcb->sHidingPrefix, 395 PatLen ) == 0) { 396 return TRUE; 397 } 398 } 399 } 400 401 /* checking name suffix */ 402 if (Vcb->bHidingSuffix) { 403 PatLen = strlen(&Vcb->sHidingSuffix[0]); 404 if (PatLen > 0 && PatLen <= OemName->Length) { 405 if ( _strnicmp(&OemName->Buffer[OemName->Length - PatLen], 406 Vcb->sHidingSuffix, PatLen ) == 0) { 407 return TRUE; 408 } 409 } 410 } 411 412 return FALSE; 413 } 414 415 static int Ext2FillEntry(void *context, const char *name, int namlen, 416 ULONG offset, __u32 ino, unsigned int d_type) 417 { 418 PEXT2_FILLDIR_CONTEXT fc = context; 419 PEXT2_IRP_CONTEXT IrpContext = fc->efc_irp; 420 421 PEXT2_FCB Fcb = IrpContext->Fcb; 422 PEXT2_CCB Ccb = IrpContext->Ccb; 423 PEXT2_VCB Vcb = Fcb->Vcb; 424 425 OEM_STRING Oem; 426 UNICODE_STRING Unicode = { 0 }; 427 NTSTATUS Status = STATUS_SUCCESS; 428 ULONG EntrySize; 429 USHORT NameLen; 430 int rc = 0; 431 432 if (fc->efc_start > 0 && (fc->efc_single || (fc->efc_size < 433 fc->efc_start + namlen * 2 + Ext2GetInfoLength(fc->efc_fi)) )) { 434 rc = 1; 435 goto errorout; 436 } 437 438 439 Oem.Buffer = (void *)name; 440 Oem.Length = namlen & 0xFF; 441 Oem.MaximumLength = Oem.Length; 442 443 /* skip . and .. */ 444 if ((Oem.Length == 1 && name[0] == '.') || (Oem.Length == 2 && 445 name[0] == '.' && name[1] == '.' )) { 446 goto errorout; 447 } 448 449 if (Ext2IsWearingCloak(Vcb, &Oem)) { 450 goto errorout; 451 } 452 453 NameLen = (USHORT)Ext2OEMToUnicodeSize(Vcb, &Oem); 454 if (NameLen <= 0) { 455 fc->efc_status = STATUS_INSUFFICIENT_RESOURCES; 456 rc = -ENOMEM; 457 goto errorout; 458 } 459 460 Unicode.MaximumLength = NameLen + 2; 461 Unicode.Buffer = Ext2AllocatePool( 462 PagedPool, 463 Unicode.MaximumLength, 464 EXT2_INAME_MAGIC 465 ); 466 if (!Unicode.Buffer) { 467 DEBUG(DL_ERR, ( "Ex2QueryDirectory: failed to " 468 "allocate InodeFileName.\n")); 469 fc->efc_status = STATUS_INSUFFICIENT_RESOURCES; 470 rc = -ENOMEM; 471 goto errorout; 472 } 473 RtlZeroMemory(Unicode.Buffer, Unicode.MaximumLength); 474 INC_MEM_COUNT(PS_INODE_NAME, Unicode.Buffer, Unicode.MaximumLength); 475 476 Status = Ext2OEMToUnicode(Vcb, &Unicode, &Oem); 477 if (!NT_SUCCESS(Status)) { 478 DEBUG(DL_ERR, ( "Ex2QueryDirectory: Ext2OEMtoUnicode failed with %xh.\n", Status)); 479 fc->efc_status = STATUS_INSUFFICIENT_RESOURCES; 480 rc = -ENOMEM; 481 goto errorout; 482 } 483 484 if (FsRtlDoesNameContainWildCards( &Ccb->DirectorySearchPattern) ? 485 FsRtlIsNameInExpression(&Ccb->DirectorySearchPattern, 486 &Unicode, TRUE, NULL) : 487 !RtlCompareUnicodeString(&Ccb->DirectorySearchPattern, 488 &Unicode, TRUE)) { 489 Status = Ext2ProcessEntry(fc->efc_irp, Vcb, Fcb, fc->efc_fi, ino, fc->efc_buf, 490 CEILING_ALIGNED(ULONG, fc->efc_start, 8), 491 fc->efc_size - CEILING_ALIGNED(ULONG, fc->efc_start, 8), 492 offset, &Unicode, &EntrySize, fc->efc_single); 493 if (NT_SUCCESS(Status)) { 494 if (EntrySize > 0) { 495 fc->efc_prev = CEILING_ALIGNED(ULONG, fc->efc_start, 8); 496 fc->efc_start = fc->efc_prev + EntrySize; 497 } else { 498 DbgBreak(); 499 } 500 } else { 501 if (Status == STATUS_BUFFER_OVERFLOW) { 502 if (fc->efc_start == 0) { 503 fc->efc_start = EntrySize; 504 } else { 505 Status = STATUS_SUCCESS; 506 } 507 } 508 rc = 1; 509 } 510 } 511 512 errorout: 513 514 fc->efc_status = Status; 515 if (Unicode.Buffer) { 516 DEC_MEM_COUNT(PS_INODE_NAME, Unicode.Buffer, Unicode.MaximumLength ); 517 Ext2FreePool(Unicode.Buffer, EXT2_INAME_MAGIC); 518 } 519 520 return rc; 521 } 522 523 524 NTSTATUS 525 Ext2QueryDirectory (IN PEXT2_IRP_CONTEXT IrpContext) 526 { 527 PDEVICE_OBJECT DeviceObject; 528 NTSTATUS Status = STATUS_UNSUCCESSFUL; 529 PEXT2_VCB Vcb = NULL; 530 PFILE_OBJECT FileObject = NULL; 531 PEXT2_FCB Fcb = NULL; 532 PEXT2_MCB Mcb = NULL; 533 PEXT2_CCB Ccb = NULL; 534 PIRP Irp = NULL; 535 PIO_STACK_LOCATION IoStackLocation = NULL; 536 537 ULONG Length; 538 ULONG FileIndex; 539 PUNICODE_STRING FileName; 540 PUCHAR Buffer; 541 542 BOOLEAN RestartScan; 543 BOOLEAN ReturnSingleEntry; 544 BOOLEAN IndexSpecified; 545 BOOLEAN FirstQuery; 546 BOOLEAN FcbResourceAcquired = FALSE; 547 548 USHORT NameLen; 549 FILE_INFORMATION_CLASS fi; 550 551 OEM_STRING Oem = { 0 }; 552 UNICODE_STRING Unicode = { 0 }; 553 PEXT2_DIR_ENTRY2 pDir = NULL; 554 555 ULONG ByteOffset; 556 ULONG RecLen = 0; 557 ULONG EntrySize = 0; 558 559 EXT2_FILLDIR_CONTEXT fc = { 0 }; 560 561 _SEH2_TRY { 562 563 ASSERT(IrpContext); 564 ASSERT((IrpContext->Identifier.Type == EXT2ICX) && 565 (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); 566 567 DeviceObject = IrpContext->DeviceObject; 568 569 // 570 // This request is not allowed on the main device object 571 // 572 if (IsExt2FsDevice(DeviceObject)) { 573 Status = STATUS_INVALID_DEVICE_REQUEST; 574 _SEH2_LEAVE; 575 } 576 577 Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; 578 ASSERT(Vcb != NULL); 579 ASSERT((Vcb->Identifier.Type == EXT2VCB) && 580 (Vcb->Identifier.Size == sizeof(EXT2_VCB))); 581 582 if (!IsMounted(Vcb)) { 583 Status = STATUS_VOLUME_DISMOUNTED; 584 _SEH2_LEAVE; 585 } 586 587 if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { 588 Status = STATUS_ACCESS_DENIED; 589 _SEH2_LEAVE; 590 } 591 592 FileObject = IrpContext->FileObject; 593 Fcb = (PEXT2_FCB) FileObject->FsContext; 594 if (Fcb == NULL) { 595 Status = STATUS_INVALID_PARAMETER; 596 _SEH2_LEAVE; 597 } 598 Mcb = Fcb->Mcb; 599 if (NULL == Mcb) { 600 Status = STATUS_INVALID_PARAMETER; 601 _SEH2_LEAVE; 602 } 603 ASSERT (!IsMcbSymLink(Mcb)); 604 605 // 606 // This request is not allowed on volumes 607 // 608 if (Fcb->Identifier.Type == EXT2VCB) { 609 Status = STATUS_INVALID_PARAMETER; 610 _SEH2_LEAVE; 611 } 612 613 ASSERT((Fcb->Identifier.Type == EXT2FCB) && 614 (Fcb->Identifier.Size == sizeof(EXT2_FCB))); 615 616 if (!IsMcbDirectory(Mcb)) { 617 Status = STATUS_NOT_A_DIRECTORY; 618 _SEH2_LEAVE; 619 } 620 621 if (IsFileDeleted(Mcb)) { 622 Status = STATUS_NOT_A_DIRECTORY; 623 _SEH2_LEAVE; 624 } 625 626 Ccb = (PEXT2_CCB) FileObject->FsContext2; 627 628 ASSERT(Ccb); 629 ASSERT((Ccb->Identifier.Type == EXT2CCB) && 630 (Ccb->Identifier.Size == sizeof(EXT2_CCB))); 631 632 Irp = IrpContext->Irp; 633 IoStackLocation = IoGetCurrentIrpStackLocation(Irp); 634 635 #ifndef _GNU_NTIFS_ 636 637 fi = IoStackLocation->Parameters.QueryDirectory.FileInformationClass; 638 639 Length = IoStackLocation->Parameters.QueryDirectory.Length; 640 641 FileName = (PUNICODE_STRING)IoStackLocation->Parameters.QueryDirectory.FileName; 642 643 FileIndex = IoStackLocation->Parameters.QueryDirectory.FileIndex; 644 645 #else // _GNU_NTIFS_ 646 647 fi = ((PEXTENDED_IO_STACK_LOCATION) 648 IoStackLocation)->Parameters.QueryDirectory.FileInformationClass; 649 650 Length = ((PEXTENDED_IO_STACK_LOCATION) 651 IoStackLocation)->Parameters.QueryDirectory.Length; 652 653 FileName = ((PEXTENDED_IO_STACK_LOCATION) 654 IoStackLocation)->Parameters.QueryDirectory.FileName; 655 656 FileIndex = ((PEXTENDED_IO_STACK_LOCATION) 657 IoStackLocation)->Parameters.QueryDirectory.FileIndex; 658 659 #endif // _GNU_NTIFS_ 660 661 RestartScan = FlagOn(((PEXTENDED_IO_STACK_LOCATION) 662 IoStackLocation)->Flags, SL_RESTART_SCAN); 663 ReturnSingleEntry = FlagOn(((PEXTENDED_IO_STACK_LOCATION) 664 IoStackLocation)->Flags, SL_RETURN_SINGLE_ENTRY); 665 IndexSpecified = FlagOn(((PEXTENDED_IO_STACK_LOCATION) 666 IoStackLocation)->Flags, SL_INDEX_SPECIFIED); 667 668 Buffer = Ext2GetUserBuffer(Irp); 669 if (Buffer == NULL) { 670 DbgBreak(); 671 Status = STATUS_INVALID_USER_BUFFER; 672 _SEH2_LEAVE; 673 } 674 675 if (!IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { 676 Status = STATUS_PENDING; 677 _SEH2_LEAVE; 678 } 679 680 if (!ExAcquireResourceSharedLite( 681 &Fcb->MainResource, 682 IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) { 683 Status = STATUS_PENDING; 684 _SEH2_LEAVE; 685 } 686 FcbResourceAcquired = TRUE; 687 688 if (FileName != NULL) { 689 690 if (Ccb->DirectorySearchPattern.Buffer != NULL) { 691 692 FirstQuery = FALSE; 693 694 } else { 695 696 FirstQuery = TRUE; 697 698 Ccb->DirectorySearchPattern.Length = 699 Ccb->DirectorySearchPattern.MaximumLength = 700 FileName->Length; 701 702 Ccb->DirectorySearchPattern.Buffer = 703 Ext2AllocatePool(PagedPool, FileName->Length, 704 EXT2_DIRSP_MAGIC); 705 706 if (Ccb->DirectorySearchPattern.Buffer == NULL) { 707 DEBUG(DL_ERR, ( "Ex2QueryDirectory: failed to allocate SerarchPattern.\n")); 708 Status = STATUS_INSUFFICIENT_RESOURCES; 709 _SEH2_LEAVE; 710 } 711 712 INC_MEM_COUNT( PS_DIR_PATTERN, 713 Ccb->DirectorySearchPattern.Buffer, 714 Ccb->DirectorySearchPattern.MaximumLength); 715 716 Status = RtlUpcaseUnicodeString( 717 &(Ccb->DirectorySearchPattern), 718 FileName, 719 FALSE); 720 721 if (!NT_SUCCESS(Status)) { 722 _SEH2_LEAVE; 723 } 724 } 725 726 } else if (Ccb->DirectorySearchPattern.Buffer != NULL) { 727 728 FirstQuery = FALSE; 729 FileName = &Ccb->DirectorySearchPattern; 730 731 } else { 732 733 FirstQuery = TRUE; 734 735 Ccb->DirectorySearchPattern.Length = 736 Ccb->DirectorySearchPattern.MaximumLength = 2; 737 738 Ccb->DirectorySearchPattern.Buffer = 739 Ext2AllocatePool(PagedPool, 4, EXT2_DIRSP_MAGIC); 740 741 if (Ccb->DirectorySearchPattern.Buffer == NULL) { 742 DEBUG(DL_ERR, ( "Ex2QueryDirectory: failed to allocate SerarchPattern (1st).\n")); 743 Status = STATUS_INSUFFICIENT_RESOURCES; 744 _SEH2_LEAVE; 745 } 746 747 INC_MEM_COUNT( PS_DIR_PATTERN, 748 Ccb->DirectorySearchPattern.Buffer, 749 Ccb->DirectorySearchPattern.MaximumLength); 750 751 RtlZeroMemory(Ccb->DirectorySearchPattern.Buffer, 4); 752 RtlCopyMemory( 753 Ccb->DirectorySearchPattern.Buffer, 754 L"*\0", 2); 755 } 756 757 if (IndexSpecified) { 758 Ccb->filp.f_pos = FileIndex; 759 } else { 760 if (RestartScan || FirstQuery) { 761 Ccb->filp.f_pos = FileIndex = 0; 762 } else { 763 FileIndex = (ULONG)Ccb->filp.f_pos; 764 } 765 } 766 767 RtlZeroMemory(Buffer, Length); 768 769 fc.efc_irp = IrpContext; 770 fc.efc_buf = Buffer; 771 fc.efc_size = Length; 772 fc.efc_start = 0; 773 fc.efc_single = ReturnSingleEntry; 774 fc.efc_fi = fi; 775 fc.efc_status = STATUS_SUCCESS; 776 777 #ifdef EXT2_HTREE_INDEX 778 779 if (EXT3_HAS_COMPAT_FEATURE(Mcb->Inode.i_sb, 780 EXT3_FEATURE_COMPAT_DIR_INDEX) && 781 ((EXT3_I(&Mcb->Inode)->i_flags & EXT3_INDEX_FL) || 782 ((Mcb->Inode.i_size >> BLOCK_BITS) == 1)) ) { 783 int rc = ext3_dx_readdir(&Ccb->filp, Ext2FillEntry, &fc); 784 Status = fc.efc_status; 785 if (rc != ERR_BAD_DX_DIR) { 786 goto errorout; 787 } 788 /* 789 * We don't set the inode dirty flag since it's not 790 * critical that it get flushed back to the disk. 791 */ 792 EXT3_I(&Mcb->Inode)->i_flags &= ~EXT3_INDEX_FL; 793 } 794 #endif 795 796 if (Mcb->Inode.i_size <= Ccb->filp.f_pos) { 797 Status = STATUS_NO_MORE_FILES; 798 _SEH2_LEAVE; 799 } 800 801 pDir = Ext2AllocatePool( 802 PagedPool, 803 sizeof(EXT2_DIR_ENTRY2), 804 EXT2_DENTRY_MAGIC 805 ); 806 807 if (!pDir) { 808 DEBUG(DL_ERR, ( "Ex2QueryDirectory: failed to allocate pDir.\n")); 809 Status = STATUS_INSUFFICIENT_RESOURCES; 810 _SEH2_LEAVE; 811 } 812 813 INC_MEM_COUNT(PS_DIR_ENTRY, pDir, sizeof(EXT2_DIR_ENTRY2)); 814 ByteOffset = FileIndex; 815 816 DEBUG(DL_CP, ("Ex2QueryDirectory: Dir: %wZ Index=%xh Pattern : %wZ.\n", 817 &Fcb->Mcb->FullName, FileIndex, &Ccb->DirectorySearchPattern)); 818 819 while ((ByteOffset < Mcb->Inode.i_size) && 820 (CEILING_ALIGNED(ULONG, fc.efc_start, 8) < Length)) { 821 822 RtlZeroMemory(pDir, sizeof(EXT2_DIR_ENTRY2)); 823 824 Status = Ext2ReadInode( 825 IrpContext, 826 Vcb, 827 Mcb, 828 (ULONGLONG)ByteOffset, 829 (PVOID)pDir, 830 sizeof(EXT2_DIR_ENTRY2), 831 FALSE, 832 &EntrySize); 833 834 if (!NT_SUCCESS(Status)) { 835 DbgBreak(); 836 _SEH2_LEAVE; 837 } 838 839 if (pDir->rec_len == 0) { 840 RecLen = BLOCK_SIZE - (ByteOffset & (BLOCK_SIZE - 1)); 841 } else { 842 RecLen = ext3_rec_len_from_disk(pDir->rec_len); 843 } 844 845 if (!pDir->inode || pDir->inode >= INODES_COUNT) { 846 goto ProcessNextEntry; 847 } 848 849 /* skip . and .. */ 850 if ((pDir->name_len == 1 && pDir->name[0] == '.') || 851 (pDir->name_len == 2 && pDir->name[0] == '.' && pDir->name[1] == '.' )) { 852 goto ProcessNextEntry; 853 } 854 855 Oem.Buffer = pDir->name; 856 Oem.Length = (pDir->name_len & 0xff); 857 Oem.MaximumLength = Oem.Length; 858 859 if (Ext2IsWearingCloak(Vcb, &Oem)) { 860 goto ProcessNextEntry; 861 } 862 863 NameLen = (USHORT) Ext2OEMToUnicodeSize(Vcb, &Oem); 864 865 if (NameLen <= 0) { 866 DEBUG(DL_CP, ("Ext2QueryDirectory: failed to count unicode length for inode: %xh\n", 867 pDir->inode)); 868 Status = STATUS_INSUFFICIENT_RESOURCES; 869 break; 870 } 871 872 if ( Unicode.Buffer != NULL && Unicode.MaximumLength > NameLen) { 873 /* reuse buffer */ 874 } else { 875 /* free and re-allocate it */ 876 if (Unicode.Buffer) { 877 DEC_MEM_COUNT(PS_INODE_NAME, 878 Unicode.Buffer, 879 Unicode.MaximumLength); 880 Ext2FreePool(Unicode.Buffer, EXT2_INAME_MAGIC); 881 } 882 Unicode.MaximumLength = NameLen + 2; 883 Unicode.Buffer = Ext2AllocatePool( 884 PagedPool, Unicode.MaximumLength, 885 EXT2_INAME_MAGIC 886 ); 887 if (!Unicode.Buffer) { 888 DEBUG(DL_ERR, ( "Ex2QueryDirectory: failed to " 889 "allocate InodeFileName.\n")); 890 Status = STATUS_INSUFFICIENT_RESOURCES; 891 _SEH2_LEAVE; 892 } 893 INC_MEM_COUNT(PS_INODE_NAME, Unicode.Buffer, Unicode.MaximumLength); 894 } 895 896 Unicode.Length = 0; 897 RtlZeroMemory(Unicode.Buffer, Unicode.MaximumLength); 898 899 Status = Ext2OEMToUnicode(Vcb, &Unicode, &Oem); 900 if (!NT_SUCCESS(Status)) { 901 DEBUG(DL_ERR, ( "Ex2QueryDirectory: Ext2OEMtoUnicode failed with %xh.\n", Status)); 902 Status = STATUS_INSUFFICIENT_RESOURCES; 903 _SEH2_LEAVE; 904 } 905 906 DEBUG(DL_CP, ( "Ex2QueryDirectory: process inode: %xh / %wZ (%d).\n", 907 pDir->inode, &Unicode, Unicode.Length)); 908 909 if (FsRtlDoesNameContainWildCards( 910 &(Ccb->DirectorySearchPattern)) ? 911 FsRtlIsNameInExpression( 912 &(Ccb->DirectorySearchPattern), 913 &Unicode, 914 TRUE, 915 NULL) : 916 !RtlCompareUnicodeString( 917 &(Ccb->DirectorySearchPattern), 918 &Unicode, 919 TRUE) ) { 920 921 Status = Ext2ProcessEntry( 922 IrpContext, 923 Vcb, 924 Fcb, 925 fi, 926 pDir->inode, 927 Buffer, 928 CEILING_ALIGNED(ULONG, fc.efc_start, 8), 929 Length - CEILING_ALIGNED(ULONG, fc.efc_start, 8), 930 ByteOffset, 931 &Unicode, 932 &EntrySize, 933 ReturnSingleEntry 934 ); 935 936 if (NT_SUCCESS(Status)) { 937 if (EntrySize > 0) { 938 fc.efc_prev = CEILING_ALIGNED(ULONG, fc.efc_start, 8); 939 fc.efc_start = fc.efc_prev + EntrySize; 940 } else { 941 DbgBreak(); 942 } 943 } else { 944 if (Status == STATUS_BUFFER_OVERFLOW) { 945 if (fc.efc_start == 0) { 946 fc.efc_start = EntrySize; 947 } else { 948 Status = STATUS_SUCCESS; 949 } 950 } else { 951 _SEH2_LEAVE; 952 } 953 break; 954 } 955 } 956 957 ProcessNextEntry: 958 959 ByteOffset += RecLen; 960 Ccb->filp.f_pos = ByteOffset; 961 962 if (fc.efc_start && ReturnSingleEntry) { 963 Status = STATUS_SUCCESS; 964 goto errorout; 965 } 966 } 967 968 errorout: 969 970 ((PULONG)((PUCHAR)Buffer + fc.efc_prev))[0] = 0; 971 FileIndex = ByteOffset; 972 973 if (Status == STATUS_BUFFER_OVERFLOW) { 974 /* just return fc.efc_start/EntrySize bytes that we filled */ 975 } else if (!fc.efc_start) { 976 if (NT_SUCCESS(Status)) { 977 if (FirstQuery) { 978 Status = STATUS_NO_SUCH_FILE; 979 } else { 980 Status = STATUS_NO_MORE_FILES; 981 } 982 } 983 } else { 984 Status = STATUS_SUCCESS; 985 } 986 987 } _SEH2_FINALLY { 988 989 if (FcbResourceAcquired) { 990 ExReleaseResourceLite(&Fcb->MainResource); 991 } 992 993 if (pDir != NULL) { 994 Ext2FreePool(pDir, EXT2_DENTRY_MAGIC); 995 DEC_MEM_COUNT(PS_DIR_ENTRY, pDir, sizeof(EXT2_DIR_ENTRY2)); 996 } 997 998 if (Unicode.Buffer != NULL) { 999 DEC_MEM_COUNT(PS_INODE_NAME, Unicode.Buffer, Unicode.MaximumLength); 1000 Ext2FreePool(Unicode.Buffer, EXT2_INAME_MAGIC); 1001 } 1002 1003 if (!IrpContext->ExceptionInProgress) { 1004 1005 if ( Status == STATUS_PENDING || 1006 Status == STATUS_CANT_WAIT) { 1007 1008 Status = Ext2LockUserBuffer( 1009 IrpContext->Irp, 1010 Length, 1011 IoWriteAccess ); 1012 1013 if (NT_SUCCESS(Status)) { 1014 Status = Ext2QueueRequest(IrpContext); 1015 } else { 1016 Ext2CompleteIrpContext(IrpContext, Status); 1017 } 1018 } else { 1019 IrpContext->Irp->IoStatus.Information = fc.efc_start; 1020 Ext2CompleteIrpContext(IrpContext, Status); 1021 } 1022 } 1023 } _SEH2_END; 1024 1025 return Status; 1026 } 1027 1028 NTSTATUS 1029 Ext2NotifyChangeDirectory ( 1030 IN PEXT2_IRP_CONTEXT IrpContext 1031 ) 1032 { 1033 PDEVICE_OBJECT DeviceObject; 1034 BOOLEAN CompleteRequest = TRUE; 1035 NTSTATUS Status = STATUS_UNSUCCESSFUL; 1036 PEXT2_VCB Vcb = NULL; 1037 PEXT2_FCB Fcb = NULL; 1038 PEXT2_CCB Ccb = NULL; 1039 PIRP Irp = NULL; 1040 PIO_STACK_LOCATION IrpSp; 1041 PFILE_OBJECT FileObject; 1042 ULONG CompletionFilter; 1043 BOOLEAN WatchTree; 1044 1045 BOOLEAN bFcbAcquired = FALSE; 1046 1047 _SEH2_TRY { 1048 1049 ASSERT(IrpContext); 1050 ASSERT((IrpContext->Identifier.Type == EXT2ICX) && 1051 (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); 1052 1053 // 1054 // Always set the wait flag in the Irp context for the original request. 1055 // 1056 1057 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); 1058 1059 DeviceObject = IrpContext->DeviceObject; 1060 1061 if (IsExt2FsDevice(DeviceObject)) { 1062 Status = STATUS_INVALID_DEVICE_REQUEST; 1063 _SEH2_LEAVE; 1064 } 1065 1066 Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; 1067 1068 ASSERT(Vcb != NULL); 1069 ASSERT((Vcb->Identifier.Type == EXT2VCB) && 1070 (Vcb->Identifier.Size == sizeof(EXT2_VCB))); 1071 1072 FileObject = IrpContext->FileObject; 1073 Fcb = (PEXT2_FCB) FileObject->FsContext; 1074 ASSERT(Fcb); 1075 if (Fcb->Identifier.Type == EXT2VCB) { 1076 DbgBreak(); 1077 Status = STATUS_INVALID_PARAMETER; 1078 _SEH2_LEAVE; 1079 } 1080 ASSERT((Fcb->Identifier.Type == EXT2FCB) && 1081 (Fcb->Identifier.Size == sizeof(EXT2_FCB))); 1082 1083 Ccb = (PEXT2_CCB) FileObject->FsContext2; 1084 ASSERT(Ccb); 1085 ASSERT((Ccb->Identifier.Type == EXT2CCB) && 1086 (Ccb->Identifier.Size == sizeof(EXT2_CCB))); 1087 1088 /* do nothing if target fie was deleted */ 1089 if (FlagOn(Fcb->Flags, FCB_DELETE_PENDING)) { 1090 Status = STATUS_FILE_DELETED; 1091 _SEH2_LEAVE; 1092 } 1093 1094 if (!IsDirectory(Fcb)) { 1095 DbgBreak(); 1096 Status = STATUS_INVALID_PARAMETER; 1097 _SEH2_LEAVE; 1098 } 1099 1100 if (ExAcquireResourceExclusiveLite( 1101 &Fcb->MainResource, 1102 TRUE )) { 1103 bFcbAcquired = TRUE; 1104 } else { 1105 Status = STATUS_PENDING; 1106 _SEH2_LEAVE; 1107 } 1108 1109 Irp = IrpContext->Irp; 1110 1111 IrpSp = IoGetCurrentIrpStackLocation(Irp); 1112 1113 #ifndef _GNU_NTIFS_ 1114 1115 CompletionFilter = 1116 IrpSp->Parameters.NotifyDirectory.CompletionFilter; 1117 1118 #else // _GNU_NTIFS_ 1119 1120 CompletionFilter = ((PEXTENDED_IO_STACK_LOCATION) 1121 IrpSp)->Parameters.NotifyDirectory.CompletionFilter; 1122 1123 #endif // _GNU_NTIFS_ 1124 1125 WatchTree = IsFlagOn(IrpSp->Flags, SL_WATCH_TREE); 1126 1127 if (FlagOn(Fcb->Flags, FCB_DELETE_PENDING)) { 1128 Status = STATUS_DELETE_PENDING; 1129 _SEH2_LEAVE; 1130 } 1131 1132 FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, 1133 &Vcb->NotifyList, 1134 FileObject->FsContext2, 1135 (PSTRING)(&Fcb->Mcb->FullName), 1136 WatchTree, 1137 FALSE, 1138 CompletionFilter, 1139 Irp, 1140 NULL, 1141 NULL ); 1142 1143 CompleteRequest = FALSE; 1144 1145 Status = STATUS_PENDING; 1146 1147 /* 1148 Currently the driver is read-only but here is an example on how to use the 1149 FsRtl-functions to report a change: 1150 1151 ANSI_STRING TestString; 1152 USHORT FileNamePartLength; 1153 1154 RtlInitAnsiString(&TestString, "\\ntifs.h"); 1155 1156 FileNamePartLength = 7; 1157 1158 FsRtlNotifyReportChange( 1159 Vcb->NotifySync, // PNOTIFY_SYNC NotifySync 1160 &Vcb->NotifyList, // PLIST_ENTRY NotifyList 1161 &TestString, // PSTRING FullTargetName 1162 &FileNamePartLength, // PUSHORT FileNamePartLength 1163 FILE_NOTIFY_CHANGE_NAME // ULONG FilterMatch 1164 ); 1165 1166 or 1167 1168 ANSI_STRING TestString; 1169 1170 RtlInitAnsiString(&TestString, "\\ntifs.h"); 1171 1172 FsRtlNotifyFullReportChange( 1173 Vcb->NotifySync, // PNOTIFY_SYNC NotifySync 1174 &Vcb->NotifyList, // PLIST_ENTRY NotifyList 1175 &TestString, // PSTRING FullTargetName 1176 1, // USHORT TargetNameOffset 1177 NULL, // PSTRING StreamName OPTIONAL 1178 NULL, // PSTRING NormalizedParentName OPTIONAL 1179 FILE_NOTIFY_CHANGE_NAME, // ULONG FilterMatch 1180 0, // ULONG Action 1181 NULL // PVOID TargetContext 1182 ); 1183 */ 1184 1185 } _SEH2_FINALLY { 1186 1187 if (bFcbAcquired) { 1188 ExReleaseResourceLite(&Fcb->MainResource); 1189 } 1190 1191 if (!IrpContext->ExceptionInProgress) { 1192 if (CompleteRequest) { 1193 if (Status == STATUS_PENDING) { 1194 Ext2QueueRequest(IrpContext); 1195 } else { 1196 Ext2CompleteIrpContext(IrpContext, Status); 1197 } 1198 } else { 1199 IrpContext->Irp = NULL; 1200 Ext2CompleteIrpContext(IrpContext, Status); 1201 } 1202 } 1203 } _SEH2_END; 1204 1205 return Status; 1206 } 1207 1208 VOID 1209 Ext2NotifyReportChange ( 1210 IN PEXT2_IRP_CONTEXT IrpContext, 1211 IN PEXT2_VCB Vcb, 1212 IN PEXT2_MCB Mcb, 1213 IN ULONG Filter, 1214 IN ULONG Action ) 1215 { 1216 USHORT Offset; 1217 1218 Offset = (USHORT) ( Mcb->FullName.Length - 1219 Mcb->ShortName.Length); 1220 1221 FsRtlNotifyFullReportChange( Vcb->NotifySync, 1222 &(Vcb->NotifyList), 1223 (PSTRING) (&Mcb->FullName), 1224 (USHORT) Offset, 1225 (PSTRING)NULL, 1226 (PSTRING) NULL, 1227 (ULONG) Filter, 1228 (ULONG) Action, 1229 (PVOID) NULL ); 1230 1231 // ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 1232 } 1233 1234 1235 NTSTATUS 1236 Ext2DirectoryControl (IN PEXT2_IRP_CONTEXT IrpContext) 1237 { 1238 NTSTATUS Status; 1239 1240 ASSERT(IrpContext); 1241 1242 ASSERT((IrpContext->Identifier.Type == EXT2ICX) && 1243 (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); 1244 1245 switch (IrpContext->MinorFunction) { 1246 1247 case IRP_MN_QUERY_DIRECTORY: 1248 Status = Ext2QueryDirectory(IrpContext); 1249 break; 1250 1251 case IRP_MN_NOTIFY_CHANGE_DIRECTORY: 1252 Status = Ext2NotifyChangeDirectory(IrpContext); 1253 break; 1254 1255 default: 1256 Status = STATUS_INVALID_DEVICE_REQUEST; 1257 Ext2CompleteIrpContext(IrpContext, Status); 1258 } 1259 1260 return Status; 1261 } 1262 1263 1264 BOOLEAN 1265 Ext2IsDirectoryEmpty ( 1266 PEXT2_IRP_CONTEXT IrpContext, 1267 PEXT2_VCB Vcb, 1268 PEXT2_MCB Mcb 1269 ) 1270 { 1271 if (!IsMcbDirectory(Mcb) || IsMcbSymLink(Mcb)) { 1272 return TRUE; 1273 } 1274 1275 return !!ext3_is_dir_empty(IrpContext, &Mcb->Inode); 1276 } 1277