3 * Copyright (C) 2002,2003 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 * PROGRAMMER: Eric Kohl
26 /* INCLUDES *****************************************************************/
33 /* FUNCTIONS ****************************************************************/
37 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt
,
40 PLARGE_INTEGER StreamOffset
,
47 * FUNCTION: Retrieves the file name, be it in short or long file name format
54 ULONG BlockOffset
= 0;
56 Record
= (PDIR_RECORD
)*Block
;
57 while(Index
< *pIndex
)
59 BlockOffset
+= Record
->RecordLength
;
60 Offset
+= Record
->RecordLength
;
62 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
63 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
65 DPRINT("Map next sector\n");
66 CcUnpinData(*Context
);
67 StreamOffset
->QuadPart
+= BLOCKSIZE
;
68 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
71 if (!CcMapData(DeviceExt
->StreamFileObject
,
76 DPRINT("CcMapData() failed\n");
77 return(STATUS_UNSUCCESSFUL
);
79 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
82 if (Offset
>= DirLength
)
83 return(STATUS_NO_MORE_ENTRIES
);
88 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
89 Index
, Record
->RecordLength
, Offset
);
91 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
95 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
101 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
105 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
106 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
111 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
115 DPRINT("Name '%S'\n", Name
);
121 return(STATUS_SUCCESS
);
126 CdfsFindFile(PDEVICE_EXTENSION DeviceExt
,
133 * FUNCTION: Find a file
145 PVOID Context
= NULL
;
149 LARGE_INTEGER StreamOffset
;
151 DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
152 Parent
, FileToFind
, pDirIndex
? *pDirIndex
: 0);
153 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
154 Fcb
->PathName
, Fcb
->ObjectName
);
158 if (wcslen (FileToFind
) == 0)
161 TempStr
[0] = (WCHAR
) '.';
163 FileToFind
= (PWSTR
)&TempStr
;
168 if (Parent
->Entry
.ExtentLocationL
== DeviceExt
->CdInfo
.RootStart
)
180 StreamOffset
.QuadPart
= (LONGLONG
)DeviceExt
->CdInfo
.RootStart
* (LONGLONG
)BLOCKSIZE
;
181 DirSize
= DeviceExt
->CdInfo
.RootSize
;
184 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
185 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
187 /* it's root : complete essentials fields then return ok */
188 RtlZeroMemory(Fcb
, sizeof(FCB
));
190 Fcb
->PathName
[0]='\\';
191 Fcb
->ObjectName
= &Fcb
->PathName
[1];
192 Fcb
->Entry
.ExtentLocationL
= DeviceExt
->CdInfo
.RootStart
;
193 Fcb
->Entry
.DataLengthL
= DeviceExt
->CdInfo
.RootSize
;
194 Fcb
->Entry
.FileFlags
= 0x02; //FILE_ATTRIBUTE_DIRECTORY;
200 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
201 return (STATUS_SUCCESS
);
206 StreamOffset
.QuadPart
= (LONGLONG
)Parent
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
207 DirSize
= Parent
->Entry
.DataLengthL
;
210 DPRINT("StreamOffset %I64u DirSize %lu\n", StreamOffset
.QuadPart
, DirSize
);
212 if (pDirIndex
&& (*pDirIndex
))
213 DirIndex
= *pDirIndex
;
215 if(!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
216 BLOCKSIZE
, TRUE
, &Context
, &Block
))
218 DPRINT("CcMapData() failed\n");
219 return(STATUS_UNSUCCESSFUL
);
225 Record
= (PDIR_RECORD
)Ptr
;
226 if (Record
->RecordLength
== 0)
228 DPRINT1("Stopped!\n");
232 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
233 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
235 Status
= CdfsGetEntryName(DeviceExt
, &Context
, &Block
, &StreamOffset
,
236 DirSize
, (PVOID
*)&Ptr
, name
, &DirIndex
, pDirIndex2
);
237 if (Status
== STATUS_NO_MORE_ENTRIES
)
241 else if (Status
== STATUS_UNSUCCESSFUL
)
243 /* Note: the directory cache has already been unpinned */
247 DPRINT("Name '%S'\n", name
);
249 if (wstrcmpjoki(name
, FileToFind
)) /* || wstrcmpjoki (name2, FileToFind)) */
251 if (Parent
&& Parent
->PathName
)
253 len
= wcslen(Parent
->PathName
);
254 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
255 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
256 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
258 Fcb
->ObjectName
[0] = '\\';
259 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
264 Fcb
->ObjectName
=Fcb
->PathName
;
265 Fcb
->ObjectName
[0]='\\';
266 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
269 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb
->PathName
, Fcb
->ObjectName
);
271 memcpy(&Fcb
->Entry
, Ptr
, sizeof(DIR_RECORD
));
272 wcsncpy(Fcb
->ObjectName
, name
, MAX_PATH
);
274 *pDirIndex
= DirIndex
;
276 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
277 Fcb
->PathName
, Fcb
->ObjectName
, DirIndex
);
279 CcUnpinData(Context
);
281 return(STATUS_SUCCESS
);
285 Ptr
= Ptr
+ Record
->RecordLength
;
288 if (((ULONG
)Ptr
- (ULONG
)Block
) >= DirSize
)
290 DPRINT("Stopped!\n");
295 CcUnpinData(Context
);
298 *pDirIndex
= DirIndex
;
300 return(STATUS_UNSUCCESSFUL
);
305 CdfsGetNameInformation(PFCB Fcb
,
306 PDEVICE_EXTENSION DeviceExt
,
307 PFILE_NAMES_INFORMATION Info
,
312 DPRINT("CdfsGetNameInformation() called\n");
314 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
315 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
316 return(STATUS_BUFFER_OVERFLOW
);
318 Info
->FileNameLength
= Length
;
319 Info
->NextEntryOffset
=
320 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
321 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
323 return(STATUS_SUCCESS
);
328 CdfsGetDirectoryInformation(PFCB Fcb
,
329 PDEVICE_EXTENSION DeviceExt
,
330 PFILE_DIRECTORY_INFORMATION Info
,
335 DPRINT("CdfsGetDirectoryInformation() called\n");
337 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
338 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
339 return(STATUS_BUFFER_OVERFLOW
);
341 Info
->FileNameLength
= Length
;
342 Info
->NextEntryOffset
=
343 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
344 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
346 /* Convert file times */
347 CdfsDateTimeToFileTime(Fcb
,
348 &Info
->CreationTime
);
349 CdfsDateTimeToFileTime(Fcb
,
350 &Info
->LastAccessTime
);
351 CdfsDateTimeToFileTime(Fcb
,
352 &Info
->LastWriteTime
);
353 CdfsDateTimeToFileTime(Fcb
,
356 /* Convert file flags */
357 CdfsFileFlagsToAttributes(Fcb
,
358 &Info
->FileAttributes
);
360 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
362 /* Make AllocSize a rounded up multiple of the sector size */
363 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
367 return(STATUS_SUCCESS
);
372 CdfsGetFullDirectoryInformation(PFCB Fcb
,
373 PDEVICE_EXTENSION DeviceExt
,
374 PFILE_FULL_DIRECTORY_INFORMATION Info
,
379 DPRINT("CdfsGetFullDirectoryInformation() called\n");
381 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
382 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
383 return(STATUS_BUFFER_OVERFLOW
);
385 Info
->FileNameLength
= Length
;
386 Info
->NextEntryOffset
=
387 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
388 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
390 /* Convert file times */
391 CdfsDateTimeToFileTime(Fcb
,
392 &Info
->CreationTime
);
393 CdfsDateTimeToFileTime(Fcb
,
394 &Info
->LastAccessTime
);
395 CdfsDateTimeToFileTime(Fcb
,
396 &Info
->LastWriteTime
);
397 CdfsDateTimeToFileTime(Fcb
,
400 /* Convert file flags */
401 CdfsFileFlagsToAttributes(Fcb
,
402 &Info
->FileAttributes
);
404 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
406 /* Make AllocSize a rounded up multiple of the sector size */
407 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
412 return(STATUS_SUCCESS
);
417 CdfsGetBothDirectoryInformation(PFCB Fcb
,
418 PDEVICE_EXTENSION DeviceExt
,
419 PFILE_BOTH_DIRECTORY_INFORMATION Info
,
424 DPRINT("CdfsGetBothDirectoryInformation() called\n");
426 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
427 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
428 return(STATUS_BUFFER_OVERFLOW
);
430 Info
->FileNameLength
= Length
;
431 Info
->NextEntryOffset
=
432 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
433 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
435 /* Convert file times */
436 CdfsDateTimeToFileTime(Fcb
,
437 &Info
->CreationTime
);
438 CdfsDateTimeToFileTime(Fcb
,
439 &Info
->LastAccessTime
);
440 CdfsDateTimeToFileTime(Fcb
,
441 &Info
->LastWriteTime
);
442 CdfsDateTimeToFileTime(Fcb
,
445 /* Convert file flags */
446 CdfsFileFlagsToAttributes(Fcb
,
447 &Info
->FileAttributes
);
449 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
451 /* Make AllocSize a rounded up multiple of the sector size */
452 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
457 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
459 /* Standard ISO-9660 format */
460 Info
->ShortNameLength
= Length
;
461 memcpy(Info
->ShortName
, Fcb
->ObjectName
, Length
);
465 /* Joliet extension */
467 /* FIXME: Copy or create a short file name */
469 Info
->ShortName
[0] = 0;
470 Info
->ShortNameLength
= 0;
473 return(STATUS_SUCCESS
);
479 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
482 //PDEVICE_OBJECT DeviceObject;
483 //PDEVICE_EXTENSION DeviceExtension;
484 //LONG BufferLength = 0;
485 PUNICODE_STRING SearchPattern
= NULL
;
486 //FILE_INFORMATION_CLASS FileInformationClass;
488 PUCHAR Buffer
= NULL
;
489 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
493 BOOLEAN First
= FALSE
;
494 PIO_STACK_LOCATION Stack
;
495 PFILE_OBJECT FileObject
;
496 //NTSTATUS Status = STATUS_SUCCESS;
498 DPRINT1("NtfsQueryDirectory() called\n");
501 Irp
= IrpContext
->Irp
;
502 // DeviceObject = IrpContext->DeviceObject;
504 // DeviceExtension = DeviceObject->DeviceExtension;
505 Stack
= IoGetCurrentIrpStackLocation(Irp
);
506 FileObject
= Stack
->FileObject
;
508 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
509 // Fcb = (PNTFS_FCB)FileObject->FsContext;
511 /* Obtain the callers parameters */
512 //BufferLength = Stack->Parameters.QueryDirectory.Length;
513 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
514 //FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
515 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
518 if (SearchPattern
!= NULL
)
520 if (!Ccb
->DirectorySearchPattern
)
523 Ccb
->DirectorySearchPattern
=
524 ExAllocatePoolWithTag(NonPagedPool
, SearchPattern
->Length
+ sizeof(WCHAR
), TAG_NTFS
);
525 if (!Ccb
->DirectorySearchPattern
)
527 return STATUS_INSUFFICIENT_RESOURCES
;
530 memcpy(Ccb
->DirectorySearchPattern
,
531 SearchPattern
->Buffer
,
532 SearchPattern
->Length
);
533 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
536 else if (!Ccb
->DirectorySearchPattern
)
539 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
540 if (!Ccb
->DirectorySearchPattern
)
542 return STATUS_INSUFFICIENT_RESOURCES
;
545 Ccb
->DirectorySearchPattern
[0] = L
'*';
546 Ccb
->DirectorySearchPattern
[1] = 0;
549 DPRINT("Search pattern '%S'\n", Ccb
->DirectorySearchPattern
);
551 /* Determine directory index */
552 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
554 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
556 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
561 /* Determine Buffer for result */
564 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
568 Buffer
= Irp
->UserBuffer
;
571 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
574 TempFcb
.ObjectName
= TempFcb
.PathName
;
575 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
577 Status
= CdfsFindFile(DeviceExtension
,
580 Ccb
->DirectorySearchPattern
,
583 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb
.ObjectName
, Status
, Ccb
->Entry
);
585 if (NT_SUCCESS(Status
))
587 switch (FileInformationClass
)
589 case FileNameInformation
:
590 Status
= CdfsGetNameInformation(&TempFcb
,
592 (PFILE_NAMES_INFORMATION
)Buffer
,
596 case FileDirectoryInformation
:
597 Status
= CdfsGetDirectoryInformation(&TempFcb
,
599 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
603 case FileFullDirectoryInformation
:
604 Status
= CdfsGetFullDirectoryInformation(&TempFcb
,
606 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
610 case FileBothDirectoryInformation
:
611 Status
= NtfsGetBothDirectoryInformation(&TempFcb
,
613 (PFILE_BOTH_DIRECTORY_INFORMATION
)Buffer
,
618 Status
= STATUS_INVALID_INFO_CLASS
;
621 if (Status
== STATUS_BUFFER_OVERFLOW
)
625 Buffer0
->NextEntryOffset
= 0;
634 Buffer0
->NextEntryOffset
= 0;
639 Status
= STATUS_NO_SUCH_FILE
;
643 Status
= STATUS_NO_MORE_FILES
;
648 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
649 Buffer0
->FileIndex
= FileIndex
++;
652 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
656 BufferLength
-= Buffer0
->NextEntryOffset
;
657 Buffer
+= Buffer0
->NextEntryOffset
;
663 Buffer0
->NextEntryOffset
= 0;
668 //Status = STATUS_SUCCESS;
672 return STATUS_NO_MORE_FILES
;
678 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject
,
681 PNTFS_IRP_CONTEXT IrpContext
= NULL
;
682 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
684 DPRINT1("NtfsDirectoryControl() called\n");
686 FsRtlEnterFileSystem();
687 ASSERT(DeviceObject
);
690 NtfsIsIrpTopLevel(Irp
);
692 IrpContext
= NtfsAllocateIrpContext(DeviceObject
, Irp
);
695 switch (IrpContext
->MinorFunction
)
697 case IRP_MN_QUERY_DIRECTORY
:
698 Status
= NtfsQueryDirectory(IrpContext
);
701 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
702 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
703 Status
= STATUS_NOT_IMPLEMENTED
;
707 Status
= STATUS_INVALID_DEVICE_REQUEST
;
712 Status
= STATUS_INSUFFICIENT_RESOURCES
;
714 Irp
->IoStatus
.Status
= Status
;
715 Irp
->IoStatus
.Information
= 0;
716 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
719 ExFreePoolWithTag(IrpContext
, 'PRIN');
721 IoSetTopLevelIrp(NULL
);
722 FsRtlExitFileSystem();