3 * Copyright (C) 2002 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.
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 CdfsGetEntryName(PDEVICE_EXTENSION DeviceExt,
49 PLARGE_INTEGER StreamOffset,
56 * FUNCTION: Retrieves the file name, be it in short or long file name format
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;
115 if (*CurrentOffset >= DirLength)
116 return(STATUS_NO_MORE_ENTRIES);
118 DPRINT("Index %lu RecordLength %lu Offset %lu\n",
119 *pIndex, Record->RecordLength, *CurrentOffset);
121 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
125 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
131 if (DeviceExt->CdInfo.JolietLevel == 0)
135 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
136 Name[i] = (WCHAR)Record->FileId[i];
141 CdfsSwapString(Name, Record->FileId, Record->FileIdLength);
145 DPRINT("Name '%S'\n", Name);
149 return(STATUS_SUCCESS);
153 CdfsFindFile(PDEVICE_EXTENSION DeviceExt,
160 * FUNCTION: Find a file
165 WCHAR ShortNameBuffer[13];
166 UNICODE_STRING ShortName;
167 UNICODE_STRING LongName;
175 PVOID Context = NULL;
178 LARGE_INTEGER StreamOffset;
180 GENERATE_NAME_CONTEXT NameContext;
182 DPRINT("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n",
183 Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
184 DPRINT("FindFile: old Pathname %x, old Objectname %x)\n",
185 Fcb->PathName, Fcb->ObjectName);
189 if (wcslen (FileToFind) == 0)
192 TempStr[0] = (WCHAR) '.';
194 FileToFind = (PWSTR)&TempStr;
199 if (Parent->Entry.ExtentLocationL == DeviceExt->CdInfo.RootStart)
211 StreamOffset.QuadPart = (LONGLONG)DeviceExt->CdInfo.RootStart * (LONGLONG)BLOCKSIZE;
212 DirSize = DeviceExt->CdInfo.RootSize;
215 if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
216 || (FileToFind[0] == '.' && FileToFind[1] == 0))
218 /* it's root : complete essentials fields then return ok */
219 RtlZeroMemory(Fcb, sizeof(FCB));
221 Fcb->PathName[0]='\\';
222 Fcb->ObjectName = &Fcb->PathName[1];
223 Fcb->Entry.ExtentLocationL = DeviceExt->CdInfo.RootStart;
224 Fcb->Entry.DataLengthL = DeviceExt->CdInfo.RootSize;
225 Fcb->Entry.FileFlags = 0x02; //FILE_ATTRIBUTE_DIRECTORY;
231 DPRINT("CdfsFindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
232 return (STATUS_SUCCESS);
237 StreamOffset.QuadPart = (LONGLONG)Parent->Entry.ExtentLocationL * (LONGLONG)BLOCKSIZE;
238 DirSize = Parent->Entry.DataLengthL;
241 DPRINT("StreamOffset %I64u DirSize %lu\n", StreamOffset.QuadPart, DirSize);
243 if (pDirIndex && (*pDirIndex))
244 DirIndex = *pDirIndex;
246 if (pOffset && (*pOffset))
249 StreamOffset.QuadPart += ROUND_DOWN(Offset, BLOCKSIZE);
252 if(!CcMapData(DeviceExt->StreamFileObject, &StreamOffset,
253 BLOCKSIZE, TRUE, &Context, &Block))
255 DPRINT("CcMapData() failed\n");
256 return(STATUS_UNSUCCESSFUL);
259 Record = (PDIR_RECORD) (Block + Offset % BLOCKSIZE);
262 Offset += Record->RecordLength;
263 Record = (PVOID)Record + Record->RecordLength;
267 DPRINT("RecordLength %u ExtAttrRecordLength %u NameLength %u\n",
268 Record->RecordLength, Record->ExtAttrRecordLength, Record->FileIdLength);
270 Status = CdfsGetEntryName(DeviceExt, &Context, &Block, &StreamOffset,
271 DirSize, (PVOID*)&Record, name, &DirIndex, &Offset);
273 if (Status == STATUS_NO_MORE_ENTRIES)
277 else if (Status == STATUS_UNSUCCESSFUL)
279 /* Note: the directory cache has already been unpinned */
283 DPRINT("Name '%S'\n", name);
285 RtlInitUnicodeString(&LongName, name);
286 ShortName.Length = 0;
287 ShortName.MaximumLength = 26;
288 ShortName.Buffer = ShortNameBuffer;
290 if ((RtlIsNameLegalDOS8Dot3(&LongName, NULL, &HasSpaces) == FALSE) ||
293 /* Build short name */
294 RtlGenerate8dot3Name(&LongName,
301 /* copy short name */
302 RtlUpcaseUnicodeString(&ShortName,
307 DPRINT("ShortName '%wZ'\n", &ShortName);
309 if (wstrcmpjoki(name, FileToFind) ||
310 wstrcmpjoki(ShortNameBuffer, FileToFind))
312 if (Parent && Parent->PathName)
314 len = wcslen(Parent->PathName);
315 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
316 Fcb->ObjectName=&Fcb->PathName[len];
317 if (len != 1 || Fcb->PathName[0] != '\\')
319 Fcb->ObjectName[0] = '\\';
320 Fcb->ObjectName = &Fcb->ObjectName[1];
325 Fcb->ObjectName=Fcb->PathName;
326 Fcb->ObjectName[0]='\\';
327 Fcb->ObjectName=&Fcb->ObjectName[1];
330 DPRINT("PathName '%S' ObjectName '%S'\n", Fcb->PathName, Fcb->ObjectName);
332 memcpy(&Fcb->Entry, Record, sizeof(DIR_RECORD));
333 wcsncpy(Fcb->ObjectName, name, MAX_PATH);
335 /* Copy short name */
336 Fcb->ShortNameLength = ShortName.Length;
337 memcpy(Fcb->ShortName, ShortName.Buffer, ShortName.Length);
340 *pDirIndex = DirIndex;
344 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",
345 Fcb->PathName, Fcb->ObjectName, DirIndex);
347 CcUnpinData(Context);
349 return(STATUS_SUCCESS);
353 Offset += Record->RecordLength;
354 Record = (PVOID)Record + Record->RecordLength;
359 CcUnpinData(Context);
362 *pDirIndex = DirIndex;
367 return(STATUS_UNSUCCESSFUL);
372 CdfsGetNameInformation(PFCB Fcb,
373 PDEVICE_EXTENSION DeviceExt,
374 PFILE_NAMES_INFORMATION Info,
379 DPRINT("CdfsGetNameInformation() 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 return(STATUS_SUCCESS);
395 CdfsGetDirectoryInformation(PFCB Fcb,
396 PDEVICE_EXTENSION DeviceExt,
397 PFILE_DIRECTORY_INFORMATION Info,
402 DPRINT("CdfsGetDirectoryInformation() called\n");
404 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
405 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
406 return(STATUS_BUFFER_OVERFLOW);
408 Info->FileNameLength = Length;
409 Info->NextEntryOffset =
410 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
411 memcpy(Info->FileName, Fcb->ObjectName, Length);
413 /* Convert file times */
414 CdfsDateTimeToFileTime(Fcb,
415 &Info->CreationTime);
416 CdfsDateTimeToFileTime(Fcb,
417 &Info->LastAccessTime);
418 CdfsDateTimeToFileTime(Fcb,
419 &Info->LastWriteTime);
420 CdfsDateTimeToFileTime(Fcb,
423 /* Convert file flags */
424 CdfsFileFlagsToAttributes(Fcb,
425 &Info->FileAttributes);
427 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
429 /* Make AllocSize a rounded up multiple of the sector size */
430 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
434 return(STATUS_SUCCESS);
439 CdfsGetFullDirectoryInformation(PFCB Fcb,
440 PDEVICE_EXTENSION DeviceExt,
441 PFILE_FULL_DIRECTORY_INFORMATION Info,
446 DPRINT("CdfsGetFullDirectoryInformation() called\n");
448 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
449 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
450 return(STATUS_BUFFER_OVERFLOW);
452 Info->FileNameLength = Length;
453 Info->NextEntryOffset =
454 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
455 memcpy(Info->FileName, Fcb->ObjectName, Length);
457 /* Convert file times */
458 CdfsDateTimeToFileTime(Fcb,
459 &Info->CreationTime);
460 CdfsDateTimeToFileTime(Fcb,
461 &Info->LastAccessTime);
462 CdfsDateTimeToFileTime(Fcb,
463 &Info->LastWriteTime);
464 CdfsDateTimeToFileTime(Fcb,
467 /* Convert file flags */
468 CdfsFileFlagsToAttributes(Fcb,
469 &Info->FileAttributes);
471 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
473 /* Make AllocSize a rounded up multiple of the sector size */
474 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
479 return(STATUS_SUCCESS);
484 CdfsGetBothDirectoryInformation(PFCB Fcb,
485 PDEVICE_EXTENSION DeviceExt,
486 PFILE_BOTH_DIRECTORY_INFORMATION Info,
491 DPRINT("CdfsGetBothDirectoryInformation() called\n");
493 Length = wcslen(Fcb->ObjectName) * sizeof(WCHAR);
494 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length) > BufferLength)
495 return(STATUS_BUFFER_OVERFLOW);
497 Info->FileNameLength = Length;
498 Info->NextEntryOffset =
499 ROUND_UP(sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + Length, 4);
500 memcpy(Info->FileName, Fcb->ObjectName, Length);
502 /* Convert file times */
503 CdfsDateTimeToFileTime(Fcb,
504 &Info->CreationTime);
505 CdfsDateTimeToFileTime(Fcb,
506 &Info->LastAccessTime);
507 CdfsDateTimeToFileTime(Fcb,
508 &Info->LastWriteTime);
509 CdfsDateTimeToFileTime(Fcb,
512 /* Convert file flags */
513 CdfsFileFlagsToAttributes(Fcb,
514 &Info->FileAttributes);
516 Info->EndOfFile.QuadPart = Fcb->Entry.DataLengthL;
518 /* Make AllocSize a rounded up multiple of the sector size */
519 Info->AllocationSize.QuadPart = ROUND_UP(Fcb->Entry.DataLengthL, BLOCKSIZE);
524 /* Copy short name */
525 Info->ShortNameLength = Fcb->ShortNameLength;
526 memcpy(Info->ShortName, Fcb->ShortName, Fcb->ShortNameLength);
528 return(STATUS_SUCCESS);
533 CdfsQueryDirectory(PDEVICE_OBJECT DeviceObject,
536 PDEVICE_EXTENSION DeviceExtension;
537 LONG BufferLength = 0;
538 PUNICODE_STRING SearchPattern = NULL;
539 FILE_INFORMATION_CLASS FileInformationClass;
541 PUCHAR Buffer = NULL;
542 PFILE_NAMES_INFORMATION Buffer0 = NULL;
546 BOOLEAN First = FALSE;
547 PIO_STACK_LOCATION Stack;
548 PFILE_OBJECT FileObject;
549 NTSTATUS Status = STATUS_SUCCESS;
551 DPRINT("CdfsQueryDirectory() called\n");
553 DeviceExtension = DeviceObject->DeviceExtension;
554 Stack = IoGetCurrentIrpStackLocation(Irp);
555 FileObject = Stack->FileObject;
557 Ccb = (PCCB)FileObject->FsContext2;
558 Fcb = (PFCB)FileObject->FsContext;
560 /* Obtain the callers parameters */
561 BufferLength = Stack->Parameters.QueryDirectory.Length;
562 SearchPattern = Stack->Parameters.QueryDirectory.FileName;
563 FileInformationClass =
564 Stack->Parameters.QueryDirectory.FileInformationClass;
565 FileIndex = Stack->Parameters.QueryDirectory.FileIndex;
568 if (SearchPattern != NULL)
570 if (!Ccb->DirectorySearchPattern)
573 Ccb->DirectorySearchPattern =
574 ExAllocatePool(NonPagedPool, SearchPattern->Length + sizeof(WCHAR));
575 if (!Ccb->DirectorySearchPattern)
577 return(STATUS_INSUFFICIENT_RESOURCES);
580 memcpy(Ccb->DirectorySearchPattern,
581 SearchPattern->Buffer,
582 SearchPattern->Length);
583 Ccb->DirectorySearchPattern[SearchPattern->Length / sizeof(WCHAR)] = 0;
586 else if (!Ccb->DirectorySearchPattern)
589 Ccb->DirectorySearchPattern = ExAllocatePool(NonPagedPool, 2 * sizeof(WCHAR));
590 if (!Ccb->DirectorySearchPattern)
592 return(STATUS_INSUFFICIENT_RESOURCES);
594 Ccb->DirectorySearchPattern[0] = L'*';
595 Ccb->DirectorySearchPattern[1] = 0;
597 DPRINT("Search pattern '%S'\n", Ccb->DirectorySearchPattern);
599 /* Determine directory index */
600 if (Stack->Flags & SL_INDEX_SPECIFIED)
602 Ccb->Entry = Ccb->CurrentByteOffset.u.LowPart;
605 else if (First || (Stack->Flags & SL_RESTART_SCAN))
611 /* Determine Buffer for result */
614 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
618 Buffer = Irp->UserBuffer;
620 DPRINT("Buffer=%x tofind=%S\n", Buffer, Ccb->DirectorySearchPattern);
622 TempFcb.ObjectName = TempFcb.PathName;
623 while (Status == STATUS_SUCCESS && BufferLength > 0)
625 Status = CdfsFindFile(DeviceExtension,
628 Ccb->DirectorySearchPattern,
631 DPRINT("Found %S, Status=%x, entry %x\n", TempFcb.ObjectName, Status, Ccb->Entry);
633 if (NT_SUCCESS(Status))
635 switch (FileInformationClass)
637 case FileNameInformation:
638 Status = CdfsGetNameInformation(&TempFcb,
640 (PFILE_NAMES_INFORMATION)Buffer,
644 case FileDirectoryInformation:
645 Status = CdfsGetDirectoryInformation(&TempFcb,
647 (PFILE_DIRECTORY_INFORMATION)Buffer,
651 case FileFullDirectoryInformation:
652 Status = CdfsGetFullDirectoryInformation(&TempFcb,
654 (PFILE_FULL_DIRECTORY_INFORMATION)Buffer,
658 case FileBothDirectoryInformation:
659 Status = CdfsGetBothDirectoryInformation(&TempFcb,
661 (PFILE_BOTH_DIRECTORY_INFORMATION)Buffer,
666 Status = STATUS_INVALID_INFO_CLASS;
669 if (Status == STATUS_BUFFER_OVERFLOW)
673 Buffer0->NextEntryOffset = 0;
682 Buffer0->NextEntryOffset = 0;
687 Status = STATUS_NO_SUCH_FILE;
691 Status = STATUS_NO_MORE_FILES;
696 Buffer0 = (PFILE_NAMES_INFORMATION)Buffer;
697 Buffer0->FileIndex = FileIndex++;
700 if (Stack->Flags & SL_RETURN_SINGLE_ENTRY)
704 BufferLength -= Buffer0->NextEntryOffset;
705 Buffer += Buffer0->NextEntryOffset;
710 Buffer0->NextEntryOffset = 0;
715 Status = STATUS_SUCCESS;
724 CdfsDirectoryControl(PDEVICE_OBJECT DeviceObject,
727 PIO_STACK_LOCATION Stack;
730 DPRINT("CdfsDirectoryControl() called\n");
732 Stack = IoGetCurrentIrpStackLocation(Irp);
734 switch (Stack->MinorFunction)
736 case IRP_MN_QUERY_DIRECTORY:
737 Status = CdfsQueryDirectory(DeviceObject,
741 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
742 DPRINT1("IRP_MN_NOTIFY_CHANGE_DIRECTORY\n");
743 Status = STATUS_NOT_IMPLEMENTED;
747 DPRINT1("CDFS: MinorFunction %d\n", Stack->MinorFunction);
748 Status = STATUS_INVALID_DEVICE_REQUEST;
752 Irp->IoStatus.Status = Status;
753 Irp->IoStatus.Information = 0;
755 IoCompleteRequest(Irp, IO_NO_INCREMENT);