3 * Copyright (C) 2002, 2003, 2014 ReactOS Team
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.
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.
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.
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)
28 /* INCLUDES *****************************************************************/
35 /* FUNCTIONS ****************************************************************/
38 * @name NtfsAddFilenameToDirectory
41 * Adds a $FILE_NAME attribute to a given directory index.
44 * Points to the target disk's DEVICE_EXTENSION.
46 * @param DirectoryMftIndex
47 * Mft index of the parent directory which will receive the file.
49 * @param FileReferenceNumber
50 * File reference of the file to be added to the directory. This is a combination of the
51 * Mft index and sequence number.
53 * @param FilenameAttribute
54 * Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
57 * STATUS_SUCCESS on success.
58 * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
59 * STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record.
62 * WIP - Can only support an empty directory.
63 * One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each
64 * file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will
65 * get both attributes added to its parent directory.
68 NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt
,
69 ULONGLONG DirectoryMftIndex
,
70 ULONGLONG FileReferenceNumber
,
71 PFILENAME_ATTRIBUTE FilenameAttribute
)
73 NTSTATUS Status
= STATUS_SUCCESS
;
74 PFILE_RECORD_HEADER ParentFileRecord
;
75 PNTFS_ATTR_CONTEXT IndexRootContext
;
76 PINDEX_ROOT_ATTRIBUTE I30IndexRoot
;
77 ULONG IndexRootOffset
;
78 ULONGLONG I30IndexRootLength
;
79 PINDEX_ENTRY_ATTRIBUTE IndexNodeEntry
;
81 PNTFS_ATTR_RECORD DestinationAttribute
;
82 PINDEX_ROOT_ATTRIBUTE NewIndexRoot
;
83 ULONG AttributeLength
;
84 PNTFS_ATTR_RECORD NextAttribute
;
86 // Allocate memory for the parent directory
87 ParentFileRecord
= ExAllocatePoolWithTag(NonPagedPool
,
88 DeviceExt
->NtfsInfo
.BytesPerFileRecord
,
90 if (!ParentFileRecord
)
92 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
93 return STATUS_INSUFFICIENT_RESOURCES
;
96 // Open the parent directory
97 Status
= ReadFileRecord(DeviceExt
, DirectoryMftIndex
, ParentFileRecord
);
98 if (!NT_SUCCESS(Status
))
100 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
101 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
106 DPRINT1("Dumping old parent file record:\n");
107 NtfsDumpFileRecord(DeviceExt
, ParentFileRecord
);
109 // Find the index root attribute for the directory
110 Status
= FindAttribute(DeviceExt
,
117 if (!NT_SUCCESS(Status
))
119 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
121 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
125 I30IndexRootLength
= AttributeDataLength(&IndexRootContext
->Record
);
127 // Allocate memory for the index root data
128 I30IndexRoot
= (PINDEX_ROOT_ATTRIBUTE
)ExAllocatePoolWithTag(NonPagedPool
, I30IndexRootLength
, TAG_NTFS
);
131 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
132 ReleaseAttributeContext(IndexRootContext
);
133 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
136 // Read the Index Root
137 Status
= ReadAttribute(DeviceExt
, IndexRootContext
, 0, (PCHAR
)I30IndexRoot
, I30IndexRootLength
);
138 if (!NT_SUCCESS(Status
))
140 DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex
);
141 ReleaseAttributeContext(IndexRootContext
);
142 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
143 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
147 // Make sure it's empty (temporarily)
148 IndexNodeEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)I30IndexRoot
+ I30IndexRoot
->Header
.FirstEntryOffset
+ 0x10);
149 if (IndexNodeEntry
->Data
.Directory
.IndexedFile
!= 0 || IndexNodeEntry
->Flags
!= 2)
151 DPRINT1("FIXME: File-creation is only supported in empty directories right now! Be patient! :)\n");
152 ReleaseAttributeContext(IndexRootContext
);
153 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
154 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
155 return STATUS_NOT_IMPLEMENTED
;
158 // Now we need to setup a new index root attribute to replace the old one
159 NewIndexRoot
= ExAllocatePoolWithTag(NonPagedPool
, DeviceExt
->NtfsInfo
.BytesPerIndexRecord
, TAG_NTFS
);
162 DPRINT1("ERROR: Unable to allocate memory for new index root attribute!\n");
163 ReleaseAttributeContext(IndexRootContext
);
164 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
165 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
166 return STATUS_INSUFFICIENT_RESOURCES
;
169 // Setup the new index record
170 RtlZeroMemory(NewIndexRoot
, DeviceExt
->NtfsInfo
.BytesPerIndexRecord
); // shouldn't be necessary but aids in debugging
172 NewIndexRoot
->AttributeType
= AttributeFileName
;
173 NewIndexRoot
->CollationRule
= COLLATION_FILE_NAME
;
174 NewIndexRoot
->SizeOfEntry
= DeviceExt
->NtfsInfo
.BytesPerIndexRecord
;
175 // If Bytes per index record is less than cluster size, clusters per index record becomes sectors per index
176 if(NewIndexRoot
->SizeOfEntry
< DeviceExt
->NtfsInfo
.BytesPerCluster
)
177 NewIndexRoot
->ClustersPerIndexRecord
= NewIndexRoot
->SizeOfEntry
/ DeviceExt
->NtfsInfo
.BytesPerSector
;
179 NewIndexRoot
->ClustersPerIndexRecord
= NewIndexRoot
->SizeOfEntry
/ DeviceExt
->NtfsInfo
.BytesPerCluster
;
181 // Setup the Index node header
182 NewIndexRoot
->Header
.FirstEntryOffset
= 0x10;
183 NewIndexRoot
->Header
.Flags
= INDEX_ROOT_SMALL
;
184 // still need to calculate sizes
186 // The first index node entry will be for the filename we're adding
187 IndexNodeEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)NewIndexRoot
+ NewIndexRoot
->Header
.FirstEntryOffset
+ 0x10);
188 IndexNodeEntry
->Data
.Directory
.IndexedFile
= FileReferenceNumber
;
189 IndexNodeEntry
->Flags
= INDEX_ROOT_SMALL
;
190 IndexNodeEntry
->KeyLength
= FIELD_OFFSET(FILENAME_ATTRIBUTE
, Name
) + (2 * FilenameAttribute
->NameLength
);
191 IndexNodeEntry
->Length
= ALIGN_UP_BY(IndexNodeEntry
->KeyLength
, 8) + FIELD_OFFSET(INDEX_ENTRY_ATTRIBUTE
, FileName
);
193 // Now we can calculate the Node length (temp logic)
194 NewIndexRoot
->Header
.TotalSizeOfEntries
= NewIndexRoot
->Header
.FirstEntryOffset
+ IndexNodeEntry
->Length
+ 0x10;
195 NewIndexRoot
->Header
.AllocatedSize
= NewIndexRoot
->Header
.TotalSizeOfEntries
;
197 DPRINT1("New Index Node Entry Stream Length: %u\nNew Inde Node Entry Length: %u\n",
198 IndexNodeEntry
->KeyLength
,
199 IndexNodeEntry
->Length
);
201 // copy over the attribute proper
202 RtlCopyMemory(&IndexNodeEntry
->FileName
, FilenameAttribute
, IndexNodeEntry
->KeyLength
);
204 // Now setup the dummy key
205 IndexNodeEntry
= (PINDEX_ENTRY_ATTRIBUTE
)((ULONG_PTR
)IndexNodeEntry
+ IndexNodeEntry
->Length
);
207 IndexNodeEntry
->Data
.Directory
.IndexedFile
= 0;
208 IndexNodeEntry
->Length
= 0x10;
209 IndexNodeEntry
->KeyLength
= 0;
210 IndexNodeEntry
->Flags
= NTFS_INDEX_ENTRY_END
;
212 // This is when we'd normally setup the length (already done above)
214 // Write back the new index root attribute to the parent directory file record
216 // First, we need to make sure the attribute is large enough.
217 // We can't set the size as we normally would, because if we extend past the file record,
218 // we must create an index allocation and index bitmap (TODO). Also TODO: support file records with
219 // $ATTRIBUTE_LIST's.
220 AttributeLength
= NewIndexRoot
->Header
.AllocatedSize
+ FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE
, Header
);
221 DestinationAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)ParentFileRecord
+ IndexRootOffset
);
223 NextAttribute
= (PNTFS_ATTR_RECORD
)((ULONG_PTR
)DestinationAttribute
+ DestinationAttribute
->Length
);
224 if (NextAttribute
->Type
!= AttributeEnd
)
226 DPRINT1("FIXME: For now, only resizing index root at the end of a file record is supported!\n");
227 ExFreePoolWithTag(NewIndexRoot
, TAG_NTFS
);
228 ReleaseAttributeContext(IndexRootContext
);
229 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
230 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
231 return STATUS_NOT_IMPLEMENTED
;
234 // Update the length of the attribute in the file record of the parent directory
235 InternalSetResidentAttributeLength(IndexRootContext
,
240 Status
= UpdateFileRecord(DeviceExt
, DirectoryMftIndex
, ParentFileRecord
);
241 if (!NT_SUCCESS(Status
))
243 DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex
);
244 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
245 ExFreePoolWithTag(NewIndexRoot
, TAG_NTFS
);
246 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
250 // Update the parent directory with the new index root
251 Status
= WriteAttribute(DeviceExt
,
254 (PUCHAR
)NewIndexRoot
,
257 if (!NT_SUCCESS(Status
) )
259 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
260 ExFreePoolWithTag(NewIndexRoot
, TAG_NTFS
);
261 ReleaseAttributeContext(IndexRootContext
);
262 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
263 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
267 // re-read the parent file record, so we can dump it
268 Status
= ReadFileRecord(DeviceExt
, DirectoryMftIndex
, ParentFileRecord
);
269 if (!NT_SUCCESS(Status
))
271 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
275 DPRINT1("Dumping new parent file record:\n");
276 NtfsDumpFileRecord(DeviceExt
, ParentFileRecord
);
280 ExFreePoolWithTag(NewIndexRoot
, TAG_NTFS
);
281 ReleaseAttributeContext(IndexRootContext
);
282 ExFreePoolWithTag(I30IndexRoot
, TAG_NTFS
);
283 ExFreePoolWithTag(ParentFileRecord
, TAG_NTFS
);
289 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt
,
290 PFILE_RECORD_HEADER FileRecord
,
293 PULONGLONG AllocatedSize
)
295 ULONGLONG Size
= 0ULL;
296 ULONGLONG Allocated
= 0ULL;
298 PNTFS_ATTR_CONTEXT DataContext
;
300 Status
= FindAttribute(DeviceExt
, FileRecord
, AttributeData
, Stream
, StreamLength
, &DataContext
, NULL
);
301 if (NT_SUCCESS(Status
))
303 Size
= AttributeDataLength(&DataContext
->Record
);
304 Allocated
= AttributeAllocatedLength(&DataContext
->Record
);
305 ReleaseAttributeContext(DataContext
);
308 if (AllocatedSize
!= NULL
) *AllocatedSize
= Allocated
;
315 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
316 PFILE_RECORD_HEADER FileRecord
,
318 PFILE_NAMES_INFORMATION Info
,
322 PFILENAME_ATTRIBUTE FileName
;
324 DPRINT("NtfsGetNameInformation() called\n");
326 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
327 if (FileName
== NULL
)
329 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
330 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
331 return STATUS_OBJECT_NAME_NOT_FOUND
;
334 Length
= FileName
->NameLength
* sizeof (WCHAR
);
335 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
336 return(STATUS_BUFFER_OVERFLOW
);
338 Info
->FileNameLength
= Length
;
339 Info
->NextEntryOffset
=
340 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
341 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
343 return(STATUS_SUCCESS
);
348 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
349 PFILE_RECORD_HEADER FileRecord
,
351 PFILE_DIRECTORY_INFORMATION Info
,
355 PFILENAME_ATTRIBUTE FileName
;
356 PSTANDARD_INFORMATION StdInfo
;
358 DPRINT("NtfsGetDirectoryInformation() called\n");
360 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
361 if (FileName
== NULL
)
363 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
364 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
365 return STATUS_OBJECT_NAME_NOT_FOUND
;
368 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
369 ASSERT(StdInfo
!= NULL
);
371 Length
= FileName
->NameLength
* sizeof (WCHAR
);
372 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
373 return(STATUS_BUFFER_OVERFLOW
);
375 Info
->FileNameLength
= Length
;
376 Info
->NextEntryOffset
=
377 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
378 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
380 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
381 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
382 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
383 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
385 /* Convert file flags */
386 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
388 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
390 Info
->FileIndex
= MFTIndex
;
392 return STATUS_SUCCESS
;
397 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
398 PFILE_RECORD_HEADER FileRecord
,
400 PFILE_FULL_DIRECTORY_INFORMATION Info
,
404 PFILENAME_ATTRIBUTE FileName
;
405 PSTANDARD_INFORMATION StdInfo
;
407 DPRINT("NtfsGetFullDirectoryInformation() called\n");
409 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
410 if (FileName
== NULL
)
412 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
413 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
414 return STATUS_OBJECT_NAME_NOT_FOUND
;
417 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
418 ASSERT(StdInfo
!= NULL
);
420 Length
= FileName
->NameLength
* sizeof (WCHAR
);
421 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
422 return(STATUS_BUFFER_OVERFLOW
);
424 Info
->FileNameLength
= Length
;
425 Info
->NextEntryOffset
=
426 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
427 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
429 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
430 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
431 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
432 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
434 /* Convert file flags */
435 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
437 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
439 Info
->FileIndex
= MFTIndex
;
442 return STATUS_SUCCESS
;
447 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
448 PFILE_RECORD_HEADER FileRecord
,
450 PFILE_BOTH_DIR_INFORMATION Info
,
454 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
455 PSTANDARD_INFORMATION StdInfo
;
457 DPRINT("NtfsGetBothDirectoryInformation() called\n");
459 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
460 if (FileName
== NULL
)
462 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
463 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
464 return STATUS_OBJECT_NAME_NOT_FOUND
;
466 ShortFileName
= GetFileNameFromRecord(DeviceExt
, FileRecord
, NTFS_FILE_NAME_DOS
);
468 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
469 ASSERT(StdInfo
!= NULL
);
471 Length
= FileName
->NameLength
* sizeof (WCHAR
);
472 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
473 return(STATUS_BUFFER_OVERFLOW
);
475 Info
->FileNameLength
= Length
;
476 Info
->NextEntryOffset
=
477 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
, sizeof(ULONG
));
478 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
482 /* Should we upcase the filename? */
483 ASSERT(ShortFileName
->NameLength
<= ARRAYSIZE(Info
->ShortName
));
484 Info
->ShortNameLength
= ShortFileName
->NameLength
* sizeof(WCHAR
);
485 RtlCopyMemory(Info
->ShortName
, ShortFileName
->Name
, Info
->ShortNameLength
);
489 Info
->ShortName
[0] = 0;
490 Info
->ShortNameLength
= 0;
493 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
494 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
495 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
496 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
498 /* Convert file flags */
499 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
501 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
503 Info
->FileIndex
= MFTIndex
;
506 return STATUS_SUCCESS
;
511 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
514 PDEVICE_OBJECT DeviceObject
;
515 PDEVICE_EXTENSION DeviceExtension
;
516 LONG BufferLength
= 0;
517 PUNICODE_STRING SearchPattern
= NULL
;
518 FILE_INFORMATION_CLASS FileInformationClass
;
520 PUCHAR Buffer
= NULL
;
521 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
524 BOOLEAN First
= FALSE
;
525 PIO_STACK_LOCATION Stack
;
526 PFILE_OBJECT FileObject
;
527 NTSTATUS Status
= STATUS_SUCCESS
;
528 PFILE_RECORD_HEADER FileRecord
;
529 ULONGLONG MFTRecord
, OldMFTRecord
= 0;
530 UNICODE_STRING Pattern
;
532 DPRINT1("NtfsQueryDirectory() called\n");
535 Irp
= IrpContext
->Irp
;
536 DeviceObject
= IrpContext
->DeviceObject
;
538 DeviceExtension
= DeviceObject
->DeviceExtension
;
539 Stack
= IoGetCurrentIrpStackLocation(Irp
);
540 FileObject
= Stack
->FileObject
;
542 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
543 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
545 /* Obtain the callers parameters */
546 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
547 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
548 FileInformationClass
= Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
549 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
551 if (NtfsFCBIsCompressed(Fcb
))
553 DPRINT1("Compressed directory!\n");
555 return STATUS_NOT_IMPLEMENTED
;
558 if (!ExAcquireResourceSharedLite(&Fcb
->MainResource
,
559 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
561 return STATUS_PENDING
;
564 if (SearchPattern
!= NULL
)
566 if (!Ccb
->DirectorySearchPattern
)
570 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
571 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
572 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
573 if (!Ccb
->DirectorySearchPattern
)
575 ExReleaseResourceLite(&Fcb
->MainResource
);
576 return STATUS_INSUFFICIENT_RESOURCES
;
579 memcpy(Ccb
->DirectorySearchPattern
, SearchPattern
->Buffer
, SearchPattern
->Length
);
580 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
583 else if (!Ccb
->DirectorySearchPattern
)
586 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
587 if (!Ccb
->DirectorySearchPattern
)
589 ExReleaseResourceLite(&Fcb
->MainResource
);
590 return STATUS_INSUFFICIENT_RESOURCES
;
593 Ccb
->DirectorySearchPattern
[0] = L
'*';
594 Ccb
->DirectorySearchPattern
[1] = 0;
597 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
598 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
599 DPRINT("In: '%S'\n", Fcb
->PathName
);
601 /* Determine directory index */
602 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
604 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
606 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
611 /* Get Buffer for result */
612 Buffer
= NtfsGetUserBuffer(Irp
, FALSE
);
614 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
616 if (!ExAcquireResourceExclusiveLite(&DeviceExtension
->DirResource
,
617 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
619 ExReleaseResourceLite(&Fcb
->MainResource
);
620 return STATUS_PENDING
;
623 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
625 Status
= NtfsFindFileAt(DeviceExtension
,
631 (Stack
->Flags
& SL_CASE_SENSITIVE
));
633 if (NT_SUCCESS(Status
))
635 /* HACK: files with both a short name and a long name are present twice in the index.
636 * Ignore the second entry, if it is immediately following the first one.
638 if (MFTRecord
== OldMFTRecord
)
640 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord
);
642 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
645 OldMFTRecord
= MFTRecord
;
647 switch (FileInformationClass
)
649 case FileNameInformation
:
650 Status
= NtfsGetNameInformation(DeviceExtension
,
653 (PFILE_NAMES_INFORMATION
)Buffer
,
657 case FileDirectoryInformation
:
658 Status
= NtfsGetDirectoryInformation(DeviceExtension
,
661 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
665 case FileFullDirectoryInformation
:
666 Status
= NtfsGetFullDirectoryInformation(DeviceExtension
,
669 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
673 case FileBothDirectoryInformation
:
674 Status
= NtfsGetBothDirectoryInformation(DeviceExtension
,
677 (PFILE_BOTH_DIR_INFORMATION
)Buffer
,
682 Status
= STATUS_INVALID_INFO_CLASS
;
685 if (Status
== STATUS_BUFFER_OVERFLOW
)
689 Buffer0
->NextEntryOffset
= 0;
698 Buffer0
->NextEntryOffset
= 0;
703 Status
= STATUS_NO_SUCH_FILE
;
707 Status
= STATUS_NO_MORE_FILES
;
712 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
713 Buffer0
->FileIndex
= FileIndex
++;
716 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
720 BufferLength
-= Buffer0
->NextEntryOffset
;
721 Buffer
+= Buffer0
->NextEntryOffset
;
722 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
727 Buffer0
->NextEntryOffset
= 0;
730 ExReleaseResourceLite(&DeviceExtension
->DirResource
);
731 ExReleaseResourceLite(&Fcb
->MainResource
);
735 Status
= STATUS_SUCCESS
;
743 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext
)
745 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
747 DPRINT1("NtfsDirectoryControl() called\n");
749 switch (IrpContext
->MinorFunction
)
751 case IRP_MN_QUERY_DIRECTORY
:
752 Status
= NtfsQueryDirectory(IrpContext
);
755 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
756 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
757 Status
= STATUS_NOT_IMPLEMENTED
;
761 Status
= STATUS_INVALID_DEVICE_REQUEST
;
765 if (Status
== STATUS_PENDING
&& IrpContext
->Flags
& IRPCONTEXT_COMPLETE
)
767 return NtfsMarkIrpContextForQueue(IrpContext
);
770 IrpContext
->Irp
->IoStatus
.Information
= 0;