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)
27 /* INCLUDES *****************************************************************/
34 /* FUNCTIONS ****************************************************************/
38 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt
,
41 PLARGE_INTEGER StreamOffset
,
48 * FUNCTION: Retrieves the file name, be it in short or long file name format
55 ULONG BlockOffset
= 0;
57 Record
= (PDIR_RECORD
)*Block
;
58 while(Index
< *pIndex
)
60 BlockOffset
+= Record
->RecordLength
;
61 Offset
+= Record
->RecordLength
;
63 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
64 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
66 DPRINT("Map next sector\n");
67 CcUnpinData(*Context
);
68 StreamOffset
->QuadPart
+= BLOCKSIZE
;
69 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
72 if (!CcMapData(DeviceExt
->StreamFileObject
,
77 DPRINT("CcMapData() failed\n");
78 return(STATUS_UNSUCCESSFUL
);
80 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
83 if (Offset
>= DirLength
)
84 return(STATUS_NO_MORE_ENTRIES
);
89 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
90 Index
, Record
->RecordLength
, Offset
);
92 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
96 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
102 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
106 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
107 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
112 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
116 DPRINT("Name '%S'\n", Name
);
122 return(STATUS_SUCCESS
);
128 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
129 PFILE_RECORD_HEADER FileRecord
,
130 PNTFS_ATTR_CONTEXT DataContext
,
131 PFILE_NAMES_INFORMATION Info
,
135 PFILENAME_ATTRIBUTE FileName
;
137 DPRINT("NtfsGetNameInformation() called\n");
139 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
140 ASSERT(FileName
!= NULL
);
142 Length
= FileName
->NameLength
* sizeof (WCHAR
);
143 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
144 return(STATUS_BUFFER_OVERFLOW
);
146 Info
->FileNameLength
= Length
;
147 Info
->NextEntryOffset
=
148 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
149 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
151 return(STATUS_SUCCESS
);
156 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
157 PFILE_RECORD_HEADER FileRecord
,
158 PNTFS_ATTR_CONTEXT DataContext
,
159 PFILE_DIRECTORY_INFORMATION Info
,
163 PFILENAME_ATTRIBUTE FileName
;
165 DPRINT("NtfsGetDirectoryInformation() called\n");
167 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
168 ASSERT(FileName
!= NULL
);
170 Length
= FileName
->NameLength
* sizeof (WCHAR
);
171 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
172 return(STATUS_BUFFER_OVERFLOW
);
174 Info
->FileNameLength
= Length
;
175 Info
->NextEntryOffset
=
176 ROUND_UP(sizeof(FILE_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
, &Info
->FileAttributes
);
187 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
188 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
192 return STATUS_SUCCESS
;
197 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
198 PFILE_RECORD_HEADER FileRecord
,
199 PNTFS_ATTR_CONTEXT DataContext
,
200 PFILE_FULL_DIRECTORY_INFORMATION Info
,
204 PFILENAME_ATTRIBUTE FileName
;
206 DPRINT("NtfsGetFullDirectoryInformation() called\n");
208 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
209 ASSERT(FileName
!= NULL
);
211 Length
= FileName
->NameLength
* sizeof (WCHAR
);
212 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
213 return(STATUS_BUFFER_OVERFLOW
);
215 Info
->FileNameLength
= Length
;
216 Info
->NextEntryOffset
=
217 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
218 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
220 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
221 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
222 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
223 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
225 /* Convert file flags */
226 NtfsFileFlagsToAttributes(FileName
->FileAttributes
, &Info
->FileAttributes
);
228 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
229 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
234 return STATUS_SUCCESS
;
239 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
240 PFILE_RECORD_HEADER FileRecord
,
241 PNTFS_ATTR_CONTEXT DataContext
,
242 PFILE_BOTH_DIR_INFORMATION Info
,
246 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
248 DPRINT("NtfsGetBothDirectoryInformation() called\n");
250 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
251 ASSERT(FileName
!= NULL
);
252 ShortFileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
254 Length
= FileName
->NameLength
* sizeof (WCHAR
);
255 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
256 return(STATUS_BUFFER_OVERFLOW
);
258 Info
->FileNameLength
= Length
;
259 Info
->NextEntryOffset
=
260 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
, sizeof(ULONG
));
261 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
265 /* Should we upcase the filename? */
266 ASSERT(ShortFileName
->NameLength
<= ARRAYSIZE(Info
->ShortName
));
267 Info
->ShortNameLength
= ShortFileName
->NameLength
* sizeof(WCHAR
);
268 RtlCopyMemory(Info
->ShortName
, ShortFileName
->Name
, Info
->ShortNameLength
);
272 Info
->ShortName
[0] = 0;
273 Info
->ShortNameLength
= 0;
276 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
277 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
278 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
279 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
281 /* Convert file flags */
282 NtfsFileFlagsToAttributes(FileName
->FileAttributes
, &Info
->FileAttributes
);
284 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
285 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
290 return STATUS_SUCCESS
;
295 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
298 PDEVICE_OBJECT DeviceObject
;
299 PDEVICE_EXTENSION DeviceExtension
;
300 LONG BufferLength
= 0;
301 PUNICODE_STRING SearchPattern
= NULL
;
302 FILE_INFORMATION_CLASS FileInformationClass
;
304 PUCHAR Buffer
= NULL
;
305 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
308 BOOLEAN First
= FALSE
;
309 PIO_STACK_LOCATION Stack
;
310 PFILE_OBJECT FileObject
;
311 NTSTATUS Status
= STATUS_SUCCESS
;
312 PFILE_RECORD_HEADER FileRecord
;
313 PNTFS_ATTR_CONTEXT DataContext
;
314 ULONGLONG MFTRecord
, OldMFTRecord
= 0;
315 UNICODE_STRING Pattern
;
317 DPRINT1("NtfsQueryDirectory() called\n");
320 Irp
= IrpContext
->Irp
;
321 DeviceObject
= IrpContext
->DeviceObject
;
323 DeviceExtension
= DeviceObject
->DeviceExtension
;
324 Stack
= IoGetCurrentIrpStackLocation(Irp
);
325 FileObject
= Stack
->FileObject
;
327 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
328 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
330 /* Obtain the callers parameters */
331 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
332 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
333 FileInformationClass
= Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
334 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
336 if (SearchPattern
!= NULL
)
338 if (!Ccb
->DirectorySearchPattern
)
342 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
343 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
344 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
345 if (!Ccb
->DirectorySearchPattern
)
347 return STATUS_INSUFFICIENT_RESOURCES
;
350 Status
= RtlUpcaseUnicodeString(&Pattern
, SearchPattern
, FALSE
);
351 if (!NT_SUCCESS(Status
))
353 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern
, Status
);
354 ExFreePoolWithTag(Ccb
->DirectorySearchPattern
, TAG_NTFS
);
355 Ccb
->DirectorySearchPattern
= NULL
;
358 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
361 else if (!Ccb
->DirectorySearchPattern
)
364 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
365 if (!Ccb
->DirectorySearchPattern
)
367 return STATUS_INSUFFICIENT_RESOURCES
;
370 Ccb
->DirectorySearchPattern
[0] = L
'*';
371 Ccb
->DirectorySearchPattern
[1] = 0;
374 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
375 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
376 DPRINT("In: '%S'\n", Fcb
->PathName
);
378 /* Determine directory index */
379 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
381 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
383 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
388 /* Determine Buffer for result */
391 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
395 Buffer
= Irp
->UserBuffer
;
398 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
400 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
402 Status
= NtfsFindFileAt(DeviceExtension
,
410 if (NT_SUCCESS(Status
))
412 /* HACK: files with both a short name and a long name are present twice in the index.
413 * Ignore the second entry, if it is immediately following the first one.
415 if (MFTRecord
== OldMFTRecord
)
417 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord
);
419 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
422 OldMFTRecord
= MFTRecord
;
424 switch (FileInformationClass
)
426 case FileNameInformation
:
427 Status
= NtfsGetNameInformation(DeviceExtension
,
430 (PFILE_NAMES_INFORMATION
)Buffer
,
434 case FileDirectoryInformation
:
435 Status
= NtfsGetDirectoryInformation(DeviceExtension
,
438 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
442 case FileFullDirectoryInformation
:
443 Status
= NtfsGetFullDirectoryInformation(DeviceExtension
,
446 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
450 case FileBothDirectoryInformation
:
451 Status
= NtfsGetBothDirectoryInformation(DeviceExtension
,
454 (PFILE_BOTH_DIR_INFORMATION
)Buffer
,
459 Status
= STATUS_INVALID_INFO_CLASS
;
462 if (Status
== STATUS_BUFFER_OVERFLOW
)
466 Buffer0
->NextEntryOffset
= 0;
475 Buffer0
->NextEntryOffset
= 0;
480 Status
= STATUS_NO_SUCH_FILE
;
484 Status
= STATUS_NO_MORE_FILES
;
489 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
490 Buffer0
->FileIndex
= FileIndex
++;
493 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
497 BufferLength
-= Buffer0
->NextEntryOffset
;
498 Buffer
+= Buffer0
->NextEntryOffset
;
499 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
504 Buffer0
->NextEntryOffset
= 0;
509 Status
= STATUS_SUCCESS
;
518 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject
,
521 PNTFS_IRP_CONTEXT IrpContext
= NULL
;
522 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
524 DPRINT1("NtfsDirectoryControl() called\n");
526 FsRtlEnterFileSystem();
527 ASSERT(DeviceObject
);
530 NtfsIsIrpTopLevel(Irp
);
532 IrpContext
= NtfsAllocateIrpContext(DeviceObject
, Irp
);
535 switch (IrpContext
->MinorFunction
)
537 case IRP_MN_QUERY_DIRECTORY
:
538 Status
= NtfsQueryDirectory(IrpContext
);
541 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
542 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
543 Status
= STATUS_NOT_IMPLEMENTED
;
547 Status
= STATUS_INVALID_DEVICE_REQUEST
;
552 Status
= STATUS_INSUFFICIENT_RESOURCES
;
554 Irp
->IoStatus
.Status
= Status
;
555 Irp
->IoStatus
.Information
= 0;
556 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
559 ExFreePoolWithTag(IrpContext
, 'PRIN');
561 IoSetTopLevelIrp(NULL
);
562 FsRtlExitFileSystem();