xref: /reactos/drivers/filesystems/ntfs/dirctl.c (revision da5f10af)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2003, 2014 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * COPYRIGHT:        See COPYING in the top level directory
20  * PROJECT:          ReactOS kernel
21  * FILE:             drivers/filesystem/ntfs/dirctl.c
22  * PURPOSE:          NTFS filesystem driver
23  * PROGRAMMERS:      Eric Kohl
24  *                   Pierre Schweitzer (pierre@reactos.org)
25  *                   Hervé Poussineau (hpoussin@reactos.org)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "ntfs.h"
31 
32 #define NDEBUG
33 #include <debug.h>
34 
35 /* FUNCTIONS ****************************************************************/
36 
37 ULONGLONG
38 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
39                 PFILE_RECORD_HEADER FileRecord,
40                 PCWSTR Stream,
41                 ULONG StreamLength,
42                 PULONGLONG AllocatedSize)
43 {
44     ULONGLONG Size = 0ULL;
45     ULONGLONG Allocated = 0ULL;
46     NTSTATUS Status;
47     PNTFS_ATTR_CONTEXT DataContext;
48 
49     Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
50     if (NT_SUCCESS(Status))
51     {
52         Size = AttributeDataLength(DataContext->pRecord);
53         Allocated = AttributeAllocatedLength(DataContext->pRecord);
54         ReleaseAttributeContext(DataContext);
55     }
56 
57     if (AllocatedSize != NULL) *AllocatedSize = Allocated;
58 
59     return Size;
60 }
61 
62 
63 #define ULONG_ROUND_UP(x)   ROUND_UP((x), (sizeof(ULONG)))
64 
65 
66 static NTSTATUS
67 NtfsGetNamesInformation(PDEVICE_EXTENSION DeviceExt,
68                         PFILE_RECORD_HEADER FileRecord,
69                         ULONGLONG MFTIndex,
70                         PFILE_NAMES_INFORMATION Info,
71                         ULONG BufferLength,
72                         PULONG Written,
73                         BOOLEAN First)
74 {
75     ULONG Length;
76     NTSTATUS Status;
77     ULONG BytesToCopy = 0;
78     PFILENAME_ATTRIBUTE FileName;
79 
80     DPRINT("NtfsGetNamesInformation() called\n");
81 
82     *Written = 0;
83     Status = STATUS_BUFFER_OVERFLOW;
84     if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
85     {
86         return Status;
87     }
88 
89     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
90     if (FileName == NULL)
91     {
92         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
93         NtfsDumpFileAttributes(DeviceExt, FileRecord);
94         return STATUS_OBJECT_NAME_NOT_FOUND;
95     }
96 
97     Length = FileName->NameLength * sizeof (WCHAR);
98     if (First || (BufferLength >= FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + Length))
99     {
100         Info->FileNameLength = Length;
101 
102         *Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
103         Info->NextEntryOffset = 0;
104         if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
105         {
106             BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
107             RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
108             *Written += BytesToCopy;
109 
110             if (BytesToCopy == Length)
111             {
112                 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
113                                                        BytesToCopy);
114                 Status = STATUS_SUCCESS;
115             }
116         }
117     }
118 
119     return Status;
120 }
121 
122 
123 static NTSTATUS
124 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
125                             PFILE_RECORD_HEADER FileRecord,
126                             ULONGLONG MFTIndex,
127                             PFILE_DIRECTORY_INFORMATION Info,
128                             ULONG BufferLength,
129                             PULONG Written,
130                             BOOLEAN First)
131 {
132     ULONG Length;
133     NTSTATUS Status;
134     ULONG BytesToCopy = 0;
135     PFILENAME_ATTRIBUTE FileName;
136     PSTANDARD_INFORMATION StdInfo;
137 
138     DPRINT("NtfsGetDirectoryInformation() called\n");
139 
140     *Written = 0;
141     Status = STATUS_BUFFER_OVERFLOW;
142     if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
143     {
144         return Status;
145     }
146 
147     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
148     if (FileName == NULL)
149     {
150         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
151         NtfsDumpFileAttributes(DeviceExt, FileRecord);
152         return STATUS_OBJECT_NAME_NOT_FOUND;
153     }
154 
155     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
156     ASSERT(StdInfo != NULL);
157 
158     Length = FileName->NameLength * sizeof (WCHAR);
159     if (First || (BufferLength >= FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + Length))
160     {
161         Info->FileNameLength = Length;
162 
163         *Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
164         Info->NextEntryOffset = 0;
165         if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
166         {
167             BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
168             RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
169             *Written += BytesToCopy;
170 
171             if (BytesToCopy == Length)
172             {
173                 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
174                                                        BytesToCopy);
175                 Status = STATUS_SUCCESS;
176             }
177         }
178 
179         Info->CreationTime.QuadPart = FileName->CreationTime;
180         Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
181         Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
182         Info->ChangeTime.QuadPart = FileName->ChangeTime;
183 
184         /* Convert file flags */
185         NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
186 
187         Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
188 
189         Info->FileIndex = MFTIndex;
190     }
191 
192     return Status;
193 }
194 
195 
196 static NTSTATUS
197 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
198                                 PFILE_RECORD_HEADER FileRecord,
199                                 ULONGLONG MFTIndex,
200                                 PFILE_FULL_DIRECTORY_INFORMATION Info,
201                                 ULONG BufferLength,
202                                 PULONG Written,
203                                 BOOLEAN First)
204 {
205     ULONG Length;
206     NTSTATUS Status;
207     ULONG BytesToCopy = 0;
208     PFILENAME_ATTRIBUTE FileName;
209     PSTANDARD_INFORMATION StdInfo;
210 
211     DPRINT("NtfsGetFullDirectoryInformation() called\n");
212 
213     *Written = 0;
214     Status = STATUS_BUFFER_OVERFLOW;
215     if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
216     {
217         return Status;
218     }
219 
220     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
221     if (FileName == NULL)
222     {
223         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
224         NtfsDumpFileAttributes(DeviceExt, FileRecord);
225         return STATUS_OBJECT_NAME_NOT_FOUND;
226     }
227 
228     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
229     ASSERT(StdInfo != NULL);
230 
231     Length = FileName->NameLength * sizeof (WCHAR);
232     if (First || (BufferLength >= FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + Length))
233     {
234         Info->FileNameLength = Length;
235 
236         *Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
237         Info->NextEntryOffset = 0;
238         if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
239         {
240             BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
241             RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
242             *Written += BytesToCopy;
243 
244             if (BytesToCopy == Length)
245             {
246                 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
247                                                        BytesToCopy);
248                 Status = STATUS_SUCCESS;
249             }
250         }
251 
252         Info->CreationTime.QuadPart = FileName->CreationTime;
253         Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
254         Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
255         Info->ChangeTime.QuadPart = FileName->ChangeTime;
256 
257         /* Convert file flags */
258         NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
259 
260         Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
261 
262         Info->FileIndex = MFTIndex;
263         Info->EaSize = 0;
264     }
265 
266     return Status;
267 }
268 
269 
270 static NTSTATUS
271 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
272                                 PFILE_RECORD_HEADER FileRecord,
273                                 ULONGLONG MFTIndex,
274                                 PFILE_BOTH_DIR_INFORMATION Info,
275                                 ULONG BufferLength,
276                                 PULONG Written,
277                                 BOOLEAN First)
278 {
279     ULONG Length;
280     NTSTATUS Status;
281     ULONG BytesToCopy = 0;
282     PFILENAME_ATTRIBUTE FileName, ShortFileName;
283     PSTANDARD_INFORMATION StdInfo;
284 
285     DPRINT("NtfsGetBothDirectoryInformation() called\n");
286 
287     *Written = 0;
288     Status = STATUS_BUFFER_OVERFLOW;
289     if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
290     {
291         return Status;
292     }
293 
294     FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
295     if (FileName == NULL)
296     {
297         DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
298         NtfsDumpFileAttributes(DeviceExt, FileRecord);
299         return STATUS_OBJECT_NAME_NOT_FOUND;
300     }
301     ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
302 
303     StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
304     ASSERT(StdInfo != NULL);
305 
306     Length = FileName->NameLength * sizeof (WCHAR);
307     if (First || (BufferLength >= FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + Length))
308     {
309         Info->FileNameLength = Length;
310 
311         *Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
312         Info->NextEntryOffset = 0;
313         if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
314         {
315             BytesToCopy = min(Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
316             RtlCopyMemory(Info->FileName, FileName->Name, BytesToCopy);
317             *Written += BytesToCopy;
318 
319             if (BytesToCopy == Length)
320             {
321                 Info->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
322                                                        BytesToCopy);
323                 Status = STATUS_SUCCESS;
324             }
325         }
326 
327         if (ShortFileName)
328         {
329             /* Should we upcase the filename? */
330             ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
331             Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
332             RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
333         }
334         else
335         {
336             Info->ShortName[0] = 0;
337             Info->ShortNameLength = 0;
338         }
339 
340         Info->CreationTime.QuadPart = FileName->CreationTime;
341         Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
342         Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
343         Info->ChangeTime.QuadPart = FileName->ChangeTime;
344 
345         /* Convert file flags */
346         NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
347 
348         Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
349 
350         Info->FileIndex = MFTIndex;
351         Info->EaSize = 0;
352     }
353 
354     return Status;
355 }
356 
357 
358 NTSTATUS
359 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
360 {
361     PIRP Irp;
362     PDEVICE_OBJECT DeviceObject;
363     PDEVICE_EXTENSION DeviceExtension;
364     LONG BufferLength = 0;
365     PUNICODE_STRING SearchPattern = NULL;
366     FILE_INFORMATION_CLASS FileInformationClass;
367     ULONG FileIndex = 0;
368     PUCHAR Buffer = NULL;
369     PFILE_NAMES_INFORMATION Buffer0 = NULL;
370     PNTFS_FCB Fcb;
371     PNTFS_CCB Ccb;
372     BOOLEAN First = FALSE;
373     PIO_STACK_LOCATION Stack;
374     PFILE_OBJECT FileObject;
375     NTSTATUS Status = STATUS_SUCCESS;
376     PFILE_RECORD_HEADER FileRecord;
377     ULONGLONG MFTRecord, OldMFTRecord = 0;
378     UNICODE_STRING Pattern;
379     ULONG Written;
380 
381     DPRINT1("NtfsQueryDirectory() called\n");
382 
383     ASSERT(IrpContext);
384     Irp = IrpContext->Irp;
385     DeviceObject = IrpContext->DeviceObject;
386 
387     DeviceExtension = DeviceObject->DeviceExtension;
388     Stack = IoGetCurrentIrpStackLocation(Irp);
389     FileObject = Stack->FileObject;
390 
391     Ccb = (PNTFS_CCB)FileObject->FsContext2;
392     Fcb = (PNTFS_FCB)FileObject->FsContext;
393 
394     /* Obtain the callers parameters */
395     BufferLength = Stack->Parameters.QueryDirectory.Length;
396     SearchPattern = Stack->Parameters.QueryDirectory.FileName;
397     FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
398     FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
399 
400     if (NtfsFCBIsCompressed(Fcb))
401     {
402         DPRINT1("Compressed directory!\n");
403         UNIMPLEMENTED;
404         return STATUS_NOT_IMPLEMENTED;
405     }
406 
407     if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
408                                      BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
409     {
410         return STATUS_PENDING;
411     }
412 
413     if (SearchPattern != NULL)
414     {
415         if (!Ccb->DirectorySearchPattern)
416         {
417             First = TRUE;
418             Pattern.Length = 0;
419             Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
420             Ccb->DirectorySearchPattern = Pattern.Buffer =
421                 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
422             if (!Ccb->DirectorySearchPattern)
423             {
424                 ExReleaseResourceLite(&Fcb->MainResource);
425                 return STATUS_INSUFFICIENT_RESOURCES;
426             }
427 
428             memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
429             Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
430         }
431     }
432     else if (!Ccb->DirectorySearchPattern)
433     {
434         First = TRUE;
435         Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
436         if (!Ccb->DirectorySearchPattern)
437         {
438             ExReleaseResourceLite(&Fcb->MainResource);
439             return STATUS_INSUFFICIENT_RESOURCES;
440         }
441 
442         Ccb->DirectorySearchPattern[0] = L'*';
443         Ccb->DirectorySearchPattern[1] = 0;
444     }
445 
446     RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
447     DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
448     DPRINT("In: '%S'\n", Fcb->PathName);
449 
450     /* Determine directory index */
451     if (Stack->Flags & SL_INDEX_SPECIFIED)
452     {
453         Ccb->Entry = FileIndex;
454     }
455     else if (First || (Stack->Flags & SL_RESTART_SCAN))
456     {
457         Ccb->Entry = 0;
458     }
459 
460     /* Get Buffer for result */
461     Buffer = NtfsGetUserBuffer(Irp, FALSE);
462 
463     DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
464 
465     if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
466                                         BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
467     {
468         ExReleaseResourceLite(&Fcb->MainResource);
469         return STATUS_PENDING;
470     }
471 
472     Written = 0;
473     while (Status == STATUS_SUCCESS && BufferLength > 0)
474     {
475         Status = NtfsFindFileAt(DeviceExtension,
476                                 &Pattern,
477                                 &Ccb->Entry,
478                                 &FileRecord,
479                                 &MFTRecord,
480                                 Fcb->MFTIndex,
481                                 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE));
482 
483         if (NT_SUCCESS(Status))
484         {
485             /* HACK: files with both a short name and a long name are present twice in the index.
486              * Ignore the second entry, if it is immediately following the first one.
487              */
488             if (MFTRecord == OldMFTRecord)
489             {
490                 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
491                 Ccb->Entry++;
492                 ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
493                 continue;
494             }
495             OldMFTRecord = MFTRecord;
496 
497             switch (FileInformationClass)
498             {
499                 case FileNamesInformation:
500                     Status = NtfsGetNamesInformation(DeviceExtension,
501                                                      FileRecord,
502                                                      MFTRecord,
503                                                      (PFILE_NAMES_INFORMATION)Buffer,
504                                                      BufferLength,
505                                                      &Written,
506                                                      Buffer0 == NULL);
507                     break;
508 
509                 case FileDirectoryInformation:
510                     Status = NtfsGetDirectoryInformation(DeviceExtension,
511                                                          FileRecord,
512                                                          MFTRecord,
513                                                          (PFILE_DIRECTORY_INFORMATION)Buffer,
514                                                          BufferLength,
515                                                          &Written,
516                                                          Buffer0 == NULL);
517                     break;
518 
519                 case FileFullDirectoryInformation:
520                     Status = NtfsGetFullDirectoryInformation(DeviceExtension,
521                                                              FileRecord,
522                                                              MFTRecord,
523                                                              (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
524                                                              BufferLength,
525                                                              &Written,
526                                                              Buffer0 == NULL);
527                     break;
528 
529                 case FileBothDirectoryInformation:
530                     Status = NtfsGetBothDirectoryInformation(DeviceExtension,
531                                                              FileRecord,
532                                                              MFTRecord,
533                                                              (PFILE_BOTH_DIR_INFORMATION)Buffer,
534                                                              BufferLength,
535                                                              &Written,
536                                                              Buffer0 == NULL);
537                     break;
538 
539                 default:
540                     Status = STATUS_INVALID_INFO_CLASS;
541             }
542 
543             if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
544             {
545                 break;
546             }
547         }
548         else
549         {
550             Status = (First ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
551             break;
552         }
553 
554         Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
555         Buffer0->FileIndex = FileIndex++;
556         Ccb->Entry++;
557         BufferLength -= Buffer0->NextEntryOffset;
558 
559         ExFreeToNPagedLookasideList(&DeviceExtension->FileRecLookasideList, FileRecord);
560 
561         if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
562         {
563             break;
564         }
565 
566         Buffer += Buffer0->NextEntryOffset;
567     }
568 
569     if (Buffer0)
570     {
571         Buffer0->NextEntryOffset = 0;
572         Status = STATUS_SUCCESS;
573         IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
574     }
575     else
576     {
577         ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
578         ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
579         IrpContext->Irp->IoStatus.Information = Written;
580     }
581 
582     ExReleaseResourceLite(&DeviceExtension->DirResource);
583     ExReleaseResourceLite(&Fcb->MainResource);
584 
585     return Status;
586 }
587 
588 
589 NTSTATUS
590 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
591 {
592     NTSTATUS Status = STATUS_UNSUCCESSFUL;
593 
594     DPRINT1("NtfsDirectoryControl() called\n");
595 
596     switch (IrpContext->MinorFunction)
597     {
598         case IRP_MN_QUERY_DIRECTORY:
599             Status = NtfsQueryDirectory(IrpContext);
600             break;
601 
602         case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
603             DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
604             Status = STATUS_NOT_IMPLEMENTED;
605             break;
606 
607         default:
608             Status = STATUS_INVALID_DEVICE_REQUEST;
609             break;
610     }
611 
612     if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
613     {
614         return NtfsMarkIrpContextForQueue(IrpContext);
615     }
616 
617     IrpContext->Irp->IoStatus.Information = 0;
618 
619     return Status;
620 }
621 
622 /* EOF */
623