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