3 * Copyright (C) 2002, 2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: dirctl.c,v 1.16 2004/09/14 21:46:39 ekohl Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/fs/cdfs/dirctl.c
24 * PURPOSE: CDROM (ISO 9660) filesystem driver
25 * PROGRAMMER: Art Yerkes
30 /* INCLUDES *****************************************************************/
32 #include <ddk/ntddk.h>
39 /* DEFINES ******************************************************************/
41 #define ROUND_DOWN(N, S) (((N) / (S)) * (S))
43 /* FUNCTIONS ****************************************************************/
46 * FUNCTION: Retrieves the file name, be it in short or long file name format
49 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt
,
52 PLARGE_INTEGER StreamOffset
,
59 PDIR_RECORD Record
= *Ptr
;
62 if (*CurrentOffset
>= DirLength
)
63 return(STATUS_NO_MORE_ENTRIES
);
65 if (*CurrentOffset
== 0)
68 Record
= (PDIR_RECORD
)*Block
;
69 while (Index
< *pIndex
)
71 (*Ptr
) += Record
->RecordLength
;
72 (*CurrentOffset
) += Record
->RecordLength
;
74 if (*Ptr
- *Block
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
76 DPRINT("Map next sector\n");
77 CcUnpinData(*Context
);
78 StreamOffset
->QuadPart
+= BLOCKSIZE
;
79 *CurrentOffset
= ROUND_UP(*CurrentOffset
, BLOCKSIZE
);
80 if (!CcMapData(DeviceExt
->StreamFileObject
,
85 DPRINT("CcMapData() failed\n");
86 return(STATUS_UNSUCCESSFUL
);
89 Record
= (PDIR_RECORD
)*Ptr
;
91 if (*CurrentOffset
>= DirLength
)
92 return(STATUS_NO_MORE_ENTRIES
);
98 if (*Ptr
- *Block
>= BLOCKSIZE
|| Record
->RecordLength
== 0)
100 DPRINT("Map next sector\n");
101 CcUnpinData(*Context
);
102 StreamOffset
->QuadPart
+= BLOCKSIZE
;
103 *CurrentOffset
= ROUND_UP(*CurrentOffset
, BLOCKSIZE
);
104 if (!CcMapData(DeviceExt
->StreamFileObject
,
109 DPRINT("CcMapData() failed\n");
110 return(STATUS_UNSUCCESSFUL
);
113 Record
= (PDIR_RECORD
)*Ptr
;
116 if (*CurrentOffset
>= DirLength
)
117 return STATUS_NO_MORE_ENTRIES
;
119 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
120 *pIndex
, Record
->RecordLength
, *CurrentOffset
);
122 if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 0)
126 else if (Record
->FileIdLength
== 1 && Record
->FileId
[0] == 1)
132 if (DeviceExt
->CdInfo
.JolietLevel
== 0)
136 for (i
= 0; i
< Record
->FileIdLength
&& Record
->FileId
[i
] != ';'; i
++)
137 Name
[i
] = (WCHAR
)Record
->FileId
[i
];
142 CdfsSwapString(Name
, Record
->FileId
, Record
->FileIdLength
);
146 DPRINT("Name '%S'\n", Name
);
150 return(STATUS_SUCCESS
);
155 * FUNCTION: Find a file
158 CdfsFindFile(PDEVICE_EXTENSION DeviceExt
,
161 PUNICODE_STRING FileToFind
,
166 WCHAR ShortNameBuffer
[13];
167 UNICODE_STRING TempString
;
168 UNICODE_STRING ShortName
;
169 UNICODE_STRING LongName
;
176 PVOID Context
= NULL
;
179 LARGE_INTEGER StreamOffset
;
181 GENERATE_NAME_CONTEXT NameContext
;
183 DPRINT("FindFile(Parent %x, FileToFind '%wZ', DirIndex: %d)\n",
184 Parent
, FileToFind
, pDirIndex
? *pDirIndex
: 0);
185 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
186 Fcb
->PathName
, Fcb
->ObjectName
);
191 if (FileToFind
== NULL
|| FileToFind
->Length
== 0)
194 RtlInitUnicodeString(&TempString
, L
".");
195 FileToFind
= &TempString
;
200 if (Parent
->Entry
.ExtentLocationL
== DeviceExt
->CdInfo
.RootStart
)
212 StreamOffset
.QuadPart
= (LONGLONG
)DeviceExt
->CdInfo
.RootStart
* (LONGLONG
)BLOCKSIZE
;
213 DirSize
= DeviceExt
->CdInfo
.RootSize
;
216 if (FileToFind
->Buffer
[0] == 0 ||
217 (FileToFind
->Buffer
[0] == '\\' && FileToFind
->Buffer
[1] == 0) ||
218 (FileToFind
->Buffer
[0] == '.' && FileToFind
->Buffer
[1] == 0))
220 /* it's root : complete essentials fields then return ok */
221 RtlZeroMemory(Fcb
, sizeof(FCB
));
223 Fcb
->PathName
[0] = '\\';
224 Fcb
->ObjectName
= &Fcb
->PathName
[1];
225 Fcb
->Entry
.ExtentLocationL
= DeviceExt
->CdInfo
.RootStart
;
226 Fcb
->Entry
.DataLengthL
= DeviceExt
->CdInfo
.RootSize
;
227 Fcb
->Entry
.FileFlags
= 0x02; //FILE_ATTRIBUTE_DIRECTORY;
233 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb
->PathName
, Fcb
->ObjectName
);
234 return STATUS_SUCCESS
;
239 StreamOffset
.QuadPart
= (LONGLONG
)Parent
->Entry
.ExtentLocationL
* (LONGLONG
)BLOCKSIZE
;
240 DirSize
= Parent
->Entry
.DataLengthL
;
243 DPRINT("StreamOffset %I64u DirSize %lu\n", StreamOffset
.QuadPart
, DirSize
);
245 if (pDirIndex
&& (*pDirIndex
))
246 DirIndex
= *pDirIndex
;
248 if (pOffset
&& (*pOffset
))
251 StreamOffset
.QuadPart
+= ROUND_DOWN(Offset
, BLOCKSIZE
);
254 if (!CcMapData(DeviceExt
->StreamFileObject
, &StreamOffset
,
255 BLOCKSIZE
, TRUE
, &Context
, &Block
))
257 DPRINT("CcMapData() failed\n");
258 return STATUS_UNSUCCESSFUL
;
261 Record
= (PDIR_RECORD
) (Block
+ Offset
% BLOCKSIZE
);
264 Offset
+= Record
->RecordLength
;
265 Record
= (PVOID
)Record
+ Record
->RecordLength
;
270 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
271 Record
->RecordLength
, Record
->ExtAttrRecordLength
, Record
->FileIdLength
);
273 Status
= CdfsGetEntryName(DeviceExt
, &Context
, &Block
, &StreamOffset
,
274 DirSize
, (PVOID
*)&Record
, name
, &DirIndex
, &Offset
);
276 if (Status
== STATUS_NO_MORE_ENTRIES
)
280 else if (Status
== STATUS_UNSUCCESSFUL
)
282 /* Note: the directory cache has already been unpinned */
286 DPRINT("Name '%S'\n", name
);
288 RtlInitUnicodeString(&LongName
, name
);
289 ShortName
.Length
= 0;
290 ShortName
.MaximumLength
= 26;
291 ShortName
.Buffer
= ShortNameBuffer
;
293 if ((RtlIsNameLegalDOS8Dot3(&LongName
, NULL
, &HasSpaces
) == FALSE
) ||
296 /* Build short name */
297 RtlGenerate8dot3Name(&LongName
,
304 /* copy short name */
305 RtlUpcaseUnicodeString(&ShortName
,
310 DPRINT("ShortName '%wZ'\n", &ShortName
);
312 if (FsRtlIsNameInExpression(FileToFind
, &LongName
, TRUE
, NULL
) ||
313 FsRtlIsNameInExpression(FileToFind
, &ShortName
, TRUE
, NULL
))
315 if (Parent
&& Parent
->PathName
)
317 len
= wcslen(Parent
->PathName
);
318 memcpy(Fcb
->PathName
, Parent
->PathName
, len
*sizeof(WCHAR
));
319 Fcb
->ObjectName
=&Fcb
->PathName
[len
];
320 if (len
!= 1 || Fcb
->PathName
[0] != '\\')
322 Fcb
->ObjectName
[0] = '\\';
323 Fcb
->ObjectName
= &Fcb
->ObjectName
[1];
328 Fcb
->ObjectName
=Fcb
->PathName
;
329 Fcb
->ObjectName
[0]='\\';
330 Fcb
->ObjectName
=&Fcb
->ObjectName
[1];
333 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb
->PathName
, Fcb
->ObjectName
);
335 memcpy(&Fcb
->Entry
, Record
, sizeof(DIR_RECORD
));
336 wcsncpy(Fcb
->ObjectName
, name
, MAX_PATH
);
338 /* Copy short name */
339 Fcb
->ShortNameU
.Length
= ShortName
.Length
;
340 Fcb
->ShortNameU
.MaximumLength
= ShortName
.Length
;
341 Fcb
->ShortNameU
.Buffer
= Fcb
->ShortNameBuffer
;
342 memcpy(Fcb
->ShortNameBuffer
, ShortName
.Buffer
, ShortName
.Length
);
345 *pDirIndex
= DirIndex
;
349 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
350 Fcb
->PathName
, Fcb
->ObjectName
, DirIndex
);
352 CcUnpinData(Context
);
354 return STATUS_SUCCESS
;
357 Offset
+= Record
->RecordLength
;
358 Record
= (PVOID
)Record
+ Record
->RecordLength
;
362 CcUnpinData(Context
);
365 *pDirIndex
= DirIndex
;
370 return STATUS_UNSUCCESSFUL
;
375 CdfsGetNameInformation(PFCB Fcb
,
376 PDEVICE_EXTENSION DeviceExt
,
377 PFILE_NAMES_INFORMATION Info
,
382 DPRINT("CdfsGetNameInformation() 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 return(STATUS_SUCCESS
);
398 CdfsGetDirectoryInformation(PFCB Fcb
,
399 PDEVICE_EXTENSION DeviceExt
,
400 PFILE_DIRECTORY_INFORMATION Info
,
405 DPRINT("CdfsGetDirectoryInformation() called\n");
407 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
408 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
409 return(STATUS_BUFFER_OVERFLOW
);
411 Info
->FileNameLength
= Length
;
412 Info
->NextEntryOffset
=
413 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
414 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
416 /* Convert file times */
417 CdfsDateTimeToFileTime(Fcb
,
418 &Info
->CreationTime
);
419 CdfsDateTimeToFileTime(Fcb
,
420 &Info
->LastAccessTime
);
421 CdfsDateTimeToFileTime(Fcb
,
422 &Info
->LastWriteTime
);
423 CdfsDateTimeToFileTime(Fcb
,
426 /* Convert file flags */
427 CdfsFileFlagsToAttributes(Fcb
,
428 &Info
->FileAttributes
);
429 if (CdfsFCBIsDirectory(Fcb
))
431 Info
->EndOfFile
.QuadPart
= 0LL;
432 Info
->AllocationSize
.QuadPart
= 0LL;
436 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
438 /* Make AllocSize a rounded up multiple of the sector size */
439 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
444 return(STATUS_SUCCESS
);
449 CdfsGetFullDirectoryInformation(PFCB Fcb
,
450 PDEVICE_EXTENSION DeviceExt
,
451 PFILE_FULL_DIRECTORY_INFORMATION Info
,
456 DPRINT("CdfsGetFullDirectoryInformation() called\n");
458 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
459 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
460 return(STATUS_BUFFER_OVERFLOW
);
462 Info
->FileNameLength
= Length
;
463 Info
->NextEntryOffset
=
464 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
465 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
467 /* Convert file times */
468 CdfsDateTimeToFileTime(Fcb
,
469 &Info
->CreationTime
);
470 CdfsDateTimeToFileTime(Fcb
,
471 &Info
->LastAccessTime
);
472 CdfsDateTimeToFileTime(Fcb
,
473 &Info
->LastWriteTime
);
474 CdfsDateTimeToFileTime(Fcb
,
477 /* Convert file flags */
478 CdfsFileFlagsToAttributes(Fcb
,
479 &Info
->FileAttributes
);
481 if (CdfsFCBIsDirectory(Fcb
))
483 Info
->EndOfFile
.QuadPart
= 0LL;
484 Info
->AllocationSize
.QuadPart
= 0LL;
488 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
490 /* Make AllocSize a rounded up multiple of the sector size */
491 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
497 return(STATUS_SUCCESS
);
502 CdfsGetBothDirectoryInformation(PFCB Fcb
,
503 PDEVICE_EXTENSION DeviceExt
,
504 PFILE_BOTH_DIRECTORY_INFORMATION Info
,
509 DPRINT("CdfsGetBothDirectoryInformation() called\n");
511 Length
= wcslen(Fcb
->ObjectName
) * sizeof(WCHAR
);
512 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION
) + Length
) > BufferLength
)
513 return(STATUS_BUFFER_OVERFLOW
);
515 Info
->FileNameLength
= Length
;
516 Info
->NextEntryOffset
=
517 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION
) + Length
, 4);
518 memcpy(Info
->FileName
, Fcb
->ObjectName
, Length
);
520 /* Convert file times */
521 CdfsDateTimeToFileTime(Fcb
,
522 &Info
->CreationTime
);
523 CdfsDateTimeToFileTime(Fcb
,
524 &Info
->LastAccessTime
);
525 CdfsDateTimeToFileTime(Fcb
,
526 &Info
->LastWriteTime
);
527 CdfsDateTimeToFileTime(Fcb
,
530 /* Convert file flags */
531 CdfsFileFlagsToAttributes(Fcb
,
532 &Info
->FileAttributes
);
534 if (CdfsFCBIsDirectory(Fcb
))
536 Info
->EndOfFile
.QuadPart
= 0LL;
537 Info
->AllocationSize
.QuadPart
= 0LL;
541 Info
->EndOfFile
.QuadPart
= Fcb
->Entry
.DataLengthL
;
543 /* Make AllocSize a rounded up multiple of the sector size */
544 Info
->AllocationSize
.QuadPart
= ROUND_UP(Fcb
->Entry
.DataLengthL
, BLOCKSIZE
);
550 /* Copy short name */
551 Info
->ShortNameLength
= Fcb
->ShortNameU
.Length
;
552 memcpy(Info
->ShortName
, Fcb
->ShortNameU
.Buffer
, Fcb
->ShortNameU
.Length
);
554 return(STATUS_SUCCESS
);
559 CdfsQueryDirectory(PDEVICE_OBJECT DeviceObject
,
562 PDEVICE_EXTENSION DeviceExtension
;
563 LONG BufferLength
= 0;
564 PUNICODE_STRING SearchPattern
= NULL
;
565 FILE_INFORMATION_CLASS FileInformationClass
;
567 PUCHAR Buffer
= NULL
;
568 PFILE_NAMES_INFORMATION Buffer0
= NULL
;
572 BOOLEAN First
= FALSE
;
573 PEXTENDED_IO_STACK_LOCATION Stack
;
574 PFILE_OBJECT FileObject
;
575 NTSTATUS Status
= STATUS_SUCCESS
;
577 DPRINT("CdfsQueryDirectory() called\n");
579 DeviceExtension
= DeviceObject
->DeviceExtension
;
580 Stack
= (PEXTENDED_IO_STACK_LOCATION
) IoGetCurrentIrpStackLocation(Irp
);
581 FileObject
= Stack
->FileObject
;
583 Ccb
= (PCCB
)FileObject
->FsContext2
;
584 Fcb
= (PFCB
)FileObject
->FsContext
;
586 /* Obtain the callers parameters */
587 BufferLength
= Stack
->Parameters
.QueryDirectory
.Length
;
588 SearchPattern
= Stack
->Parameters
.QueryDirectory
.FileName
;
589 FileInformationClass
=
590 Stack
->Parameters
.QueryDirectory
.FileInformationClass
;
591 FileIndex
= Stack
->Parameters
.QueryDirectory
.FileIndex
;
594 if (SearchPattern
!= NULL
)
596 if (Ccb
->DirectorySearchPattern
.Buffer
== NULL
)
599 Ccb
->DirectorySearchPattern
.Buffer
=
600 ExAllocatePool(NonPagedPool
, SearchPattern
->Length
+ sizeof(WCHAR
));
601 if (Ccb
->DirectorySearchPattern
.Buffer
== NULL
)
603 return STATUS_INSUFFICIENT_RESOURCES
;
606 Ccb
->DirectorySearchPattern
.Length
= SearchPattern
->Length
;
607 Ccb
->DirectorySearchPattern
.MaximumLength
= SearchPattern
->Length
+ sizeof(WCHAR
);
609 memcpy(Ccb
->DirectorySearchPattern
.Buffer
,
610 SearchPattern
->Buffer
,
611 SearchPattern
->Length
);
612 Ccb
->DirectorySearchPattern
.Buffer
[SearchPattern
->Length
/ sizeof(WCHAR
)] = 0;
615 else if (Ccb
->DirectorySearchPattern
.Buffer
== NULL
)
618 Ccb
->DirectorySearchPattern
.Buffer
= ExAllocatePool(NonPagedPool
, 2 * sizeof(WCHAR
));
619 if (Ccb
->DirectorySearchPattern
.Buffer
== NULL
)
621 return STATUS_INSUFFICIENT_RESOURCES
;
624 Ccb
->DirectorySearchPattern
.Length
= sizeof(WCHAR
);
625 Ccb
->DirectorySearchPattern
.MaximumLength
= 2 * sizeof(WCHAR
);
626 Ccb
->DirectorySearchPattern
.Buffer
[0] = L
'*';
627 Ccb
->DirectorySearchPattern
.Buffer
[1] = 0;
629 DPRINT("Search pattern '%wZ'\n", &Ccb
->DirectorySearchPattern
);
631 /* Determine directory index */
632 if (Stack
->Flags
& SL_INDEX_SPECIFIED
)
634 Ccb
->Entry
= Ccb
->CurrentByteOffset
.u
.LowPart
;
637 else if (First
|| (Stack
->Flags
& SL_RESTART_SCAN
))
643 /* Determine Buffer for result */
646 Buffer
= MmGetSystemAddressForMdl(Irp
->MdlAddress
);
650 Buffer
= Irp
->UserBuffer
;
652 DPRINT("Buffer = %p tofind = %wZ\n", Buffer
, &Ccb
->DirectorySearchPattern
);
654 TempFcb
.ObjectName
= TempFcb
.PathName
;
655 while (Status
== STATUS_SUCCESS
&& BufferLength
> 0)
657 Status
= CdfsFindFile(DeviceExtension
,
660 &Ccb
->DirectorySearchPattern
,
663 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb
.ObjectName
, Status
, Ccb
->Entry
);
665 if (NT_SUCCESS(Status
))
667 switch (FileInformationClass
)
669 case FileNameInformation
:
670 Status
= CdfsGetNameInformation(&TempFcb
,
672 (PFILE_NAMES_INFORMATION
)Buffer
,
676 case FileDirectoryInformation
:
677 Status
= CdfsGetDirectoryInformation(&TempFcb
,
679 (PFILE_DIRECTORY_INFORMATION
)Buffer
,
683 case FileFullDirectoryInformation
:
684 Status
= CdfsGetFullDirectoryInformation(&TempFcb
,
686 (PFILE_FULL_DIRECTORY_INFORMATION
)Buffer
,
690 case FileBothDirectoryInformation
:
691 Status
= CdfsGetBothDirectoryInformation(&TempFcb
,
693 (PFILE_BOTH_DIRECTORY_INFORMATION
)Buffer
,
698 Status
= STATUS_INVALID_INFO_CLASS
;
701 if (Status
== STATUS_BUFFER_OVERFLOW
)
705 Buffer0
->NextEntryOffset
= 0;
714 Buffer0
->NextEntryOffset
= 0;
719 Status
= STATUS_NO_SUCH_FILE
;
723 Status
= STATUS_NO_MORE_FILES
;
728 Buffer0
= (PFILE_NAMES_INFORMATION
)Buffer
;
729 Buffer0
->FileIndex
= FileIndex
++;
732 if (Stack
->Flags
& SL_RETURN_SINGLE_ENTRY
)
736 BufferLength
-= Buffer0
->NextEntryOffset
;
737 Buffer
+= Buffer0
->NextEntryOffset
;
742 Buffer0
->NextEntryOffset
= 0;
747 Status
= STATUS_SUCCESS
;
756 CdfsDirectoryControl(PDEVICE_OBJECT DeviceObject
,
759 PIO_STACK_LOCATION Stack
;
762 DPRINT("CdfsDirectoryControl() called\n");
764 Stack
= IoGetCurrentIrpStackLocation(Irp
);
766 switch (Stack
->MinorFunction
)
768 case IRP_MN_QUERY_DIRECTORY
:
769 Status
= CdfsQueryDirectory(DeviceObject
,
773 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
774 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
775 Status
= STATUS_NOT_IMPLEMENTED
;
779 DPRINT1("CDFS: MinorFunction %d\n", Stack
->MinorFunction
);
780 Status
= STATUS_INVALID_DEVICE_REQUEST
;
784 Irp
->IoStatus
.Status
= Status
;
785 Irp
->IoStatus
.Information
= 0;
787 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);