[EXT2] Upgrade to 0.69
[reactos.git] / drivers / filesystems / ext2 / src / dirctl.c
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 }