ba7231dc8078129942c901472e26b9fdfa7b413c
[reactos.git] / drivers / filesystems / ntfs / dirctl.c
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 /**
38 * @name NtfsAddFilenameToDirectory
39 * @implemented
40 *
41 * Adds a $FILE_NAME attribute to a given directory index.
42 *
43 * @param DeviceExt
44 * Points to the target disk's DEVICE_EXTENSION.
45 *
46 * @param DirectoryMftIndex
47 * Mft index of the parent directory which will receive the file.
48 *
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.
52 *
53 * @param FilenameAttribute
54 * Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
55 *
56 * @param CaseSensitive
57 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
58 * if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
59 *
60 * @return
61 * STATUS_SUCCESS on success.
62 * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
63 * STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record.
64 *
65 * @remarks
66 * WIP - Can only support a few files in a directory.
67 * One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each
68 * file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will
69 * get both attributes added to its parent directory.
70 */
71 NTSTATUS
72 NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
73 ULONGLONG DirectoryMftIndex,
74 ULONGLONG FileReferenceNumber,
75 PFILENAME_ATTRIBUTE FilenameAttribute,
76 BOOLEAN CaseSensitive)
77 {
78 NTSTATUS Status = STATUS_SUCCESS;
79 PFILE_RECORD_HEADER ParentFileRecord;
80 PNTFS_ATTR_CONTEXT IndexRootContext;
81 PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
82 ULONG IndexRootOffset;
83 ULONGLONG I30IndexRootLength;
84 ULONG LengthWritten;
85 PNTFS_ATTR_RECORD DestinationAttribute;
86 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
87 ULONG AttributeLength;
88 PNTFS_ATTR_RECORD NextAttribute;
89 PB_TREE NewTree;
90 ULONG BtreeIndexLength;
91 ULONG MaxIndexSize;
92
93 // Allocate memory for the parent directory
94 ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool,
95 DeviceExt->NtfsInfo.BytesPerFileRecord,
96 TAG_NTFS);
97 if (!ParentFileRecord)
98 {
99 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
100 return STATUS_INSUFFICIENT_RESOURCES;
101 }
102
103 // Open the parent directory
104 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
105 if (!NT_SUCCESS(Status))
106 {
107 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
108 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
109 DirectoryMftIndex);
110 return Status;
111 }
112
113 DPRINT1("Dumping old parent file record:\n");
114 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
115
116 // Find the index root attribute for the directory
117 Status = FindAttribute(DeviceExt,
118 ParentFileRecord,
119 AttributeIndexRoot,
120 L"$I30",
121 4,
122 &IndexRootContext,
123 &IndexRootOffset);
124 if (!NT_SUCCESS(Status))
125 {
126 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
127 DirectoryMftIndex);
128 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
129 return Status;
130 }
131
132 // Find the maximum index size given what the file record can hold
133 MaxIndexSize = DeviceExt->NtfsInfo.BytesPerFileRecord
134 - IndexRootOffset
135 - IndexRootContext->Record.Resident.ValueOffset
136 - FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
137 - (sizeof(ULONG) * 2);
138
139 // Allocate memory for the index root data
140 I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record);
141 I30IndexRoot = (PINDEX_ROOT_ATTRIBUTE)ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
142 if (!I30IndexRoot)
143 {
144 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
145 ReleaseAttributeContext(IndexRootContext);
146 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
147 }
148
149 // Read the Index Root
150 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
151 if (!NT_SUCCESS(Status))
152 {
153 DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
154 ReleaseAttributeContext(IndexRootContext);
155 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
156 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
157 return Status;
158 }
159
160 // Convert the index to a B*Tree
161 Status = CreateBTreeFromIndex(IndexRootContext, I30IndexRoot, &NewTree);
162 if (!NT_SUCCESS(Status))
163 {
164 DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
165 ReleaseAttributeContext(IndexRootContext);
166 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
167 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
168 return Status;
169 }
170
171 DumpBTree(NewTree);
172
173 // Insert the key for the file we're adding
174 Status = NtfsInsertKey(FileReferenceNumber, FilenameAttribute, NewTree->RootNode, CaseSensitive);
175 if (!NT_SUCCESS(Status))
176 {
177 DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
178 DestroyBTree(NewTree);
179 ReleaseAttributeContext(IndexRootContext);
180 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
181 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
182 return Status;
183 }
184
185 DumpBTree(NewTree);
186
187 // Convert B*Tree back to Index Root
188 Status = CreateIndexRootFromBTree(DeviceExt, NewTree, MaxIndexSize, &NewIndexRoot, &BtreeIndexLength);
189 if (!NT_SUCCESS(Status))
190 {
191 DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
192 DestroyBTree(NewTree);
193 ReleaseAttributeContext(IndexRootContext);
194 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
195 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
196 return Status;
197 }
198
199 // We're done with the B-Tree now
200 DestroyBTree(NewTree);
201
202 // Write back the new index root attribute to the parent directory file record
203
204 // First, we need to resize the attribute.
205 // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
206 // We can't set the size as we normally would, because if we extend past the file record,
207 // we must create an index allocation and index bitmap (TODO). Also TODO: support file records with
208 // $ATTRIBUTE_LIST's.
209 AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
210 DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset);
211
212 // Find the attribute (or attribute-end marker) after the index root
213 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
214 if (NextAttribute->Type != AttributeEnd)
215 {
216 DPRINT1("FIXME: For now, only resizing index root at the end of a file record is supported!\n");
217 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
218 ReleaseAttributeContext(IndexRootContext);
219 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
220 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
221 return STATUS_NOT_IMPLEMENTED;
222 }
223
224 // Update the length of the attribute in the file record of the parent directory
225 InternalSetResidentAttributeLength(IndexRootContext,
226 ParentFileRecord,
227 IndexRootOffset,
228 AttributeLength);
229
230 NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
231
232 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
233 if (!NT_SUCCESS(Status))
234 {
235 DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
236 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
237 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
238 ReleaseAttributeContext(IndexRootContext);
239 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
240 return Status;
241 }
242
243 // Write the new index root to disk
244 Status = WriteAttribute(DeviceExt,
245 IndexRootContext,
246 0,
247 (PUCHAR)NewIndexRoot,
248 AttributeLength,
249 &LengthWritten);
250 if (!NT_SUCCESS(Status) )
251 {
252 DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
253 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
254 ReleaseAttributeContext(IndexRootContext);
255 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
256 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
257 return Status;
258 }
259
260 // re-read the parent file record, so we can dump it
261 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
262 if (!NT_SUCCESS(Status))
263 {
264 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
265 }
266 else
267 {
268 DPRINT1("Dumping new parent file record:\n");
269 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
270 }
271
272 // Cleanup
273 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
274 ReleaseAttributeContext(IndexRootContext);
275 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
276 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
277
278 return Status;
279 }
280
281 ULONGLONG
282 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
283 PFILE_RECORD_HEADER FileRecord,
284 PCWSTR Stream,
285 ULONG StreamLength,
286 PULONGLONG AllocatedSize)
287 {
288 ULONGLONG Size = 0ULL;
289 ULONGLONG Allocated = 0ULL;
290 NTSTATUS Status;
291 PNTFS_ATTR_CONTEXT DataContext;
292
293 Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
294 if (NT_SUCCESS(Status))
295 {
296 Size = AttributeDataLength(&DataContext->Record);
297 Allocated = AttributeAllocatedLength(&DataContext->Record);
298 ReleaseAttributeContext(DataContext);
299 }
300
301 if (AllocatedSize != NULL) *AllocatedSize = Allocated;
302
303 return Size;
304 }
305
306
307 static NTSTATUS
308 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
309 PFILE_RECORD_HEADER FileRecord,
310 ULONGLONG MFTIndex,
311 PFILE_NAMES_INFORMATION Info,
312 ULONG BufferLength)
313 {
314 ULONG Length;
315 PFILENAME_ATTRIBUTE FileName;
316
317 DPRINT("NtfsGetNameInformation() called\n");
318
319 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
320 if (FileName == NULL)
321 {
322 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
323 NtfsDumpFileAttributes(DeviceExt, FileRecord);
324 return STATUS_OBJECT_NAME_NOT_FOUND;
325 }
326
327 Length = FileName->NameLength * sizeof (WCHAR);
328 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
329 return(STATUS_BUFFER_OVERFLOW);
330
331 Info->FileNameLength = Length;
332 Info->NextEntryOffset =
333 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
334 RtlCopyMemory(Info->FileName, FileName->Name, Length);
335
336 return(STATUS_SUCCESS);
337 }
338
339
340 static NTSTATUS
341 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
342 PFILE_RECORD_HEADER FileRecord,
343 ULONGLONG MFTIndex,
344 PFILE_DIRECTORY_INFORMATION Info,
345 ULONG BufferLength)
346 {
347 ULONG Length;
348 PFILENAME_ATTRIBUTE FileName;
349 PSTANDARD_INFORMATION StdInfo;
350
351 DPRINT("NtfsGetDirectoryInformation() called\n");
352
353 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
354 if (FileName == NULL)
355 {
356 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
357 NtfsDumpFileAttributes(DeviceExt, FileRecord);
358 return STATUS_OBJECT_NAME_NOT_FOUND;
359 }
360
361 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
362 ASSERT(StdInfo != NULL);
363
364 Length = FileName->NameLength * sizeof (WCHAR);
365 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
366 return(STATUS_BUFFER_OVERFLOW);
367
368 Info->FileNameLength = Length;
369 Info->NextEntryOffset =
370 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
371 RtlCopyMemory(Info->FileName, FileName->Name, Length);
372
373 Info->CreationTime.QuadPart = FileName->CreationTime;
374 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
375 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
376 Info->ChangeTime.QuadPart = FileName->ChangeTime;
377
378 /* Convert file flags */
379 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
380
381 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
382
383 Info->FileIndex = MFTIndex;
384
385 return STATUS_SUCCESS;
386 }
387
388
389 static NTSTATUS
390 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
391 PFILE_RECORD_HEADER FileRecord,
392 ULONGLONG MFTIndex,
393 PFILE_FULL_DIRECTORY_INFORMATION Info,
394 ULONG BufferLength)
395 {
396 ULONG Length;
397 PFILENAME_ATTRIBUTE FileName;
398 PSTANDARD_INFORMATION StdInfo;
399
400 DPRINT("NtfsGetFullDirectoryInformation() called\n");
401
402 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
403 if (FileName == NULL)
404 {
405 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
406 NtfsDumpFileAttributes(DeviceExt, FileRecord);
407 return STATUS_OBJECT_NAME_NOT_FOUND;
408 }
409
410 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
411 ASSERT(StdInfo != NULL);
412
413 Length = FileName->NameLength * sizeof (WCHAR);
414 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
415 return(STATUS_BUFFER_OVERFLOW);
416
417 Info->FileNameLength = Length;
418 Info->NextEntryOffset =
419 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
420 RtlCopyMemory(Info->FileName, FileName->Name, Length);
421
422 Info->CreationTime.QuadPart = FileName->CreationTime;
423 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
424 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
425 Info->ChangeTime.QuadPart = FileName->ChangeTime;
426
427 /* Convert file flags */
428 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
429
430 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
431
432 Info->FileIndex = MFTIndex;
433 Info->EaSize = 0;
434
435 return STATUS_SUCCESS;
436 }
437
438
439 static NTSTATUS
440 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
441 PFILE_RECORD_HEADER FileRecord,
442 ULONGLONG MFTIndex,
443 PFILE_BOTH_DIR_INFORMATION Info,
444 ULONG BufferLength)
445 {
446 ULONG Length;
447 PFILENAME_ATTRIBUTE FileName, ShortFileName;
448 PSTANDARD_INFORMATION StdInfo;
449
450 DPRINT("NtfsGetBothDirectoryInformation() called\n");
451
452 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
453 if (FileName == NULL)
454 {
455 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
456 NtfsDumpFileAttributes(DeviceExt, FileRecord);
457 return STATUS_OBJECT_NAME_NOT_FOUND;
458 }
459 ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
460
461 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
462 ASSERT(StdInfo != NULL);
463
464 Length = FileName->NameLength * sizeof (WCHAR);
465 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
466 return(STATUS_BUFFER_OVERFLOW);
467
468 Info->FileNameLength = Length;
469 Info->NextEntryOffset =
470 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) + Length, sizeof(ULONG));
471 RtlCopyMemory(Info->FileName, FileName->Name, Length);
472
473 if (ShortFileName)
474 {
475 /* Should we upcase the filename? */
476 ASSERT(ShortFileName->NameLength <= ARRAYSIZE(Info->ShortName));
477 Info->ShortNameLength = ShortFileName->NameLength * sizeof(WCHAR);
478 RtlCopyMemory(Info->ShortName, ShortFileName->Name, Info->ShortNameLength);
479 }
480 else
481 {
482 Info->ShortName[0] = 0;
483 Info->ShortNameLength = 0;
484 }
485
486 Info->CreationTime.QuadPart = FileName->CreationTime;
487 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
488 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
489 Info->ChangeTime.QuadPart = FileName->ChangeTime;
490
491 /* Convert file flags */
492 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
493
494 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
495
496 Info->FileIndex = MFTIndex;
497 Info->EaSize = 0;
498
499 return STATUS_SUCCESS;
500 }
501
502
503 NTSTATUS
504 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
505 {
506 PIRP Irp;
507 PDEVICE_OBJECT DeviceObject;
508 PDEVICE_EXTENSION DeviceExtension;
509 LONG BufferLength = 0;
510 PUNICODE_STRING SearchPattern = NULL;
511 FILE_INFORMATION_CLASS FileInformationClass;
512 ULONG FileIndex = 0;
513 PUCHAR Buffer = NULL;
514 PFILE_NAMES_INFORMATION Buffer0 = NULL;
515 PNTFS_FCB Fcb;
516 PNTFS_CCB Ccb;
517 BOOLEAN First = FALSE;
518 PIO_STACK_LOCATION Stack;
519 PFILE_OBJECT FileObject;
520 NTSTATUS Status = STATUS_SUCCESS;
521 PFILE_RECORD_HEADER FileRecord;
522 ULONGLONG MFTRecord, OldMFTRecord = 0;
523 UNICODE_STRING Pattern;
524
525 DPRINT1("NtfsQueryDirectory() called\n");
526
527 ASSERT(IrpContext);
528 Irp = IrpContext->Irp;
529 DeviceObject = IrpContext->DeviceObject;
530
531 DeviceExtension = DeviceObject->DeviceExtension;
532 Stack = IoGetCurrentIrpStackLocation(Irp);
533 FileObject = Stack->FileObject;
534
535 Ccb = (PNTFS_CCB)FileObject->FsContext2;
536 Fcb = (PNTFS_FCB)FileObject->FsContext;
537
538 /* Obtain the callers parameters */
539 BufferLength = Stack->Parameters.QueryDirectory.Length;
540 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
541 FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
542 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
543
544 if (NtfsFCBIsCompressed(Fcb))
545 {
546 DPRINT1("Compressed directory!\n");
547 UNIMPLEMENTED;
548 return STATUS_NOT_IMPLEMENTED;
549 }
550
551 if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
552 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
553 {
554 return STATUS_PENDING;
555 }
556
557 if (SearchPattern != NULL)
558 {
559 if (!Ccb->DirectorySearchPattern)
560 {
561 First = TRUE;
562 Pattern.Length = 0;
563 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
564 Ccb->DirectorySearchPattern = Pattern.Buffer =
565 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
566 if (!Ccb->DirectorySearchPattern)
567 {
568 ExReleaseResourceLite(&Fcb->MainResource);
569 return STATUS_INSUFFICIENT_RESOURCES;
570 }
571
572 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
573 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
574 }
575 }
576 else if (!Ccb->DirectorySearchPattern)
577 {
578 First = TRUE;
579 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
580 if (!Ccb->DirectorySearchPattern)
581 {
582 ExReleaseResourceLite(&Fcb->MainResource);
583 return STATUS_INSUFFICIENT_RESOURCES;
584 }
585
586 Ccb->DirectorySearchPattern[0] = L'*';
587 Ccb->DirectorySearchPattern[1] = 0;
588 }
589
590 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
591 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
592 DPRINT("In: '%S'\n", Fcb->PathName);
593
594 /* Determine directory index */
595 if (Stack->Flags & SL_INDEX_SPECIFIED)
596 {
597 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
598 }
599 else if (First || (Stack->Flags & SL_RESTART_SCAN))
600 {
601 Ccb->Entry = 0;
602 }
603
604 /* Get Buffer for result */
605 Buffer = NtfsGetUserBuffer(Irp, FALSE);
606
607 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
608
609 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
610 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
611 {
612 ExReleaseResourceLite(&Fcb->MainResource);
613 return STATUS_PENDING;
614 }
615
616 while (Status == STATUS_SUCCESS && BufferLength > 0)
617 {
618 Status = NtfsFindFileAt(DeviceExtension,
619 &Pattern,
620 &Ccb->Entry,
621 &FileRecord,
622 &MFTRecord,
623 Fcb->MFTIndex,
624 (Stack->Flags & SL_CASE_SENSITIVE));
625
626 if (NT_SUCCESS(Status))
627 {
628 /* HACK: files with both a short name and a long name are present twice in the index.
629 * Ignore the second entry, if it is immediately following the first one.
630 */
631 if (MFTRecord == OldMFTRecord)
632 {
633 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
634 Ccb->Entry++;
635 ExFreePoolWithTag(FileRecord, TAG_NTFS);
636 continue;
637 }
638 OldMFTRecord = MFTRecord;
639
640 switch (FileInformationClass)
641 {
642 case FileNameInformation:
643 Status = NtfsGetNameInformation(DeviceExtension,
644 FileRecord,
645 MFTRecord,
646 (PFILE_NAMES_INFORMATION)Buffer,
647 BufferLength);
648 break;
649
650 case FileDirectoryInformation:
651 Status = NtfsGetDirectoryInformation(DeviceExtension,
652 FileRecord,
653 MFTRecord,
654 (PFILE_DIRECTORY_INFORMATION)Buffer,
655 BufferLength);
656 break;
657
658 case FileFullDirectoryInformation:
659 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
660 FileRecord,
661 MFTRecord,
662 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
663 BufferLength);
664 break;
665
666 case FileBothDirectoryInformation:
667 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
668 FileRecord,
669 MFTRecord,
670 (PFILE_BOTH_DIR_INFORMATION)Buffer,
671 BufferLength);
672 break;
673
674 default:
675 Status = STATUS_INVALID_INFO_CLASS;
676 }
677
678 if (Status == STATUS_BUFFER_OVERFLOW)
679 {
680 if (Buffer0)
681 {
682 Buffer0->NextEntryOffset = 0;
683 }
684 break;
685 }
686 }
687 else
688 {
689 if (Buffer0)
690 {
691 Buffer0->NextEntryOffset = 0;
692 }
693
694 if (First)
695 {
696 Status = STATUS_NO_SUCH_FILE;
697 }
698 else
699 {
700 Status = STATUS_NO_MORE_FILES;
701 }
702 break;
703 }
704
705 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
706 Buffer0->FileIndex = FileIndex++;
707 Ccb->Entry++;
708
709 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
710 {
711 break;
712 }
713 BufferLength -= Buffer0->NextEntryOffset;
714 Buffer += Buffer0->NextEntryOffset;
715 ExFreePoolWithTag(FileRecord, TAG_NTFS);
716 }
717
718 if (Buffer0)
719 {
720 Buffer0->NextEntryOffset = 0;
721 }
722
723 ExReleaseResourceLite(&DeviceExtension->DirResource);
724 ExReleaseResourceLite(&Fcb->MainResource);
725
726 if (FileIndex > 0)
727 {
728 Status = STATUS_SUCCESS;
729 }
730
731 return Status;
732 }
733
734
735 NTSTATUS
736 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
737 {
738 NTSTATUS Status = STATUS_UNSUCCESSFUL;
739
740 DPRINT1("NtfsDirectoryControl() called\n");
741
742 switch (IrpContext->MinorFunction)
743 {
744 case IRP_MN_QUERY_DIRECTORY:
745 Status = NtfsQueryDirectory(IrpContext);
746 break;
747
748 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
749 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
750 Status = STATUS_NOT_IMPLEMENTED;
751 break;
752
753 default:
754 Status = STATUS_INVALID_DEVICE_REQUEST;
755 break;
756 }
757
758 if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
759 {
760 return NtfsMarkIrpContextForQueue(IrpContext);
761 }
762
763 IrpContext->Irp->IoStatus.Information = 0;
764
765 return Status;
766 }
767
768 /* EOF */