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 /* GLOBALS *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
40 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt
,
43 PLARGE_INTEGER StreamOffset
,
50 * FUNCTION: Retrieves the file name, be it in short or long file name format
57 ULONG BlockOffset
= 0;
59 Record
= (PDIR_RECORD
)*Block
;
60 while(Index
< *pIndex
)
62 BlockOffset
+= Record
->RecordLength
;
63 Offset
+= Record
->RecordLength
;
65 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
66 if (BlockOffset
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
68 DPRINT("Map next sector\n");
69 CcUnpinData(*Context
);
70 StreamOffset
->QuadPart
+= BLOCKSIZE
;
71 Offset
= ROUND_UP(Offset
, BLOCKSIZE
);
74 if (!CcMapData(DeviceExt
->StreamFileObject
,
79 DPRINT("CcMapData() failed\n");
80 return(STATUS_UNSUCCESSFUL
);
82 Record
= (PDIR_RECORD
)(*Block
+ BlockOffset
);
85 if (Offset
>= DirLength
)
86 return(STATUS_NO_MORE_ENTRIES
);
91 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
92 Index
, Record
->RecordLength
, Offset
);
94 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
98 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
104 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
108 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
109 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
114 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
118 DPRINT("Name '%S'\n", Name
);
124 return(STATUS_SUCCESS
);
129 CdfsFindFile(PDEVICE_EXTENSION DeviceExt
,
136 * FUNCTION: Find a file
148 PVOID Context
= NULL
;
152 LARGE_INTEGER StreamOffset
;
154 DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
155 Parent
, FileToFind
, pDirIndex
? *pDirIndex
: 0);
156 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
157 Fcb
->PathName
, Fcb
->ObjectName
);
161 if (wcslen (FileToFind
) == 0)
164 TempStr
[0] = (WCHAR
) '.';
166 FileToFind
= (PWSTR
)&TempStr
;
171 if (Parent
->Entry
.ExtentLocationL
== DeviceExt
->CdInfo
.RootStart
)
183 StreamOffset
.QuadPart
= (LONGLONG
)DeviceExt
->CdInfo
.RootStart
* (LONGLONG
)BLOCKSIZE
;
184 DirSize
= DeviceExt
->CdInfo
.RootSize
;
187 if (FileToFind
[0] == 0 || (FileToFind
[0] == '\\' && FileToFind
[1] == 0)
188 || (FileToFind
[0] == '.' && FileToFind
[1] == 0))
190 /* it's root : complete essentials fields then return ok */
191 RtlZeroMemory(Fcb
, sizeof(FCB
));
193 Fcb
->PathName
[0]='\\';
194 Fcb
->ObjectName
= &Fcb
->PathName
[1];
195 Fcb
->Entry
.ExtentLocationL
= DeviceExt
->CdInfo
.RootStart
;
196 Fcb
->Entry
.DataLengthL
= DeviceExt
->CdInfo
.RootSize
;
197 Fcb
->Entry
.FileFlags
= 0x02; //FILE_ATTRIBUTE_DIRECTORY;
203 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
204 return (STATUS_SUCCESS
);
209 StreamOffset
.QuadPart
= (LONGLONG
)Parent
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
210 DirSize
= Parent
->Entry
.DataLengthL
;
213 DPRINT("StreamOffset %I64u DirSize %lu\n", StreamOffset
.QuadPart
, DirSize
);
215 if (pDirIndex
&& (*pDirIndex
))
216 DirIndex
= *pDirIndex
;
218 if(!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
219 BLOCKSIZE
, TRUE
, &Context
, &Block
))
221 DPRINT("CcMapData() failed\n");
222 return(STATUS_UNSUCCESSFUL
);
228 Record
= (PDIR_RECORD
)Ptr
;
229 if (Record
->RecordLength
== 0)
231 DPRINT1("Stopped!\n");
235 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
236 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
238 Status
= CdfsGetEntryName(DeviceExt
, &Context
, &Block
, &StreamOffset
,
239 DirSize
, (PVOID
*)&Ptr
, name
, &DirIndex
, pDirIndex2
);
240 if (Status
== STATUS_NO_MORE_ENTRIES
)
244 else if (Status
== STATUS_UNSUCCESSFUL
)
246 /* Note: the directory cache has already been unpinned */
250 DPRINT("Name '%S'\n", name
);
252 if (wstrcmpjoki(name
, FileToFind
)) /* || wstrcmpjoki (name2, FileToFind)) */
254 if (Parent
&& Parent
->PathName
)
256 len
= wcslen(Parent
->PathName
);
257 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
258 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
259 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
261 Fcb
->ObjectName
[0] = '\\';
262 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
267 Fcb
->ObjectName
=Fcb
->PathName
;
268 Fcb
->ObjectName
[0]='\\';
269 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
272 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb
->PathName
, Fcb
->ObjectName
);
274 memcpy(&Fcb
->Entry
, Ptr
, sizeof(DIR_RECORD
));
275 wcsncpy(Fcb
->ObjectName
, name
, MAX_PATH
);
277 *pDirIndex
= DirIndex
;
279 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
280 Fcb
->PathName
, Fcb
->ObjectName
, DirIndex
);
282 CcUnpinData(Context
);
284 return(STATUS_SUCCESS
);
288 Ptr
= Ptr
+ Record
->RecordLength
;
291 if (((ULONG
)Ptr
- (ULONG
)Block
) >= DirSize
)
293 DPRINT("Stopped!\n");
298 CcUnpinData(Context
);
301 *pDirIndex
= DirIndex
;
303 return(STATUS_UNSUCCESSFUL
);
308 CdfsGetNameInformation(PFCB Fcb
,
309 PDEVICE_EXTENSION DeviceExt
,
310 PFILE_NAMES_INFORMATION Info
,
315 DPRINT("CdfsGetNameInformation() called\n");
317 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
318 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
319 return(STATUS_BUFFER_OVERFLOW
);
321 Info
->FileNameLength
= Length
;
322 Info
->NextEntryOffset
=
323 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
324 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
326 return(STATUS_SUCCESS
);
331 CdfsGetDirectoryInformation(PFCB Fcb
,
332 PDEVICE_EXTENSION DeviceExt
,
333 PFILE_DIRECTORY_INFORMATION Info
,
338 DPRINT("CdfsGetDirectoryInformation() called\n");
340 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
341 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
342 return(STATUS_BUFFER_OVERFLOW
);
344 Info
->FileNameLength
= Length
;
345 Info
->NextEntryOffset
=
346 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
347 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
349 /* Convert file times */
350 CdfsDateTimeToFileTime(Fcb
,
351 &Info
->CreationTime
);
352 CdfsDateTimeToFileTime(Fcb
,
353 &Info
->LastAccessTime
);
354 CdfsDateTimeToFileTime(Fcb
,
355 &Info
->LastWriteTime
);
356 CdfsDateTimeToFileTime(Fcb
,
359 /* Convert file flags */
360 CdfsFileFlagsToAttributes(Fcb
,
361 &Info
->FileAttributes
);
363 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
365 /* Make AllocSize a rounded up multiple of the sector size */
366 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
370 return(STATUS_SUCCESS
);
375 CdfsGetFullDirectoryInformation(PFCB Fcb
,
376 PDEVICE_EXTENSION DeviceExt
,
377 PFILE_FULL_DIRECTORY_INFORMATION Info
,
382 DPRINT("CdfsGetFullDirectoryInformation() called\n");
384 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
385 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
386 return(STATUS_BUFFER_OVERFLOW
);
388 Info
->FileNameLength
= Length
;
389 Info
->NextEntryOffset
=
390 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
391 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
393 /* Convert file times */
394 CdfsDateTimeToFileTime(Fcb
,
395 &Info
->CreationTime
);
396 CdfsDateTimeToFileTime(Fcb
,
397 &Info
->LastAccessTime
);
398 CdfsDateTimeToFileTime(Fcb
,
399 &Info
->LastWriteTime
);
400 CdfsDateTimeToFileTime(Fcb
,
403 /* Convert file flags */
404 CdfsFileFlagsToAttributes(Fcb
,
405 &Info
->FileAttributes
);
407 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
409 /* Make AllocSize a rounded up multiple of the sector size */
410 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
415 return(STATUS_SUCCESS
);
420 CdfsGetBothDirectoryInformation(PFCB Fcb
,
421 PDEVICE_EXTENSION DeviceExt
,
422 PFILE_BOTH_DIRECTORY_INFORMATION Info
,
427 DPRINT("CdfsGetBothDirectoryInformation() called\n");
429 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
430 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
431 return(STATUS_BUFFER_OVERFLOW
);
433 Info
->FileNameLength
= Length
;
434 Info
->NextEntryOffset
=
435 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
436 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
438 /* Convert file times */
439 CdfsDateTimeToFileTime(Fcb
,
440 &Info
->CreationTime
);
441 CdfsDateTimeToFileTime(Fcb
,
442 &Info
->LastAccessTime
);
443 CdfsDateTimeToFileTime(Fcb
,
444 &Info
->LastWriteTime
);
445 CdfsDateTimeToFileTime(Fcb
,
448 /* Convert file flags */
449 CdfsFileFlagsToAttributes(Fcb
,
450 &Info
->FileAttributes
);
452 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
454 /* Make AllocSize a rounded up multiple of the sector size */
455 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
460 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
462 /* Standard ISO-9660 format */
463 Info
->ShortNameLength
= Length
;
464 memcpy(Info
->ShortName
, Fcb
->ObjectName
, Length
);
468 /* Joliet extension */
470 /* FIXME: Copy or create a short file name */
472 Info
->ShortName
[0] = 0;
473 Info
->ShortNameLength
= 0;
476 return(STATUS_SUCCESS
);
481 NtfsQueryDirectory(PNTFS_IRP_CONTEXT IrpContext
)
484 //PDEVICE_OBJECT DeviceObject;
485 //PDEVICE_EXTENSION DeviceExtension;
486 //LONG BufferLength = 0;
487 PUNICODE_STRING SearchPattern
= NULL
;
488 //FILE_INFORMATION_CLASS FileInformationClass;
490 PUCHAR Buffer
= NULL
;
491 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
495 BOOLEAN First
= FALSE
;
496 PIO_STACK_LOCATION Stack
;
497 PFILE_OBJECT FileObject
;
498 //NTSTATUS Status = STATUS_SUCCESS;
500 DPRINT1("NtfsQueryDirectory() called\n");
503 Irp
= IrpContext
->Irp
;
504 //DeviceObject = IrpContext->DeviceObject;
506 //DeviceExtension = DeviceObject->DeviceExtension;
507 Stack
= IoGetCurrentIrpStackLocation(Irp
);
508 FileObject
= Stack
->FileObject
;
510 Ccb
= (PNTFS_CCB
)FileObject
->FsContext2
;
511 //Fcb = (PNTFS_FCB)FileObject->FsContext;
513 /* Obtain the callers parameters */
514 //BufferLength = Stack->Parameters.QueryDirectory.Length;
515 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
516 //FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
517 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
520 if (SearchPattern
!= NULL
)
522 if (!Ccb
->DirectorySearchPattern
)
525 Ccb
->DirectorySearchPattern
=
526 ExAllocatePoolWithTag(NonPagedPool
, SearchPattern
->Length
+ sizeof(WCHAR
), TAG_NTFS
);
527 if (!Ccb
->DirectorySearchPattern
)
529 return(STATUS_INSUFFICIENT_RESOURCES
);
532 memcpy(Ccb
->DirectorySearchPattern
,
533 SearchPattern
->Buffer
,
534 SearchPattern
->Length
);
535 Ccb
->DirectorySearchPattern
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
538 else if (!Ccb
->DirectorySearchPattern
)
541 Ccb
->DirectorySearchPattern
= ExAllocatePoolWithTag(NonPagedPool
, 2 * sizeof(WCHAR
), TAG_NTFS
);
542 if (!Ccb
->DirectorySearchPattern
)
544 return(STATUS_INSUFFICIENT_RESOURCES
);
546 Ccb
->DirectorySearchPattern
[0] = L
'*';
547 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
;
570 DPRINT("Buffer=%p tofind=%S\n", Buffer
, Ccb
->DirectorySearchPattern
);
572 TempFcb
.ObjectName
= TempFcb
.PathName
;
573 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
575 Status
= CdfsFindFile(DeviceExtension
,
578 Ccb
->DirectorySearchPattern
,
581 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb
.ObjectName
, Status
, Ccb
->Entry
);
583 if (NT_SUCCESS(Status
))
585 switch (FileInformationClass
)
587 case FileNameInformation
:
588 Status
= CdfsGetNameInformation(&TempFcb
,
590 (PFILE_NAMES_INFORMATION
)Buffer
,
594 case FileDirectoryInformation
:
595 Status
= CdfsGetDirectoryInformation(&TempFcb
,
597 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
601 case FileFullDirectoryInformation
:
602 Status
= CdfsGetFullDirectoryInformation(&TempFcb
,
604 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
608 case FileBothDirectoryInformation
:
609 Status
= NtfsGetBothDirectoryInformation(&TempFcb
,
611 (PFILE_BOTH_DIRECTORY_INFORMATION
)Buffer
,
616 Status
= STATUS_INVALID_INFO_CLASS
;
619 if (Status
== STATUS_BUFFER_OVERFLOW
)
623 Buffer0
->NextEntryOffset
= 0;
632 Buffer0
->NextEntryOffset
= 0;
637 Status
= STATUS_NO_SUCH_FILE
;
641 Status
= STATUS_NO_MORE_FILES
;
646 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
647 Buffer0
->FileIndex
= FileIndex
++;
650 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
654 BufferLength
-= Buffer0
->NextEntryOffset
;
655 Buffer
+= Buffer0
->NextEntryOffset
;
661 Buffer0
->NextEntryOffset
= 0;
666 //Status = STATUS_SUCCESS;
670 return(STATUS_NO_MORE_FILES
);
676 NtfsFsdDirectoryControl(PDEVICE_OBJECT DeviceObject
,
679 PNTFS_IRP_CONTEXT IrpContext
= NULL
;
680 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
682 DPRINT1("NtfsDirectoryControl() called\n");
684 FsRtlEnterFileSystem();
685 ASSERT(DeviceObject
);
688 NtfsIsIrpTopLevel(Irp
);
690 IrpContext
= NtfsAllocateIrpContext(DeviceObject
, Irp
);
693 switch (IrpContext
->MinorFunction
)
695 case IRP_MN_QUERY_DIRECTORY
:
696 Status
= NtfsQueryDirectory(IrpContext
);
699 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
700 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
701 Status
= STATUS_NOT_IMPLEMENTED
;
705 Status
= STATUS_INVALID_DEVICE_REQUEST
;
710 Status
= STATUS_INSUFFICIENT_RESOURCES
;
712 Irp
->IoStatus
.Status
= Status
;
713 Irp
->IoStatus
.Information
= 0;
714 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
717 ExFreePoolWithTag(IrpContext
, 'PRIN');
719 IoSetTopLevelIrp(NULL
);
720 FsRtlExitFileSystem();