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 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt
,
42 PLARGE_INTEGER StreamOffset
,
49 * FUNCTION: Retrieves the file name, be it in short or long file name format
56 ULONG BlockOffset
= 0;
58 Record
= (PDIR_RECORD
)*Block
;
59 while(Index
< *pIndex
)
61 BlockOffset
+= Record
->RecordLength
;
62 Offset
+= Record
->RecordLength
;
64 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
65 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
67 DPRINT("Map next sector\n");
68 CcUnpinData(*Context
);
69 StreamOffset
->QuadPart
+= BLOCKSIZE
;
70 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
73 if (!CcMapData(DeviceExt
->StreamFileObject
,
78 DPRINT("CcMapData() failed\n");
79 return(STATUS_UNSUCCESSFUL
);
81 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
84 if (Offset
>= DirLength
)
85 return(STATUS_NO_MORE_ENTRIES
);
90 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
91 Index
, Record
->RecordLength
, Offset
);
93 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
97 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
103 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
107 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
108 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
113 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
117 DPRINT("Name '%S'\n", Name
);
123 return(STATUS_SUCCESS
);
129 NtfsGetNameInformation(PDEVICE_EXTENSION DeviceExt
,
130 PFILE_RECORD_HEADER FileRecord
,
131 PNTFS_ATTR_CONTEXT DataContext
,
132 PFILE_NAMES_INFORMATION Info
,
136 PFILENAME_ATTRIBUTE FileName
;
138 DPRINT("NtfsGetNameInformation() called\n");
140 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_POSIX
);
141 if (FileName
== NULL
)
143 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
144 if (FileName
== NULL
)
146 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
149 ASSERT(FileName
!= NULL
);
151 Length
= FileName
->NameLength
* sizeof (WCHAR
);
152 if ((sizeof(FILE_NAMES_INFORMATION
) + Length
) > BufferLength
)
153 return(STATUS_BUFFER_OVERFLOW
);
155 Info
->FileNameLength
= Length
;
156 Info
->NextEntryOffset
=
157 ROUND_UP(sizeof(FILE_NAMES_INFORMATION
) + Length
, sizeof(ULONG
));
158 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
160 return(STATUS_SUCCESS
);
165 NtfsGetDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
166 PFILE_RECORD_HEADER FileRecord
,
167 PNTFS_ATTR_CONTEXT DataContext
,
168 PFILE_DIRECTORY_INFORMATION Info
,
172 PFILENAME_ATTRIBUTE FileName
;
174 DPRINT("NtfsGetDirectoryInformation() called\n");
176 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_POSIX
);
177 if (FileName
== NULL
)
179 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
180 if (FileName
== NULL
)
182 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
185 ASSERT(FileName
!= NULL
);
187 Length
= FileName
->NameLength
* sizeof (WCHAR
);
188 if ((sizeof(FILE_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
189 return(STATUS_BUFFER_OVERFLOW
);
191 Info
->FileNameLength
= Length
;
192 Info
->NextEntryOffset
=
193 ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
194 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
196 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
197 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
198 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
199 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
201 /* Convert file flags */
202 NtfsFileFlagsToAttributes(FileName
->FileAttributes
, &Info
->FileAttributes
);
204 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
205 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
209 return STATUS_SUCCESS
;
214 NtfsGetFullDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
215 PFILE_RECORD_HEADER FileRecord
,
216 PNTFS_ATTR_CONTEXT DataContext
,
217 PFILE_FULL_DIRECTORY_INFORMATION Info
,
221 PFILENAME_ATTRIBUTE FileName
;
223 DPRINT("NtfsGetFullDirectoryInformation() called\n");
225 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_POSIX
);
226 if (FileName
== NULL
)
228 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
229 if (FileName
== NULL
)
231 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
234 ASSERT(FileName
!= NULL
);
236 Length
= FileName
->NameLength
* sizeof (WCHAR
);
237 if ((sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
238 return(STATUS_BUFFER_OVERFLOW
);
240 Info
->FileNameLength
= Length
;
241 Info
->NextEntryOffset
=
242 ROUND_UP(sizeof(FILE_FULL_DIRECTORY_INFORMATION
) + Length
, sizeof(ULONG
));
243 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
245 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
246 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
247 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
248 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
250 /* Convert file flags */
251 NtfsFileFlagsToAttributes(FileName
->FileAttributes
, &Info
->FileAttributes
);
253 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
254 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
259 return STATUS_SUCCESS
;
264 NtfsGetBothDirectoryInformation(PDEVICE_EXTENSION DeviceExt
,
265 PFILE_RECORD_HEADER FileRecord
,
266 PNTFS_ATTR_CONTEXT DataContext
,
267 PFILE_BOTH_DIR_INFORMATION Info
,
271 PFILENAME_ATTRIBUTE FileName
, ShortFileName
;
273 DPRINT("NtfsGetBothDirectoryInformation() called\n");
275 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_POSIX
);
276 if (FileName
== NULL
)
278 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_WIN32
);
279 if (FileName
== NULL
)
281 FileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
284 ASSERT(FileName
!= NULL
);
285 ShortFileName
= GetFileNameFromRecord(FileRecord
, NTFS_FILE_NAME_DOS
);
287 Length
= FileName
->NameLength
* sizeof (WCHAR
);
288 if ((sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
) > BufferLength
)
289 return(STATUS_BUFFER_OVERFLOW
);
291 Info
->FileNameLength
= Length
;
292 Info
->NextEntryOffset
=
293 ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION
) + Length
, sizeof(ULONG
));
294 RtlCopyMemory(Info
->FileName
, FileName
->Name
, Length
);
298 /* Should we upcase the filename? */
299 ASSERT(ShortFileName
->NameLength
<= ARRAYSIZE(Info
->ShortName
));
300 Info
->ShortNameLength
= ShortFileName
->NameLength
* sizeof(WCHAR
);
301 RtlCopyMemory(Info
->ShortName
, ShortFileName
->Name
, Info
->ShortNameLength
);
305 Info
->ShortName
[0] = 0;
306 Info
->ShortNameLength
= 0;
309 Info
->CreationTime
.QuadPart
= FileName
->CreationTime
;
310 Info
->LastAccessTime
.QuadPart
= FileName
->LastAccessTime
;
311 Info
->LastWriteTime
.QuadPart
= FileName
->LastWriteTime
;
312 Info
->ChangeTime
.QuadPart
= FileName
->ChangeTime
;
314 /* Convert file flags */
315 NtfsFileFlagsToAttributes(FileName
->FileAttributes
, &Info
->FileAttributes
);
317 Info
->EndOfFile
.QuadPart
= FileName
->AllocatedSize
;
318 Info
->AllocationSize
.QuadPart
= ROUND_UP(FileName
->AllocatedSize
, DeviceExt
->NtfsInfo
.BytesPerCluster
);
323 return STATUS_SUCCESS
;
328 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
331 PDEVICE_OBJECT DeviceObject
;
332 PDEVICE_EXTENSION DeviceExtension
;
333 LONG BufferLength
= 0;
334 PUNICODE_STRING SearchPattern
= NULL
;
335 FILE_INFORMATION_CLASS FileInformationClass
;
337 PUCHAR Buffer
= NULL
;
338 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
341 BOOLEAN First
= FALSE
;
342 PIO_STACK_LOCATION Stack
;
343 PFILE_OBJECT FileObject
;
344 NTSTATUS Status
= STATUS_SUCCESS
;
345 PFILE_RECORD_HEADER FileRecord
;
346 PNTFS_ATTR_CONTEXT DataContext
;
347 ULONGLONG MFTRecord
, OldMFTRecord
= 0;
348 UNICODE_STRING Pattern
;
350 DPRINT1("NtfsQueryDirectory() called\n");
353 Irp
= IrpContext
->Irp
;
354 DeviceObject
= IrpContext
->DeviceObject
;
356 DeviceExtension
= DeviceObject
->DeviceExtension
;
357 Stack
= IoGetCurrentIrpStackLocation(Irp
);
358 FileObject
= Stack
->FileObject
;
360 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
361 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
363 /* Obtain the callers parameters */
364 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
365 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
366 FileInformationClass
= Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
367 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
369 if (SearchPattern
!= NULL
)
371 if (!Ccb
->DirectorySearchPattern
)
375 Pattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
376 Ccb
->DirectorySearchPattern
= Pattern
.Buffer
=
377 ExAllocatePoolWithTag(NonPagedPool
, Pattern
.MaximumLength
, TAG_NTFS
);
378 if (!Ccb
->DirectorySearchPattern
)
380 return STATUS_INSUFFICIENT_RESOURCES
;
383 Status
= RtlUpcaseUnicodeString(&Pattern
, SearchPattern
, FALSE
);
384 if (!NT_SUCCESS(Status
))
386 DPRINT1("RtlUpcaseUnicodeString('%wZ') failed with status 0x%08lx\n", &Pattern
, Status
);
387 ExFreePoolWithTag(Ccb
->DirectorySearchPattern
, TAG_NTFS
);
388 Ccb
->DirectorySearchPattern
= NULL
;
391 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
394 else if (!Ccb
->DirectorySearchPattern
)
397 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
398 if (!Ccb
->DirectorySearchPattern
)
400 return STATUS_INSUFFICIENT_RESOURCES
;
403 Ccb
->DirectorySearchPattern
[0] = L
'*';
404 Ccb
->DirectorySearchPattern
[1] = 0;
407 RtlInitUnicodeString(&Pattern
, Ccb
->DirectorySearchPattern
);
408 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
409 DPRINT("In: '%S'\n", Fcb
->PathName
);
411 /* Determine directory index */
412 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
414 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
416 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
421 /* Determine Buffer for result */
424 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
428 Buffer
= Irp
->UserBuffer
;
431 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
433 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
435 Status
= NtfsFindFileAt(DeviceExtension
,
443 if (NT_SUCCESS(Status
))
445 /* HACK: files with both a short name and a long name are present twice in the index.
446 * Ignore the second entry, if it is immediately following the first one.
448 if (MFTRecord
== OldMFTRecord
)
450 DPRINT("Ignoring duplicate MFT entry 0x%x\n", MFTRecord
);
452 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
455 OldMFTRecord
= MFTRecord
;
457 switch (FileInformationClass
)
459 case FileNameInformation
:
460 Status
= NtfsGetNameInformation(DeviceExtension
,
463 (PFILE_NAMES_INFORMATION
)Buffer
,
467 case FileDirectoryInformation
:
468 Status
= NtfsGetDirectoryInformation(DeviceExtension
,
471 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
475 case FileFullDirectoryInformation
:
476 Status
= NtfsGetFullDirectoryInformation(DeviceExtension
,
479 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
483 case FileBothDirectoryInformation
:
484 Status
= NtfsGetBothDirectoryInformation(DeviceExtension
,
487 (PFILE_BOTH_DIR_INFORMATION
)Buffer
,
492 Status
= STATUS_INVALID_INFO_CLASS
;
495 if (Status
== STATUS_BUFFER_OVERFLOW
)
499 Buffer0
->NextEntryOffset
= 0;
508 Buffer0
->NextEntryOffset
= 0;
513 Status
= STATUS_NO_SUCH_FILE
;
517 Status
= STATUS_NO_MORE_FILES
;
522 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
523 Buffer0
->FileIndex
= FileIndex
++;
526 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
530 BufferLength
-= Buffer0
->NextEntryOffset
;
531 Buffer
+= Buffer0
->NextEntryOffset
;
532 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
537 Buffer0
->NextEntryOffset
= 0;
542 Status
= STATUS_SUCCESS
;
551 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject
,
554 PNTFS_IRP_CONTEXT IrpContext
= NULL
;
555 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
557 DPRINT1("NtfsDirectoryControl() called\n");
559 FsRtlEnterFileSystem();
560 ASSERT(DeviceObject
);
563 NtfsIsIrpTopLevel(Irp
);
565 IrpContext
= NtfsAllocateIrpContext(DeviceObject
, Irp
);
568 switch (IrpContext
->MinorFunction
)
570 case IRP_MN_QUERY_DIRECTORY
:
571 Status
= NtfsQueryDirectory(IrpContext
);
574 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
575 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
576 Status
= STATUS_NOT_IMPLEMENTED
;
580 Status
= STATUS_INVALID_DEVICE_REQUEST
;
585 Status
= STATUS_INSUFFICIENT_RESOURCES
;
587 Irp
->IoStatus
.Status
= Status
;
588 Irp
->IoStatus
.Information
= 0;
589 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
592 ExFreePoolWithTag(IrpContext
, 'PRIN');
594 IoSetTopLevelIrp(NULL
);
595 FsRtlExitFileSystem();