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 ****************************************************************/
38 NtfsGetFileSize(PDEVICE_EXTENSION DeviceExt
,
39 PFILE_RECORD_HEADER FileRecord
,
42 PULONGLONG AllocatedSize
)
44 ULONGLONG Size
= 0ULL;
45 ULONGLONG Allocated
= 0ULL;
47 PNTFS_ATTR_CONTEXT DataContext
;
49 Status
= FindAttribute(DeviceExt
, FileRecord
, AttributeData
, Stream
, StreamLength
, &DataContext
, NULL
);
50 if (NT_SUCCESS(Status
))
52 Size
= AttributeDataLength(&DataContext
->Record
);
53 Allocated
= AttributeAllocatedLength(&DataContext
->Record
);
54 ReleaseAttributeContext(DataContext
);
57 if (AllocatedSize
!= NULL
) *AllocatedSize
= Allocated
;
64 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
65 PFILE_RECORD_HEADER FileRecord
,
67 PFILE_NAMES_INFORMATION Info
,
71 PFILENAME_ATTRIBUTE FileName
;
73 DPRINT("NtfsGetNameInformation() called\n");
75 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
78 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
79 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
80 return STATUS_OBJECT_NAME_NOT_FOUND
;
83 Length
= FileName
->NameLength
* sizeof (WCHAR
);
84 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
85 return(STATUS_BUFFER_OVERFLOW
);
87 Info
->FileNameLength
= Length
;
88 Info
->NextEntryOffset
=
89 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
90 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
92 return(STATUS_SUCCESS
);
97 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
98 PFILE_RECORD_HEADER FileRecord
,
100 PFILE_DIRECTORY_INFORMATION Info
,
104 PFILENAME_ATTRIBUTE FileName
;
105 PSTANDARD_INFORMATION StdInfo
;
107 DPRINT("NtfsGetDirectoryInformation() called\n");
109 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
110 if (FileName
== NULL
)
112 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
113 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
114 return STATUS_OBJECT_NAME_NOT_FOUND
;
117 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
118 ASSERT(StdInfo
!= NULL
);
120 Length
= FileName
->NameLength
* sizeof (WCHAR
);
121 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
122 return(STATUS_BUFFER_OVERFLOW
);
124 Info
->FileNameLength
= Length
;
125 Info
->NextEntryOffset
=
126 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
127 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
129 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
130 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
131 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
132 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
134 /* Convert file flags */
135 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
137 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
139 Info
->FileIndex
= MFTIndex
;
141 return STATUS_SUCCESS
;
146 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
147 PFILE_RECORD_HEADER FileRecord
,
149 PFILE_FULL_DIRECTORY_INFORMATION Info
,
153 PFILENAME_ATTRIBUTE FileName
;
154 PSTANDARD_INFORMATION StdInfo
;
156 DPRINT("NtfsGetFullDirectoryInformation() called\n");
158 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
159 if (FileName
== NULL
)
161 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
162 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
163 return STATUS_OBJECT_NAME_NOT_FOUND
;
166 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
167 ASSERT(StdInfo
!= NULL
);
169 Length
= FileName
->NameLength
* sizeof (WCHAR
);
170 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
171 return(STATUS_BUFFER_OVERFLOW
);
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
);
178 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
179 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
180 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
181 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
183 /* Convert file flags */
184 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
186 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
188 Info
->FileIndex
= MFTIndex
;
191 return STATUS_SUCCESS
;
196 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
197 PFILE_RECORD_HEADER FileRecord
,
199 PFILE_BOTH_DIR_INFORMATION Info
,
203 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
204 PSTANDARD_INFORMATION StdInfo
;
206 DPRINT("NtfsGetBothDirectoryInformation() called\n");
208 FileName
= GetBestFileNameFromRecord(DeviceExt
, FileRecord
);
209 if (FileName
== NULL
)
211 DPRINT1("No name information for file ID: %#I64x\n", MFTIndex
);
212 NtfsDumpFileAttributes(DeviceExt
, FileRecord
);
213 return STATUS_OBJECT_NAME_NOT_FOUND
;
215 ShortFileName
= GetFileNameFromRecord(DeviceExt
, FileRecord
, NTFS_FILE_NAME_DOS
);
217 StdInfo
= GetStandardInformationFromRecord(DeviceExt
, FileRecord
);
218 ASSERT(StdInfo
!= NULL
);
220 Length
= FileName
->NameLength
* sizeof (WCHAR
);
221 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
222 return(STATUS_BUFFER_OVERFLOW
);
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
);
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
);
238 Info
->ShortName
[0] = 0;
239 Info
->ShortNameLength
= 0;
242 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
243 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
244 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
245 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
247 /* Convert file flags */
248 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
250 Info
->EndOfFile
.QuadPart
= NtfsGetFileSize(DeviceExt
, FileRecord
, L
"", 0, (PULONGLONG
)&Info
->AllocationSize
.QuadPart
);
252 Info
->FileIndex
= MFTIndex
;
255 return STATUS_SUCCESS
;
260 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
263 PDEVICE_OBJECT DeviceObject
;
264 PDEVICE_EXTENSION DeviceExtension
;
265 LONG BufferLength
= 0;
266 PUNICODE_STRING SearchPattern
= NULL
;
267 FILE_INFORMATION_CLASS FileInformationClass
;
269 PUCHAR Buffer
= NULL
;
270 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
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
;
281 DPRINT1("NtfsQueryDirectory() called\n");
284 Irp
= IrpContext
->Irp
;
285 DeviceObject
= IrpContext
->DeviceObject
;
287 DeviceExtension
= DeviceObject
->DeviceExtension
;
288 Stack
= IoGetCurrentIrpStackLocation(Irp
);
289 FileObject
= Stack
->FileObject
;
291 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
292 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
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
;
300 if (NtfsFCBIsCompressed(Fcb
))
302 DPRINT1("Compressed directory!\n");
304 return STATUS_NOT_IMPLEMENTED
;
307 if (!ExAcquireResourceSharedLite(&Fcb
->MainResource
,
308 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
310 return STATUS_PENDING
;
313 if (SearchPattern
!= NULL
)
315 if (!Ccb
->DirectorySearchPattern
)
319 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
320 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
321 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
322 if (!Ccb
->DirectorySearchPattern
)
324 ExReleaseResourceLite(&Fcb
->MainResource
);
325 return STATUS_INSUFFICIENT_RESOURCES
;
328 memcpy(Ccb
->DirectorySearchPattern
, SearchPattern
->Buffer
, SearchPattern
->Length
);
329 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
332 else if (!Ccb
->DirectorySearchPattern
)
335 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
336 if (!Ccb
->DirectorySearchPattern
)
338 ExReleaseResourceLite(&Fcb
->MainResource
);
339 return STATUS_INSUFFICIENT_RESOURCES
;
342 Ccb
->DirectorySearchPattern
[0] = L
'*';
343 Ccb
->DirectorySearchPattern
[1] = 0;
346 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
347 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
348 DPRINT("In: '%S'\n", Fcb
->PathName
);
350 /* Determine directory index */
351 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
353 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
355 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
360 /* Get Buffer for result */
361 Buffer
= NtfsGetUserBuffer(Irp
, FALSE
);
363 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
365 if (!ExAcquireResourceExclusiveLite(&DeviceExtension
->DirResource
,
366 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
368 ExReleaseResourceLite(&Fcb
->MainResource
);
369 return STATUS_PENDING
;
372 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
374 Status
= NtfsFindFileAt(DeviceExtension
,
380 BooleanFlagOn(Stack
->Flags
, SL_CASE_SENSITIVE
));
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;