3 * Copyright (C) 2002, 2003, 2014 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/dirctl.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Pierre Schweitzer (pierre@reactos.org)
25 * Hervé Poussineau (hpoussin@reactos.org)
28 /* INCLUDES *****************************************************************/
35 /* FUNCTIONS ****************************************************************/
39 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt
,
40 PFILE_RECORD_HEADER FileRecord
,
43 PULONGLONG AllocatedSize
)
45 ULONGLONG Size
= 0ULL;
46 ULONGLONG Allocated
= 0ULL;
48 PNTFS_ATTR_CONTEXT DataContext
;
50 Status
= FindAttribute(DeviceExt
, FileRecord
, AttributeData
, Stream
, StreamLength
, &DataContext
, NULL
);
51 if (NT_SUCCESS(Status
))
53 Size
= AttributeDataLength(&DataContext
->Record
);
54 Allocated
= AttributeAllocatedLength(&DataContext
->Record
);
55 ReleaseAttributeContext(DataContext
);
58 if (AllocatedSize
!= NULL
) *AllocatedSize
= Allocated
;
65 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
66 PFILE_RECORD_HEADER FileRecord
,
68 PFILE_NAMES_INFORMATION Info
,
72 PFILENAME_ATTRIBUTE FileName
;
74 DPRINT("NtfsGetNameInformation() called\n");
76 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
79 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
80 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
81 return STATUS_OBJECT_NAME_NOT_FOUND
;
84 Length
= FileName
->NameLength
* sizeof (WCHAR
);
85 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
86 return(STATUS_BUFFER_OVERFLOW
);
88 Info
->FileNameLength
= Length
;
89 Info
->NextEntryOffset
=
90 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
91 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
93 return(STATUS_SUCCESS
);
98 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
99 PFILE_RECORD_HEADER FileRecord
,
101 PFILE_DIRECTORY_INFORMATION Info
,
105 PFILENAME_ATTRIBUTE FileName
;
106 PSTANDARD_INFORMATION StdInfo
;
108 DPRINT("NtfsGetDirectoryInformation() called\n");
110 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
111 if (FileName
== NULL
)
113 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
114 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
115 return STATUS_OBJECT_NAME_NOT_FOUND
;
118 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
119 ASSERT(StdInfo
!= NULL
);
121 Length
= FileName
->NameLength
* sizeof (WCHAR
);
122 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
123 return(STATUS_BUFFER_OVERFLOW
);
125 Info
->FileNameLength
= Length
;
126 Info
->NextEntryOffset
=
127 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
128 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
130 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
131 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
132 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
133 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
135 /* Convert file flags */
136 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
138 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
140 Info
->FileIndex
= MFTIndex
;
142 return STATUS_SUCCESS
;
147 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
148 PFILE_RECORD_HEADER FileRecord
,
150 PFILE_FULL_DIRECTORY_INFORMATION Info
,
154 PFILENAME_ATTRIBUTE FileName
;
155 PSTANDARD_INFORMATION StdInfo
;
157 DPRINT("NtfsGetFullDirectoryInformation() called\n");
159 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
160 if (FileName
== NULL
)
162 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
163 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
164 return STATUS_OBJECT_NAME_NOT_FOUND
;
167 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
168 ASSERT(StdInfo
!= NULL
);
170 Length
= FileName
->NameLength
* sizeof (WCHAR
);
171 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
172 return(STATUS_BUFFER_OVERFLOW
);
174 Info
->FileNameLength
= Length
;
175 Info
->NextEntryOffset
=
176 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
177 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
179 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
180 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
181 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
182 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
184 /* Convert file flags */
185 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
187 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
189 Info
->FileIndex
= MFTIndex
;
192 return STATUS_SUCCESS
;
197 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
198 PFILE_RECORD_HEADER FileRecord
,
200 PFILE_BOTH_DIR_INFORMATION Info
,
204 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
205 PSTANDARD_INFORMATION StdInfo
;
207 DPRINT("NtfsGetBothDirectoryInformation() called\n");
209 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
210 if (FileName
== NULL
)
212 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
213 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
214 return STATUS_OBJECT_NAME_NOT_FOUND
;
216 ShortFileName
= GetFileNameFromRecord(DeviceExt
, FileRecord
, NTFS_FILE_NAME_DOS
);
218 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
219 ASSERT(StdInfo
!= NULL
);
221 Length
= FileName
->NameLength
* sizeof (WCHAR
);
222 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
223 return(STATUS_BUFFER_OVERFLOW
);
225 Info
->FileNameLength
= Length
;
226 Info
->NextEntryOffset
=
227 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
, sizeof(ULONG
));
228 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
232 /* Should we upcase the filename? */
233 ASSERT(ShortFileName
->NameLength
<= ARRAYSIZE(Info
->ShortName
));
234 Info
->ShortNameLength
= ShortFileName
->NameLength
* sizeof(WCHAR
);
235 RtlCopyMemory(Info
->ShortName
, ShortFileName
->Name
, Info
->ShortNameLength
);
239 Info
->ShortName
[0] = 0;
240 Info
->ShortNameLength
= 0;
243 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
244 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
245 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
246 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
248 /* Convert file flags */
249 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
251 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
253 Info
->FileIndex
= MFTIndex
;
256 return STATUS_SUCCESS
;
261 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
264 PDEVICE_OBJECT DeviceObject
;
265 PDEVICE_EXTENSION DeviceExtension
;
266 LONG BufferLength
= 0;
267 PUNICODE_STRING SearchPattern
= NULL
;
268 FILE_INFORMATION_CLASS FileInformationClass
;
270 PUCHAR Buffer
= NULL
;
271 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
274 BOOLEAN First
= FALSE
;
275 PIO_STACK_LOCATION Stack
;
276 PFILE_OBJECT FileObject
;
277 NTSTATUS Status
= STATUS_SUCCESS
;
278 PFILE_RECORD_HEADER FileRecord
;
279 ULONGLONG MFTRecord
, OldMFTRecord
= 0;
280 UNICODE_STRING Pattern
;
282 DPRINT1("NtfsQueryDirectory() called\n");
285 Irp
= IrpContext
->Irp
;
286 DeviceObject
= IrpContext
->DeviceObject
;
288 DeviceExtension
= DeviceObject
->DeviceExtension
;
289 Stack
= IoGetCurrentIrpStackLocation(Irp
);
290 FileObject
= Stack
->FileObject
;
292 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
293 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
295 /* Obtain the callers parameters */
296 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
297 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
298 FileInformationClass
= Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
299 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
301 if (NtfsFCBIsCompressed(Fcb
))
303 DPRINT1("Compressed directory!\n");
305 return STATUS_NOT_IMPLEMENTED
;
308 if (!ExAcquireResourceSharedLite(&Fcb
->MainResource
,
309 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
311 return STATUS_PENDING
;
314 if (SearchPattern
!= NULL
)
316 if (!Ccb
->DirectorySearchPattern
)
320 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
321 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
322 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
323 if (!Ccb
->DirectorySearchPattern
)
325 ExReleaseResourceLite(&Fcb
->MainResource
);
326 return STATUS_INSUFFICIENT_RESOURCES
;
329 memcpy(Ccb
->DirectorySearchPattern
, SearchPattern
->Buffer
, SearchPattern
->Length
);
330 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
333 else if (!Ccb
->DirectorySearchPattern
)
336 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
337 if (!Ccb
->DirectorySearchPattern
)
339 ExReleaseResourceLite(&Fcb
->MainResource
);
340 return STATUS_INSUFFICIENT_RESOURCES
;
343 Ccb
->DirectorySearchPattern
[0] = L
'*';
344 Ccb
->DirectorySearchPattern
[1] = 0;
347 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
348 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
349 DPRINT("In: '%S'\n", Fcb
->PathName
);
351 /* Determine directory index */
352 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
354 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
356 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
361 /* Get Buffer for result */
362 Buffer
= NtfsGetUserBuffer(Irp
, FALSE
);
364 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
366 if (!ExAcquireResourceExclusiveLite(&DeviceExtension
->DirResource
,
367 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
369 ExReleaseResourceLite(&Fcb
->MainResource
);
370 return STATUS_PENDING
;
373 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
375 Status
= NtfsFindFileAt(DeviceExtension
,
382 if (NT_SUCCESS(Status
))
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.
387 if (MFTRecord
== OldMFTRecord
)
389 DPRINT1("Ignoring duplicate MFT entry 0x%x\n", MFTRecord
);
391 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
394 OldMFTRecord
= MFTRecord
;
396 switch (FileInformationClass
)
398 case FileNameInformation
:
399 Status
= NtfsGetNameInformation(DeviceExtension
,
402 (PFILE_NAMES_INFORMATION
)Buffer
,
406 case FileDirectoryInformation
:
407 Status
= NtfsGetDirectoryInformation(DeviceExtension
,
410 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
414 case FileFullDirectoryInformation
:
415 Status
= NtfsGetFullDirectoryInformation(DeviceExtension
,
418 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
422 case FileBothDirectoryInformation
:
423 Status
= NtfsGetBothDirectoryInformation(DeviceExtension
,
426 (PFILE_BOTH_DIR_INFORMATION
)Buffer
,
431 Status
= STATUS_INVALID_INFO_CLASS
;
434 if (Status
== STATUS_BUFFER_OVERFLOW
)
438 Buffer0
->NextEntryOffset
= 0;
447 Buffer0
->NextEntryOffset
= 0;
452 Status
= STATUS_NO_SUCH_FILE
;
456 Status
= STATUS_NO_MORE_FILES
;
461 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
462 Buffer0
->FileIndex
= FileIndex
++;
465 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
469 BufferLength
-= Buffer0
->NextEntryOffset
;
470 Buffer
+= Buffer0
->NextEntryOffset
;
471 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
476 Buffer0
->NextEntryOffset
= 0;
479 ExReleaseResourceLite(&DeviceExtension
->DirResource
);
480 ExReleaseResourceLite(&Fcb
->MainResource
);
484 Status
= STATUS_SUCCESS
;
492 NtfsDirectoryControl(PNTFS_IRP_CONTEXT IrpContext
)
494 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
496 DPRINT1("NtfsDirectoryControl() called\n");
498 switch (IrpContext
->MinorFunction
)
500 case IRP_MN_QUERY_DIRECTORY
:
501 Status
= NtfsQueryDirectory(IrpContext
);
504 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
505 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
506 Status
= STATUS_NOT_IMPLEMENTED
;
510 Status
= STATUS_INVALID_DEVICE_REQUEST
;
514 if (Status
== STATUS_PENDING
&& IrpContext
->Flags
& IRPCONTEXT_COMPLETE
)
516 return NtfsMarkIrpContextForQueue(IrpContext
);
519 IrpContext
->Irp
->IoStatus
.Information
= 0;