4d19052ac9c21d8141e931d13e323a861b7e92b3
[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 * @return
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.
60 *
61 * @remarks
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.
66 */
67 NTSTATUS
68 NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
69 ULONGLONG DirectoryMftIndex,
70 ULONGLONG FileReferenceNumber,
71 PFILENAME_ATTRIBUTE FilenameAttribute)
72 {
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;
80 ULONG LengthWritten;
81 PNTFS_ATTR_RECORD DestinationAttribute;
82 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
83 ULONG AttributeLength;
84 PNTFS_ATTR_RECORD NextAttribute;
85
86 // Allocate memory for the parent directory
87 ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool,
88 DeviceExt->NtfsInfo.BytesPerFileRecord,
89 TAG_NTFS);
90 if (!ParentFileRecord)
91 {
92 DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
93 return STATUS_INSUFFICIENT_RESOURCES;
94 }
95
96 // Open the parent directory
97 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
98 if (!NT_SUCCESS(Status))
99 {
100 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
101 DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
102 DirectoryMftIndex);
103 return Status;
104 }
105
106 DPRINT1("Dumping old parent file record:\n");
107 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
108
109 // Find the index root attribute for the directory
110 Status = FindAttribute(DeviceExt,
111 ParentFileRecord,
112 AttributeIndexRoot,
113 L"$I30",
114 4,
115 &IndexRootContext,
116 &IndexRootOffset);
117 if (!NT_SUCCESS(Status))
118 {
119 DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
120 DirectoryMftIndex);
121 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
122 return Status;
123 }
124
125 I30IndexRootLength = AttributeDataLength(&IndexRootContext->Record);
126
127 // Allocate memory for the index root data
128 I30IndexRoot = (PINDEX_ROOT_ATTRIBUTE)ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
129 if (!I30IndexRoot)
130 {
131 DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
132 ReleaseAttributeContext(IndexRootContext);
133 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
134 }
135
136 // Read the Index Root
137 Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
138 if (!NT_SUCCESS(Status))
139 {
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);
144 return Status;
145 }
146
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)
150 {
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;
156 }
157
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);
160 if (!NewIndexRoot)
161 {
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;
167 }
168
169 // Setup the new index record
170 RtlZeroMemory(NewIndexRoot, DeviceExt->NtfsInfo.BytesPerIndexRecord); // shouldn't be necessary but aids in debugging
171
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;
178 else
179 NewIndexRoot->ClustersPerIndexRecord = NewIndexRoot->SizeOfEntry / DeviceExt->NtfsInfo.BytesPerCluster;
180
181 // Setup the Index node header
182 NewIndexRoot->Header.FirstEntryOffset = 0x10;
183 NewIndexRoot->Header.Flags = INDEX_ROOT_SMALL;
184 // still need to calculate sizes
185
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);
192
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;
196
197 DPRINT1("New Index Node Entry Stream Length: %u\nNew Inde Node Entry Length: %u\n",
198 IndexNodeEntry->KeyLength,
199 IndexNodeEntry->Length);
200
201 // copy over the attribute proper
202 RtlCopyMemory(&IndexNodeEntry->FileName, FilenameAttribute, IndexNodeEntry->KeyLength);
203
204 // Now setup the dummy key
205 IndexNodeEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexNodeEntry + IndexNodeEntry->Length);
206
207 IndexNodeEntry->Data.Directory.IndexedFile = 0;
208 IndexNodeEntry->Length = 0x10;
209 IndexNodeEntry->KeyLength = 0;
210 IndexNodeEntry->Flags = NTFS_INDEX_ENTRY_END;
211
212 // This is when we'd normally setup the length (already done above)
213
214 // Write back the new index root attribute to the parent directory file record
215
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);
222
223 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + DestinationAttribute->Length);
224 if (NextAttribute->Type != AttributeEnd)
225 {
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;
232 }
233
234 // Update the length of the attribute in the file record of the parent directory
235 InternalSetResidentAttributeLength(IndexRootContext,
236 ParentFileRecord,
237 IndexRootOffset,
238 AttributeLength);
239
240 Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
241 if (!NT_SUCCESS(Status))
242 {
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);
247 return Status;
248 }
249
250 // Update the parent directory with the new index root
251 Status = WriteAttribute(DeviceExt,
252 IndexRootContext,
253 0,
254 (PUCHAR)NewIndexRoot,
255 AttributeLength,
256 &LengthWritten);
257 if (!NT_SUCCESS(Status) )
258 {
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);
264 return Status;
265 }
266
267 // re-read the parent file record, so we can dump it
268 Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
269 if (!NT_SUCCESS(Status))
270 {
271 DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
272 }
273 else
274 {
275 DPRINT1("Dumping new parent file record:\n");
276 NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
277 }
278
279 // Cleanup
280 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
281 ReleaseAttributeContext(IndexRootContext);
282 ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
283 ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
284
285 return Status;
286 }
287
288 ULONGLONG
289 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt,
290 PFILE_RECORD_HEADER FileRecord,
291 PCWSTR Stream,
292 ULONG StreamLength,
293 PULONGLONG AllocatedSize)
294 {
295 ULONGLONG Size = 0ULL;
296 ULONGLONG Allocated = 0ULL;
297 NTSTATUS Status;
298 PNTFS_ATTR_CONTEXT DataContext;
299
300 Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Stream, StreamLength, &DataContext, NULL);
301 if (NT_SUCCESS(Status))
302 {
303 Size = AttributeDataLength(&DataContext->Record);
304 Allocated = AttributeAllocatedLength(&DataContext->Record);
305 ReleaseAttributeContext(DataContext);
306 }
307
308 if (AllocatedSize != NULL) *AllocatedSize = Allocated;
309
310 return Size;
311 }
312
313
314 static NTSTATUS
315 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt,
316 PFILE_RECORD_HEADER FileRecord,
317 ULONGLONG MFTIndex,
318 PFILE_NAMES_INFORMATION Info,
319 ULONG BufferLength)
320 {
321 ULONG Length;
322 PFILENAME_ATTRIBUTE FileName;
323
324 DPRINT("NtfsGetNameInformation() called\n");
325
326 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
327 if (FileName == NULL)
328 {
329 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
330 NtfsDumpFileAttributes(DeviceExt, FileRecord);
331 return STATUS_OBJECT_NAME_NOT_FOUND;
332 }
333
334 Length = FileName->NameLength * sizeof (WCHAR);
335 if ((sizeof(FILE_NAMES_INFORMATION) + Length) > BufferLength)
336 return(STATUS_BUFFER_OVERFLOW);
337
338 Info->FileNameLength = Length;
339 Info->NextEntryOffset =
340 ROUND_UP(sizeof(FILE_NAMES_INFORMATION) + Length, sizeof(ULONG));
341 RtlCopyMemory(Info->FileName, FileName->Name, Length);
342
343 return(STATUS_SUCCESS);
344 }
345
346
347 static NTSTATUS
348 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
349 PFILE_RECORD_HEADER FileRecord,
350 ULONGLONG MFTIndex,
351 PFILE_DIRECTORY_INFORMATION Info,
352 ULONG BufferLength)
353 {
354 ULONG Length;
355 PFILENAME_ATTRIBUTE FileName;
356 PSTANDARD_INFORMATION StdInfo;
357
358 DPRINT("NtfsGetDirectoryInformation() called\n");
359
360 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
361 if (FileName == NULL)
362 {
363 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
364 NtfsDumpFileAttributes(DeviceExt, FileRecord);
365 return STATUS_OBJECT_NAME_NOT_FOUND;
366 }
367
368 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
369 ASSERT(StdInfo != NULL);
370
371 Length = FileName->NameLength * sizeof (WCHAR);
372 if ((sizeof(FILE_DIRECTORY_INFORMATION) + Length) > BufferLength)
373 return(STATUS_BUFFER_OVERFLOW);
374
375 Info->FileNameLength = Length;
376 Info->NextEntryOffset =
377 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) + Length, sizeof(ULONG));
378 RtlCopyMemory(Info->FileName, FileName->Name, Length);
379
380 Info->CreationTime.QuadPart = FileName->CreationTime;
381 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
382 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
383 Info->ChangeTime.QuadPart = FileName->ChangeTime;
384
385 /* Convert file flags */
386 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
387
388 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
389
390 Info->FileIndex = MFTIndex;
391
392 return STATUS_SUCCESS;
393 }
394
395
396 static NTSTATUS
397 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
398 PFILE_RECORD_HEADER FileRecord,
399 ULONGLONG MFTIndex,
400 PFILE_FULL_DIRECTORY_INFORMATION Info,
401 ULONG BufferLength)
402 {
403 ULONG Length;
404 PFILENAME_ATTRIBUTE FileName;
405 PSTANDARD_INFORMATION StdInfo;
406
407 DPRINT("NtfsGetFullDirectoryInformation() called\n");
408
409 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
410 if (FileName == NULL)
411 {
412 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
413 NtfsDumpFileAttributes(DeviceExt, FileRecord);
414 return STATUS_OBJECT_NAME_NOT_FOUND;
415 }
416
417 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
418 ASSERT(StdInfo != NULL);
419
420 Length = FileName->NameLength * sizeof (WCHAR);
421 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION) + Length) > BufferLength)
422 return(STATUS_BUFFER_OVERFLOW);
423
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);
428
429 Info->CreationTime.QuadPart = FileName->CreationTime;
430 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
431 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
432 Info->ChangeTime.QuadPart = FileName->ChangeTime;
433
434 /* Convert file flags */
435 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
436
437 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
438
439 Info->FileIndex = MFTIndex;
440 Info->EaSize = 0;
441
442 return STATUS_SUCCESS;
443 }
444
445
446 static NTSTATUS
447 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt,
448 PFILE_RECORD_HEADER FileRecord,
449 ULONGLONG MFTIndex,
450 PFILE_BOTH_DIR_INFORMATION Info,
451 ULONG BufferLength)
452 {
453 ULONG Length;
454 PFILENAME_ATTRIBUTE FileName, ShortFileName;
455 PSTANDARD_INFORMATION StdInfo;
456
457 DPRINT("NtfsGetBothDirectoryInformation() called\n");
458
459 FileName = GetBestFileNameFromRecord(DeviceExt, FileRecord);
460 if (FileName == NULL)
461 {
462 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex);
463 NtfsDumpFileAttributes(DeviceExt, FileRecord);
464 return STATUS_OBJECT_NAME_NOT_FOUND;
465 }
466 ShortFileName = GetFileNameFromRecord(DeviceExt, FileRecord, NTFS_FILE_NAME_DOS);
467
468 StdInfo = GetStandardInformationFromRecord(DeviceExt, FileRecord);
469 ASSERT(StdInfo != NULL);
470
471 Length = FileName->NameLength * sizeof (WCHAR);
472 if ((sizeof(FILE_BOTH_DIR_INFORMATION) + Length) > BufferLength)
473 return(STATUS_BUFFER_OVERFLOW);
474
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);
479
480 if (ShortFileName)
481 {
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);
486 }
487 else
488 {
489 Info->ShortName[0] = 0;
490 Info->ShortNameLength = 0;
491 }
492
493 Info->CreationTime.QuadPart = FileName->CreationTime;
494 Info->LastAccessTime.QuadPart = FileName->LastAccessTime;
495 Info->LastWriteTime.QuadPart = FileName->LastWriteTime;
496 Info->ChangeTime.QuadPart = FileName->ChangeTime;
497
498 /* Convert file flags */
499 NtfsFileFlagsToAttributes(FileName->FileAttributes | StdInfo->FileAttribute, &Info->FileAttributes);
500
501 Info->EndOfFile.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&Info->AllocationSize.QuadPart);
502
503 Info->FileIndex = MFTIndex;
504 Info->EaSize = 0;
505
506 return STATUS_SUCCESS;
507 }
508
509
510 NTSTATUS
511 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext)
512 {
513 PIRP Irp;
514 PDEVICE_OBJECT DeviceObject;
515 PDEVICE_EXTENSION DeviceExtension;
516 LONG BufferLength = 0;
517 PUNICODE_STRING SearchPattern = NULL;
518 FILE_INFORMATION_CLASS FileInformationClass;
519 ULONG FileIndex = 0;
520 PUCHAR Buffer = NULL;
521 PFILE_NAMES_INFORMATION Buffer0 = NULL;
522 PNTFS_FCB Fcb;
523 PNTFS_CCB Ccb;
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;
531
532 DPRINT1("NtfsQueryDirectory() called\n");
533
534 ASSERT(IrpContext);
535 Irp = IrpContext->Irp;
536 DeviceObject = IrpContext->DeviceObject;
537
538 DeviceExtension = DeviceObject->DeviceExtension;
539 Stack = IoGetCurrentIrpStackLocation(Irp);
540 FileObject = Stack->FileObject;
541
542 Ccb = (PNTFS_CCB)FileObject->FsContext2;
543 Fcb = (PNTFS_FCB)FileObject->FsContext;
544
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;
550
551 if (NtfsFCBIsCompressed(Fcb))
552 {
553 DPRINT1("Compressed directory!\n");
554 UNIMPLEMENTED;
555 return STATUS_NOT_IMPLEMENTED;
556 }
557
558 if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
559 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
560 {
561 return STATUS_PENDING;
562 }
563
564 if (SearchPattern != NULL)
565 {
566 if (!Ccb->DirectorySearchPattern)
567 {
568 First = TRUE;
569 Pattern.Length = 0;
570 Pattern.MaximumLength = SearchPattern->Length + sizeof(WCHAR);
571 Ccb->DirectorySearchPattern = Pattern.Buffer =
572 ExAllocatePoolWithTag(NonPagedPool, Pattern.MaximumLength, TAG_NTFS);
573 if (!Ccb->DirectorySearchPattern)
574 {
575 ExReleaseResourceLite(&Fcb->MainResource);
576 return STATUS_INSUFFICIENT_RESOURCES;
577 }
578
579 memcpy(Ccb->DirectorySearchPattern, SearchPattern->Buffer, SearchPattern->Length);
580 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
581 }
582 }
583 else if (!Ccb->DirectorySearchPattern)
584 {
585 First = TRUE;
586 Ccb->DirectorySearchPattern = ExAllocatePoolWithTag(NonPagedPool, 2 * sizeof(WCHAR), TAG_NTFS);
587 if (!Ccb->DirectorySearchPattern)
588 {
589 ExReleaseResourceLite(&Fcb->MainResource);
590 return STATUS_INSUFFICIENT_RESOURCES;
591 }
592
593 Ccb->DirectorySearchPattern[0] = L'*';
594 Ccb->DirectorySearchPattern[1] = 0;
595 }
596
597 RtlInitUnicodeString(&Pattern, Ccb->DirectorySearchPattern);
598 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
599 DPRINT("In: '%S'\n", Fcb->PathName);
600
601 /* Determine directory index */
602 if (Stack->Flags & SL_INDEX_SPECIFIED)
603 {
604 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
605 }
606 else if (First || (Stack->Flags & SL_RESTART_SCAN))
607 {
608 Ccb->Entry = 0;
609 }
610
611 /* Get Buffer for result */
612 Buffer = NtfsGetUserBuffer(Irp, FALSE);
613
614 DPRINT("Buffer=%p tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
615
616 if (!ExAcquireResourceExclusiveLite(&DeviceExtension->DirResource,
617 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
618 {
619 ExReleaseResourceLite(&Fcb->MainResource);
620 return STATUS_PENDING;
621 }
622
623 while (Status == STATUS_SUCCESS && BufferLength > 0)
624 {
625 Status = NtfsFindFileAt(DeviceExtension,
626 &Pattern,
627 &Ccb->Entry,
628 &FileRecord,
629 &MFTRecord,
630 Fcb->MFTIndex,
631 (Stack->Flags & SL_CASE_SENSITIVE));
632
633 if (NT_SUCCESS(Status))
634 {
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.
637 */
638 if (MFTRecord == OldMFTRecord)
639 {
640 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord);
641 Ccb->Entry++;
642 ExFreePoolWithTag(FileRecord, TAG_NTFS);
643 continue;
644 }
645 OldMFTRecord = MFTRecord;
646
647 switch (FileInformationClass)
648 {
649 case FileNameInformation:
650 Status = NtfsGetNameInformation(DeviceExtension,
651 FileRecord,
652 MFTRecord,
653 (PFILE_NAMES_INFORMATION)Buffer,
654 BufferLength);
655 break;
656
657 case FileDirectoryInformation:
658 Status = NtfsGetDirectoryInformation(DeviceExtension,
659 FileRecord,
660 MFTRecord,
661 (PFILE_DIRECTORY_INFORMATION)Buffer,
662 BufferLength);
663 break;
664
665 case FileFullDirectoryInformation:
666 Status = NtfsGetFullDirectoryInformation(DeviceExtension,
667 FileRecord,
668 MFTRecord,
669 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
670 BufferLength);
671 break;
672
673 case FileBothDirectoryInformation:
674 Status = NtfsGetBothDirectoryInformation(DeviceExtension,
675 FileRecord,
676 MFTRecord,
677 (PFILE_BOTH_DIR_INFORMATION)Buffer,
678 BufferLength);
679 break;
680
681 default:
682 Status = STATUS_INVALID_INFO_CLASS;
683 }
684
685 if (Status == STATUS_BUFFER_OVERFLOW)
686 {
687 if (Buffer0)
688 {
689 Buffer0->NextEntryOffset = 0;
690 }
691 break;
692 }
693 }
694 else
695 {
696 if (Buffer0)
697 {
698 Buffer0->NextEntryOffset = 0;
699 }
700
701 if (First)
702 {
703 Status = STATUS_NO_SUCH_FILE;
704 }
705 else
706 {
707 Status = STATUS_NO_MORE_FILES;
708 }
709 break;
710 }
711
712 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
713 Buffer0->FileIndex = FileIndex++;
714 Ccb->Entry++;
715
716 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
717 {
718 break;
719 }
720 BufferLength -= Buffer0->NextEntryOffset;
721 Buffer += Buffer0->NextEntryOffset;
722 ExFreePoolWithTag(FileRecord, TAG_NTFS);
723 }
724
725 if (Buffer0)
726 {
727 Buffer0->NextEntryOffset = 0;
728 }
729
730 ExReleaseResourceLite(&DeviceExtension->DirResource);
731 ExReleaseResourceLite(&Fcb->MainResource);
732
733 if (FileIndex > 0)
734 {
735 Status = STATUS_SUCCESS;
736 }
737
738 return Status;
739 }
740
741
742 NTSTATUS
743 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext)
744 {
745 NTSTATUS Status = STATUS_UNSUCCESSFUL;
746
747 DPRINT1("NtfsDirectoryControl() called\n");
748
749 switch (IrpContext->MinorFunction)
750 {
751 case IRP_MN_QUERY_DIRECTORY:
752 Status = NtfsQueryDirectory(IrpContext);
753 break;
754
755 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
756 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
757 Status = STATUS_NOT_IMPLEMENTED;
758 break;
759
760 default:
761 Status = STATUS_INVALID_DEVICE_REQUEST;
762 break;
763 }
764
765 if (Status == STATUS_PENDING && IrpContext->Flags & IRPCONTEXT_COMPLETE)
766 {
767 return NtfsMarkIrpContextForQueue(IrpContext);
768 }
769
770 IrpContext->Irp->IoStatus.Information = 0;
771
772 return Status;
773 }
774
775 /* EOF */