All steps on how to find and open files and directories in an UDF filesystem have been followed by OSTA UDF and ECMA-167 specifications.
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Paulo Alcantara <pca...@zytor.com> --- MdeModulePkg/Universal/Disk/UdfDxe/FileName.c | 190 ++++++++ .../Universal/Disk/UdfDxe/FileSystemOperations.c | 520 ++++++++++++++++++++- MdeModulePkg/Universal/Disk/UdfDxe/Udf.h | 25 + MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf | 1 + 4 files changed, 734 insertions(+), 2 deletions(-) create mode 100644 MdeModulePkg/Universal/Disk/UdfDxe/FileName.c diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c b/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c new file mode 100644 index 0000000..04cb5b5 --- /dev/null +++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileName.c @@ -0,0 +1,190 @@ +/** @file + UDF filesystem driver. + +Copyright (c) 2014 Paulo Alcantara <pca...@zytor.com><BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Udf.h" + +STATIC +CHAR16 * +TrimString ( + IN CHAR16 *String + ) +{ + CHAR16 *TempString; + + for ( ; (*String) && (*String == L' '); String++) { + ; + } + + TempString = String + StrLen (String) - 1; + while ((TempString >= String) && (*TempString == L' ')) { + TempString--; + } + + *(TempString + 1) = L'\0'; + + return String; +} + +STATIC +VOID +ReplaceLeft ( + IN CHAR16 *Destination, + IN CONST CHAR16 *Source + ) +{ + CONST CHAR16 *EndString; + + EndString = Source + StrLen (Source); + while (Source <= EndString) { + *Destination++ = *Source++; + } +} + +STATIC +CHAR16 * +ExcludeTrailingBackslashes ( + IN CHAR16 *String + ) +{ + CHAR16 *TempString; + + switch (*(String + 1)) { + case L'\\': + break; + case L'\0': + default: + String++; + goto Exit; + } + + TempString = String; + while ((*TempString) && (*TempString == L'\\')) { + TempString++; + } + + if (TempString - 1 > String) { + ReplaceLeft (String + 1, TempString); + } + + String++; + +Exit: + return String; +} + +CHAR16 * +MangleFileName ( + IN CHAR16 *FileName + ) +{ + CHAR16 *FileNameSavedPointer; + CHAR16 *TempFileName; + UINTN BackslashesNo; + + if ((!FileName) || ((FileName) && (!*FileName))) { + FileName = NULL; + goto Exit; + } + + FileName = TrimString (FileName); + if (!*FileName) { + goto Exit; + } + + if ((StrLen (FileName) > 1) && (FileName[StrLen (FileName) - 1] == L'\\')) { + FileName[StrLen (FileName) - 1] = L'\0'; + } + + FileNameSavedPointer = FileName; + + if (FileName[0] == L'.') { + if (FileName[1] == L'.') { + if (!FileName[2]) { + goto Exit; + } else { + FileName += 2; + } + } else if (!FileName[1]) { + goto Exit; + } + } + + while (*FileName) { + if (*FileName == L'\\') { + FileName = ExcludeTrailingBackslashes (FileName); + } else if (*FileName == L'.') { + switch (*(FileName + 1)) { + case L'\0': + *FileName = L'\0'; + break; + case L'\\': + TempFileName = FileName + 1; + TempFileName = ExcludeTrailingBackslashes (TempFileName); + ReplaceLeft (FileName, TempFileName); + break; + case '.': + if ((*(FileName - 1) != L'\\') && ((*(FileName + 2) != L'\\') || + (*(FileName + 2) != L'\0'))) { + FileName++; + continue; + } + + BackslashesNo = 0; + TempFileName = FileName - 1; + while (TempFileName >= FileNameSavedPointer) { + if (*TempFileName == L'\\') { + if (++BackslashesNo == 2) { + break; + } + } + + TempFileName--; + } + + TempFileName++; + + if ((*TempFileName == L'.') && (*(TempFileName + 1) == L'.')) { + FileName += 2; + } else { + if (*(FileName + 2)) { + ReplaceLeft (TempFileName, FileName + 3); + if (*(TempFileName - 1) == L'\\') { + FileName = TempFileName; + ExcludeTrailingBackslashes (TempFileName - 1); + TempFileName = FileName; + } + } else { + *TempFileName = L'\0'; + } + + FileName = TempFileName; + } + + break; + default: + FileName++; + } + } else { + FileName++; + } + } + + FileName = FileNameSavedPointer; + if ((StrLen (FileName) > 1) && (FileName [StrLen (FileName) - 1] == L'\\')) { + FileName [StrLen (FileName) - 1] = L'\0'; + } + +Exit: + return FileName; +} diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c index 5492494..8a13794 100644 --- a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c +++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c @@ -150,7 +150,347 @@ UdfOpen ( IN UINT64 Attributes ) { - return EFI_DEVICE_ERROR; + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + UINT32 PartitionStartingLocation; + UINT32 PartitionLength; + UDF_FILE_ENTRY FileEntry; + UDF_FILE_IDENTIFIER_DESCRIPTOR CurFileIdentifierDesc; + UDF_FILE_IDENTIFIER_DESCRIPTOR ParentFileIdentifierDesc; + UDF_FILE_IDENTIFIER_DESCRIPTOR FileIdentifierDesc; + UINT64 NextOffset; + BOOLEAN ReadDone; + CHAR16 *String; + CHAR16 *TempString; + UINT64 Offset; + CHAR16 *FileNameSavedPointer; + CHAR16 *NextFileName; + CHAR16 *TempFileName; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + BOOLEAN Found; + PRIVATE_UDF_FILE_DATA *NewPrivFileData; + UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd; + UINT64 Lsn; + BOOLEAN IsRootDirectory; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + String = NULL; + + if ((!This) || (!NewHandle) || (!FileName)) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + PartitionStartingLocation = PrivFileData->Partition.StartingLocation; + PartitionLength = PrivFileData->Partition.Length; + + FileName = MangleFileName (FileName); + if ((!FileName) || ((FileName) && (!*FileName))) { + Status = EFI_NOT_FOUND; + goto Exit; + } + + String = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR16)); + if (!String) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + BlockIo = PrivFileData->BlockIo; + DiskIo = PrivFileData->DiskIo; + Found = FALSE; + NextFileName = NULL; + NewPrivFileData = NULL; + FileNameSavedPointer = FileName; + + if (PrivFileData->IsRootDirectory) { + CopyMem ( + (VOID *)&CurFileIdentifierDesc, + (VOID *)&PrivFileData->Root.FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + CopyMem ( + (VOID *)&ParentFileIdentifierDesc, + (VOID *)&PrivFileData->Root.FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + } else { + CopyMem ( + (VOID *)&CurFileIdentifierDesc, + (VOID *)&PrivFileData->File.FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + CopyMem ( + (VOID *)&ParentFileIdentifierDesc, + (VOID *)&PrivFileData->File.ParentFileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + } + + CopyMem ( + (VOID *)&FileIdentifierDesc, + (VOID *)&CurFileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + + for (;;) { +NextLookup: + if (NextFileName) { + FreePool ((VOID *)NextFileName); + NextFileName = NULL; + } + + // + // Parse FileName + // + if (!*FileName) { + break; + } + + Offset = 0; + + TempString = String; + while ((*FileName) && (*FileName != L'\\')) { + *TempString++ = *FileName++; + } + + *TempString = L'\0'; + + if ((*FileName) && (*FileName == L'\\')) { + FileName++; + } + + Found = FALSE; + IsRootDirectory = FALSE; + + if (!*String) { + CopyMem ( + (VOID *)&FileIdentifierDesc, + (VOID *)&PrivFileData->Root.FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + if (!*FileName) { + Found = TRUE; + IsRootDirectory = TRUE; + break; + } else { + continue; + } + } + + if (StrCmp (String, L"..") == 0) { + // + // Make sure we're not going to look up Parent FID from a root + // directory or even if the current FID is not a directory(!) + // + if ((IS_FID_PARENT_FILE (&FileIdentifierDesc)) || + (!IS_FID_DIRECTORY_FILE (&FileIdentifierDesc))) { + break; + } + + CopyMem ( + (VOID *)&FileIdentifierDesc, + (VOID *)&ParentFileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + + Found = TRUE; + continue; + } else if (StrCmp (String, L".") == 0) { + Found = TRUE; + continue; + } + + // + // Start lookup + // + CopyMem ( + (VOID *)&CurFileIdentifierDesc, + (VOID *)&FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + + NextOffset = 0; + + for (;;) { + Status = ReadDirectory ( + BlockIo, + DiskIo, + PartitionStartingLocation, + PartitionLength, + &CurFileIdentifierDesc, + &FileIdentifierDesc, + &NextOffset, + &ReadDone + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (!ReadDone) { + Status = EFI_NOT_FOUND; + goto Exit; + } + + Status = FileIdentifierDescToFileName ( + &FileIdentifierDesc, + &NextFileName + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Check whether FID's File Identifier contains the expected filename + // + if (StrCmp (NextFileName, String) == 0) { + CopyMem ( + (VOID *)&ParentFileIdentifierDesc, + (VOID *)&CurFileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + + Found = TRUE; + goto NextLookup; + } + + if (NextFileName) { + FreePool ((VOID *)NextFileName); + } + } + } + + if (Found) { + NewPrivFileData = AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA)); + if (!NewPrivFileData) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + CopyMem ( + (VOID *)NewPrivFileData, + (VOID *)PrivFileData, + sizeof (PRIVATE_UDF_FILE_DATA) + ); + + if (IsRootDirectory) { + NewPrivFileData->AbsoluteFileName = NULL; + NewPrivFileData->FileName = NULL; + goto HandleRootDirectory; + } + + FileName = FileNameSavedPointer; + + NewPrivFileData->AbsoluteFileName = AllocatePool ( + ((PrivFileData->AbsoluteFileName ? + StrLen (PrivFileData->AbsoluteFileName) : + 0) + + StrLen (FileName)) * + sizeof (CHAR16) + + sizeof (CHAR16) + + sizeof (CHAR16) + ); + if (!NewPrivFileData->AbsoluteFileName) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + NewPrivFileData->AbsoluteFileName[0] = L'\0'; + if (PrivFileData->AbsoluteFileName) { + StrCat ( + NewPrivFileData->AbsoluteFileName, + PrivFileData->AbsoluteFileName + ); + StrCat (NewPrivFileData->AbsoluteFileName, L"\\"); + } + + StrCat (NewPrivFileData->AbsoluteFileName, FileName); + + NewPrivFileData->AbsoluteFileName = MangleFileName ( + NewPrivFileData->AbsoluteFileName + ); + + FileName = NewPrivFileData->AbsoluteFileName; + while ((TempFileName = StrStr (FileName, L"\\"))) { + FileName = TempFileName + 1; + } + + NewPrivFileData->FileName = AllocatePool ( + StrLen (FileName) * sizeof (CHAR16) + + sizeof (CHAR16) + ); + + NewPrivFileData->FileName[0] = L'\0'; + StrCat (NewPrivFileData->FileName, FileName); + + // + // Find FE of the FID + // + LongAd = &FileIdentifierDesc.Icb; + + Lsn = (UINT64)(PartitionStartingLocation + + LongAd->ExtentLocation.LogicalBlockNumber); + + Offset = Lsn * LOGICAL_BLOCK_SIZE; + + // + // Read FE + // + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + sizeof (UDF_FILE_ENTRY), + (VOID *)&FileEntry + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (!IS_FE (&FileEntry)) { + Status = EFI_VOLUME_CORRUPTED; + goto Exit; + } + + CopyMem ( + (VOID *)&NewPrivFileData->File.FileEntry, + (VOID *)&FileEntry, + sizeof (UDF_FILE_ENTRY) + ); + CopyMem ( + (VOID *)&NewPrivFileData->File.FileIdentifierDesc, + (VOID *)&FileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + CopyMem ( + (VOID *)&NewPrivFileData->File.ParentFileIdentifierDesc, + (VOID *)&ParentFileIdentifierDesc, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR) + ); + +HandleRootDirectory: + NewPrivFileData->IsRootDirectory = IsRootDirectory; + NewPrivFileData->FilePosition = 0; + NewPrivFileData->NextEntryOffset = 0; + + *NewHandle = &NewPrivFileData->FileIo; + Status = EFI_SUCCESS; + } else { + Status = EFI_NOT_FOUND; + } + +Exit: + if (String) { + FreePool ((VOID *)String); + } + + gBS->RestoreTPL (OldTpl); + + return Status; } /** @@ -192,7 +532,35 @@ UdfClose ( IN EFI_FILE_PROTOCOL *This ) { - return EFI_SUCCESS; + EFI_TPL OldTpl; + EFI_STATUS Status; + PRIVATE_UDF_FILE_DATA *PrivFileData; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (!This) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + Status = EFI_SUCCESS; + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + if (PrivFileData->AbsoluteFileName) { + FreePool ((VOID *)PrivFileData->AbsoluteFileName); + } + + if (PrivFileData->FileName) { + FreePool ((VOID *)PrivFileData->FileName); + } + + FreePool ((VOID *)PrivFileData); + +Exit: + gBS->RestoreTPL (OldTpl); + + return Status; } /** @@ -210,6 +578,16 @@ UdfDelete ( IN EFI_FILE_PROTOCOL *This ) { + PRIVATE_UDF_FILE_DATA *PrivFileData; + + if (!This) { + return EFI_INVALID_PARAMETER; + } + + PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This); + + (VOID)PrivFileData->FileIo.Close(This); + return EFI_WARN_DELETE_FAILURE; } @@ -761,3 +1139,141 @@ IsSupportedUdfVolume ( Exit: return Status; } + +EFI_STATUS +EFIAPI +ReadDirectory ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UINT32 PartitionStartingLocation, + IN UINT32 PartitionLength, + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *ParentFileIdentifierDesc, + OUT UDF_FILE_IDENTIFIER_DESCRIPTOR *ReadFileIdentifierDesc, + IN OUT UINT64 *NextOffset, + OUT BOOLEAN *ReadDone + ) +{ + EFI_STATUS Status; + UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd; + UINT64 Lsn; + UINT64 ParentOffset; + UINT64 FidLength; + UINT64 Offset; + UINT64 EndingPartitionOffset; + + Status = EFI_SUCCESS; + *ReadDone = FALSE; + + // + // Check if Parent is _really_ a directory. Otherwise, do nothing. + // + if (!IS_FID_DIRECTORY_FILE (ParentFileIdentifierDesc)) { + Status = EFI_NOT_FOUND; + goto Exit; + } + + LongAd = &ParentFileIdentifierDesc->Icb; + + // + // Point to the Parent FID + // + Lsn = (UINT64)(PartitionStartingLocation + + LongAd->ExtentLocation.LogicalBlockNumber + 1); + + // + // Calculate offset of the Parent FID + // + ParentOffset = Lsn * LOGICAL_BLOCK_SIZE; + + EndingPartitionOffset = (UINT64)((UINT64)(PartitionStartingLocation + + PartitionLength) * + LOGICAL_BLOCK_SIZE); + + if (!*NextOffset) { + Offset = ParentOffset; + } else { + Offset = *NextOffset; + } + + // + // Make sure we don't across a partition boundary + // + if (Offset > EndingPartitionOffset) { + goto Exit; + } + + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + Offset, + sizeof (UDF_FILE_IDENTIFIER_DESCRIPTOR), + (VOID *)ReadFileIdentifierDesc + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (!IS_FID (ReadFileIdentifierDesc)) { + goto Exit; + } + + FidLength = 38 + // Offset of Implementation Use field + ReadFileIdentifierDesc->LengthOfFileIdentifier + + ReadFileIdentifierDesc->LengthOfImplementationUse + + (4 * ((ReadFileIdentifierDesc->LengthOfFileIdentifier + + ReadFileIdentifierDesc->LengthOfImplementationUse + 38 + 3) / 4) - + (ReadFileIdentifierDesc->LengthOfFileIdentifier + + ReadFileIdentifierDesc->LengthOfImplementationUse + 38)); + + *NextOffset = Offset + FidLength; + *ReadDone = TRUE; + +Exit: + return Status; +} + +EFI_STATUS +EFIAPI +FileIdentifierDescToFileName ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc, + OUT CHAR16 **FileName + ) +{ + EFI_STATUS Status; + CHAR16 *FileIdentifier; + CHAR16 *String; + UINTN Index; + + Status = EFI_SUCCESS; + + *FileName = AllocatePool ( + FileIdentifierDesc->LengthOfFileIdentifier + + sizeof (CHAR16) + ); + if (!*FileName) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // +2 == discards the Compression ID found in the File Identifier field + // + FileIdentifier = (CHAR16 *)( + (UINT8 *)&FileIdentifierDesc->Data + + FileIdentifierDesc->LengthOfImplementationUse + 2 + ); + + String = *FileName; + + for (Index = 2; + Index < FileIdentifierDesc->LengthOfFileIdentifier; + Index += 2 + ) { + *String++ = *FileIdentifier++; + } + + *String = '\0'; + +Exit: + return Status; +} diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h index 7986445..a2fe18e 100644 --- a/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h +++ b/MdeModulePkg/Universal/Disk/UdfDxe/Udf.h @@ -680,6 +680,31 @@ FindFileIdentifierDescriptorRootDir ( OUT UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc ); +EFI_STATUS +EFIAPI +ReadDirectory ( + IN EFI_BLOCK_IO_PROTOCOL *BlockIo, + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UINT32 PartitionStartingLocation, + IN UINT32 PartitionLength, + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *ParentFileIdentifierDesc, + OUT UDF_FILE_IDENTIFIER_DESCRIPTOR *ReadFileIdentifierDesc, + IN OUT UINT64 *NextOffset, + OUT BOOLEAN *ReadDone + ); + +EFI_STATUS +EFIAPI +FileIdentifierDescToFileName ( + IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc, + OUT UINT16 **FileName + ); + +CHAR16 * +MangleFileName ( + CHAR16 *FileName + ); + /** Test to see if this driver supports ControllerHandle. Any ControllerHandle than contains a BlockIo and DiskIo protocol can be supported. diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf b/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf index 538a618..dd30863 100644 --- a/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf +++ b/MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf @@ -32,6 +32,7 @@ [Sources] ComponentName.c + FileName.c FileSystemOperations.c Udf.c Udf.h -- 1.9.3 ------------------------------------------------------------------------------ Slashdot TV. Video for Nerds. Stuff that matters. http://tv.slashdot.org/ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel