xref: /reactos/drivers/filesystems/ext2/src/dirctl.c (revision a1d7e993)
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
Ext2GetInfoLength(IN FILE_INFORMATION_CLASS FileInformationClass)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
Ext2ProcessEntry(IN PEXT2_IRP_CONTEXT IrpContext,IN PEXT2_VCB Vcb,IN PEXT2_FCB Dcb,IN FILE_INFORMATION_CLASS FileInformationClass,IN ULONG in,IN PVOID Buffer,IN ULONG UsedLength,IN ULONG Length,IN ULONG FileIndex,IN PUNICODE_STRING pName,OUT PULONG EntrySize,IN BOOLEAN Single)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
Ext2IsWearingCloak(IN PEXT2_VCB Vcb,IN POEM_STRING OemName)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 
Ext2FillEntry(void * context,const char * name,int namlen,ULONG offset,__u32 ino,unsigned int d_type)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
Ext2QueryDirectory(IN PEXT2_IRP_CONTEXT IrpContext)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
Ext2NotifyChangeDirectory(IN PEXT2_IRP_CONTEXT IrpContext)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
Ext2NotifyReportChange(IN PEXT2_IRP_CONTEXT IrpContext,IN PEXT2_VCB Vcb,IN PEXT2_MCB Mcb,IN ULONG Filter,IN ULONG Action)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
Ext2DirectoryControl(IN PEXT2_IRP_CONTEXT IrpContext)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
Ext2IsDirectoryEmpty(PEXT2_IRP_CONTEXT IrpContext,PEXT2_VCB Vcb,PEXT2_MCB Mcb)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