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