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 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
40 PFILE_RECORD_HEADER FileRecord
,
41 PNTFS_ATTR_CONTEXT DataContext
,
42 PFILE_NAMES_INFORMATION Info
,
46 PFILENAME_ATTRIBUTE FileName
;
48 DPRINT("NtfsGetNameInformation() called\n");
50 FileName
= GetBestFileNameFromRecord(FileRecord
);
51 ASSERT(FileName
!= NULL
);
53 Length
= FileName
->NameLength
* sizeof (WCHAR
);
54 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
55 return(STATUS_BUFFER_OVERFLOW
);
57 Info
->FileNameLength
= Length
;
58 Info
->NextEntryOffset
=
59 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
60 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
62 return(STATUS_SUCCESS
);
67 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
68 PFILE_RECORD_HEADER FileRecord
,
69 PNTFS_ATTR_CONTEXT DataContext
,
71 PFILE_DIRECTORY_INFORMATION Info
,
75 PFILENAME_ATTRIBUTE FileName
;
76 PSTANDARD_INFORMATION StdInfo
;
78 DPRINT("NtfsGetDirectoryInformation() called\n");
80 FileName
= GetBestFileNameFromRecord(FileRecord
);
81 ASSERT(FileName
!= NULL
);
83 StdInfo
= GetStandardInformationFromRecord(FileRecord
);
84 ASSERT(StdInfo
!= NULL
);
86 Length
= FileName
->NameLength
* sizeof (WCHAR
);
87 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
88 return(STATUS_BUFFER_OVERFLOW
);
90 Info
->FileNameLength
= Length
;
91 Info
->NextEntryOffset
=
92 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
93 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
95 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
96 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
97 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
98 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
100 /* Convert file flags */
101 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
103 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
104 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
106 Info
->FileIndex
= MFTIndex
;
108 return STATUS_SUCCESS
;
113 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
114 PFILE_RECORD_HEADER FileRecord
,
115 PNTFS_ATTR_CONTEXT DataContext
,
117 PFILE_FULL_DIRECTORY_INFORMATION Info
,
121 PFILENAME_ATTRIBUTE FileName
;
122 PSTANDARD_INFORMATION StdInfo
;
124 DPRINT("NtfsGetFullDirectoryInformation() called\n");
126 FileName
= GetBestFileNameFromRecord(FileRecord
);
127 ASSERT(FileName
!= NULL
);
129 StdInfo
= GetStandardInformationFromRecord(FileRecord
);
130 ASSERT(StdInfo
!= NULL
);
132 Length
= FileName
->NameLength
* sizeof (WCHAR
);
133 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
134 return(STATUS_BUFFER_OVERFLOW
);
136 Info
->FileNameLength
= Length
;
137 Info
->NextEntryOffset
=
138 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
139 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
141 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
142 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
143 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
144 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
146 /* Convert file flags */
147 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
149 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
150 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
152 Info
->FileIndex
= MFTIndex
;
155 return STATUS_SUCCESS
;
160 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
161 PFILE_RECORD_HEADER FileRecord
,
162 PNTFS_ATTR_CONTEXT DataContext
,
164 PFILE_BOTH_DIR_INFORMATION Info
,
168 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
169 PSTANDARD_INFORMATION StdInfo
;
171 DPRINT("NtfsGetBothDirectoryInformation() called\n");
173 FileName
= GetBestFileNameFromRecord(FileRecord
);
174 ASSERT(FileName
!= NULL
);
175 ShortFileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
177 StdInfo
= GetStandardInformationFromRecord(FileRecord
);
178 ASSERT(StdInfo
!= NULL
);
180 Length
= FileName
->NameLength
* sizeof (WCHAR
);
181 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
182 return(STATUS_BUFFER_OVERFLOW
);
184 Info
->FileNameLength
= Length
;
185 Info
->NextEntryOffset
=
186 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
, sizeof(ULONG
));
187 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
191 /* Should we upcase the filename? */
192 ASSERT(ShortFileName
->NameLength
<= ARRAYSIZE(Info
->ShortName
));
193 Info
->ShortNameLength
= ShortFileName
->NameLength
* sizeof(WCHAR
);
194 RtlCopyMemory(Info
->ShortName
, ShortFileName
->Name
, Info
->ShortNameLength
);
198 Info
->ShortName
[0] = 0;
199 Info
->ShortNameLength
= 0;
202 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
203 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
204 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
205 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
207 /* Convert file flags */
208 NtfsFileFlagsToAttributes(FileName
->FileAttributes
| StdInfo
->FileAttribute
, &Info
->FileAttributes
);
210 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
211 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
213 Info
->FileIndex
= MFTIndex
;
216 return STATUS_SUCCESS
;
221 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
224 PDEVICE_OBJECT DeviceObject
;
225 PDEVICE_EXTENSION DeviceExtension
;
226 LONG BufferLength
= 0;
227 PUNICODE_STRING SearchPattern
= NULL
;
228 FILE_INFORMATION_CLASS FileInformationClass
;
230 PUCHAR Buffer
= NULL
;
231 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
234 BOOLEAN First
= FALSE
;
235 PIO_STACK_LOCATION Stack
;
236 PFILE_OBJECT FileObject
;
237 NTSTATUS Status
= STATUS_SUCCESS
;
238 PFILE_RECORD_HEADER FileRecord
;
239 PNTFS_ATTR_CONTEXT DataContext
;
240 ULONGLONG MFTRecord
, OldMFTRecord
= 0;
241 UNICODE_STRING Pattern
;
243 DPRINT1("NtfsQueryDirectory() called\n");
246 Irp
= IrpContext
->Irp
;
247 DeviceObject
= IrpContext
->DeviceObject
;
249 DeviceExtension
= DeviceObject
->DeviceExtension
;
250 Stack
= IoGetCurrentIrpStackLocation(Irp
);
251 FileObject
= Stack
->FileObject
;
253 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
254 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
256 /* Obtain the callers parameters */
257 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
258 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
259 FileInformationClass
= Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
260 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
262 if (SearchPattern
!= NULL
)
264 if (!Ccb
->DirectorySearchPattern
)
268 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
269 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
270 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
271 if (!Ccb
->DirectorySearchPattern
)
273 return STATUS_INSUFFICIENT_RESOURCES
;
276 memcpy(Ccb
->DirectorySearchPattern
, SearchPattern
->Buffer
, SearchPattern
->Length
);
277 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
280 else if (!Ccb
->DirectorySearchPattern
)
283 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
284 if (!Ccb
->DirectorySearchPattern
)
286 return STATUS_INSUFFICIENT_RESOURCES
;
289 Ccb
->DirectorySearchPattern
[0] = L
'*';
290 Ccb
->DirectorySearchPattern
[1] = 0;
293 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
294 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
295 DPRINT("In: '%S'\n", Fcb
->PathName
);
297 /* Determine directory index */
298 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
300 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
302 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
307 /* Determine Buffer for result */
310 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
314 Buffer
= Irp
->UserBuffer
;
317 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
319 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
321 Status
= NtfsFindFileAt(DeviceExtension
,
329 if (NT_SUCCESS(Status
))
331 /* HACK: files with both a short name and a long name are present twice in the index.
332 * Ignore the second entry, if it is immediately following the first one.
334 if (MFTRecord
== OldMFTRecord
)
336 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord
);
338 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
341 OldMFTRecord
= MFTRecord
;
343 switch (FileInformationClass
)
345 case FileNameInformation
:
346 Status
= NtfsGetNameInformation(DeviceExtension
,
349 (PFILE_NAMES_INFORMATION
)Buffer
,
353 case FileDirectoryInformation
:
354 Status
= NtfsGetDirectoryInformation(DeviceExtension
,
358 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
362 case FileFullDirectoryInformation
:
363 Status
= NtfsGetFullDirectoryInformation(DeviceExtension
,
367 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
371 case FileBothDirectoryInformation
:
372 Status
= NtfsGetBothDirectoryInformation(DeviceExtension
,
376 (PFILE_BOTH_DIR_INFORMATION
)Buffer
,
381 Status
= STATUS_INVALID_INFO_CLASS
;
384 if (Status
== STATUS_BUFFER_OVERFLOW
)
388 Buffer0
->NextEntryOffset
= 0;
397 Buffer0
->NextEntryOffset
= 0;
402 Status
= STATUS_NO_SUCH_FILE
;
406 Status
= STATUS_NO_MORE_FILES
;
411 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
412 Buffer0
->FileIndex
= FileIndex
++;
415 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
419 BufferLength
-= Buffer0
->NextEntryOffset
;
420 Buffer
+= Buffer0
->NextEntryOffset
;
421 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
426 Buffer0
->NextEntryOffset
= 0;
431 Status
= STATUS_SUCCESS
;
440 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject
,
443 PNTFS_IRP_CONTEXT IrpContext
= NULL
;
444 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
446 DPRINT1("NtfsDirectoryControl() called\n");
448 FsRtlEnterFileSystem();
449 ASSERT(DeviceObject
);
452 NtfsIsIrpTopLevel(Irp
);
454 IrpContext
= NtfsAllocateIrpContext(DeviceObject
, Irp
);
457 switch (IrpContext
->MinorFunction
)
459 case IRP_MN_QUERY_DIRECTORY
:
460 Status
= NtfsQueryDirectory(IrpContext
);
463 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
464 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
465 Status
= STATUS_NOT_IMPLEMENTED
;
469 Status
= STATUS_INVALID_DEVICE_REQUEST
;
474 Status
= STATUS_INSUFFICIENT_RESOURCES
;
476 Irp
->IoStatus
.Status
= Status
;
477 Irp
->IoStatus
.Information
= 0;
478 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
481 ExFreePoolWithTag(IrpContext
, 'PRIN');
483 IoSetTopLevelIrp(NULL
);
484 FsRtlExitFileSystem();