Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Paulo Alcantara <[email protected]>
---
.../Universal/Disk/UdfDxe/FileSystemOperations.c | 431 ++++++++++++++++++++-
1 file changed, 428 insertions(+), 3 deletions(-)
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
index 031e9ae..2cd1b0b 100644
--- a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
+++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
@@ -515,7 +515,365 @@ UdfRead (
OUT VOID *Buffer
)
{
- return EFI_DEVICE_ERROR;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ PRIVATE_UDF_FILE_DATA *PrivFileData;
+ UINT32 PartitionStartingLocation;
+ UINT32 PartitionLength;
+ UDF_FILE_ENTRY *ParentFileEntry;
+ UDF_FILE_IDENTIFIER_DESCRIPTOR *ParentFileIdentifierDesc;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ EFI_DISK_IO_PROTOCOL *DiskIo;
+ UINT64 FilePosition;
+ INT64 ExtStartOffset;
+ UINT32 ExtLen;
+ UINT32 ShortAdsNo;
+ UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd;
+ UINT64 BufferOffset;
+ UINTN BytesLeft;
+ UDF_FILE_IDENTIFIER_DESCRIPTOR FileIdentifierDesc;
+ BOOLEAN ReadDone;
+ UINTN FileInfoLength;
+ EFI_FILE_INFO *FileInfo;
+ CHAR16 *FileName;
+ UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd;
+ UINT64 Lsn;
+ UINT64 Offset;
+ UDF_FILE_ENTRY FileEntry;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if ((!This) || (!Buffer)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+ PartitionStartingLocation = PrivFileData->Partition.StartingLocation;
+ PartitionLength = PrivFileData->Partition.Length;
+
+ if (PrivFileData->IsRootDirectory) {
+ ParentFileEntry = &PrivFileData->Root.FileEntry;
+ ParentFileIdentifierDesc =
+ &PrivFileData->Root.FileIdentifierDesc;
+ } else {
+ ParentFileEntry = &PrivFileData->File.FileEntry;
+ ParentFileIdentifierDesc =
+ &PrivFileData->File.FileIdentifierDesc;
+ }
+
+ BlockIo = PrivFileData->BlockIo;
+ DiskIo = PrivFileData->DiskIo;
+ FileName = NULL;
+
+ if (IS_FID_NORMAL_FILE (ParentFileIdentifierDesc)) {
+ //
+ // Check if the current position is beyond the EOF
+ //
+ if (PrivFileData->FilePosition > ParentFileEntry->InformationLength) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ } else if (
+ PrivFileData->FilePosition == ParentFileEntry->InformationLength
+ ) {
+ *BufferSize = 0;
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ //
+ // File Type should be 5 for a standard byte addressable file
+ //
+ if (ParentFileEntry->IcbTag.FileType != 5) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // The Type of Allocation Descriptor (bit 0-2) in Flags field of ICB Tag
+ // shall be set to 0 which means that Short Allocation Descriptors are
used.
+ //
+ if (ParentFileEntry->IcbTag.Flags & 0x07) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // Strategy Type of 4 is the only supported
+ //
+ if (ParentFileEntry->IcbTag.StrategyType != 4) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // OK, now read file's extents to find recorded data.
+ //
+ ShortAdsNo = ParentFileEntry->LengthOfAllocationDescriptors /
+ sizeof (UDF_SHORT_ALLOCATION_DESCRIPTOR);
+
+ //
+ // NOTE: The total length of a File Entry shall not exceed the size of one
+ // logical block, so it's OK.
+ //
+ ShortAd = (UDF_SHORT_ALLOCATION_DESCRIPTOR *)(
+ (UINT8 *)&ParentFileEntry->Data +
+ ParentFileEntry->LengthOfExtendedAttributes
+ );
+
+ ExtStartOffset = 0;
+ ExtLen = 0;
+
+ if (!PrivFileData->FilePosition) {
+ //
+ // OK. Start reading file from its first extent.
+ //
+ goto ReadFile;
+ }
+
+ //
+ // Find which extent corresponds to the current file's position
+ //
+ FilePosition = 0;
+
+ do {
+ if (FilePosition + ShortAd->ExtentLength == PrivFileData->FilePosition) {
+ break;
+ }
+
+ if (FilePosition + ShortAd->ExtentLength > PrivFileData->FilePosition) {
+ ExtStartOffset =
+ ShortAd->ExtentLength - ((FilePosition +
+ ShortAd->ExtentLength) -
+ PrivFileData->FilePosition);
+ if (ExtStartOffset < 0) {
+ ExtStartOffset = -(ExtStartOffset);
+ }
+
+ ExtLen = ExtStartOffset;
+ break;
+ }
+
+ FilePosition += ShortAd->ExtentLength;
+ ShortAd++;
+ } while (--ShortAdsNo);
+
+ if (!ShortAdsNo) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ReadFile:
+ //
+ // Found file position through the extents. Now, start reading file.
+ //
+ BufferOffset = 0;
+ BytesLeft = *BufferSize;
+
+ while ((ShortAdsNo--) && (BytesLeft)) {
+ if (ShortAd->ExtentLength - ExtLen > BytesLeft) {
+ ExtLen = BytesLeft;
+ } else {
+ ExtLen = ShortAd->ExtentLength - ExtLen;
+ }
+
+ Offset = (UINT64)((UINT64)(PartitionStartingLocation +
+ ShortAd->ExtentPosition) *
+ LOGICAL_BLOCK_SIZE + ExtStartOffset);
+
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ Offset,
+ ExtLen,
+ (VOID *)((UINT8 *)Buffer + BufferOffset)
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ BytesLeft -= ExtLen;
+ BufferOffset += ExtLen;
+ PrivFileData->FilePosition += ExtLen;
+ ExtStartOffset = 0;
+ ExtLen = 0;
+ ShortAd++;
+ }
+
+ *BufferSize = BufferOffset;
+ Status = EFI_SUCCESS;
+ } else if (IS_FID_DIRECTORY_FILE (ParentFileIdentifierDesc)) {
+ if ((!PrivFileData->NextEntryOffset) && (PrivFileData->FilePosition)) {
+ goto NoDirectoryEntriesLeft;
+ }
+
+ //
+ // Read directory entry
+ //
+ Status = ReadDirectory (
+ BlockIo,
+ DiskIo,
+ PartitionStartingLocation,
+ PartitionLength,
+ ParentFileIdentifierDesc,
+ &FileIdentifierDesc,
+ &PrivFileData->NextEntryOffset,
+ &ReadDone
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (!ReadDone) {
+ PrivFileData->NextEntryOffset = 0;
+ goto NoDirectoryEntriesLeft;
+ }
+
+ //
+ // Set up EFI_FILE_INFO structure
+ //
+ Status = FileIdentifierDescToFileName (&FileIdentifierDesc, &FileName);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // Find FE of the directory entry
+ //
+ 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;
+ }
+
+ //
+ // TODO: check if ICB Tag's flags field contain all valid bits set
+ //
+ if (!IS_FE (&FileEntry)) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // Check if BufferSize is too small to read directory entry
+ //
+ FileInfoLength = sizeof (EFI_FILE_INFO) + StrLen (FileName) + 1;
+ if (*BufferSize < FileInfoLength) {
+ *BufferSize = FileInfoLength;
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+
+ FileInfo = (EFI_FILE_INFO *)Buffer;
+
+ FileInfo->Size = FileInfoLength;
+ FileInfo->Attribute &= ~EFI_FILE_VALID_ATTR;
+ FileInfo->Attribute |= EFI_FILE_READ_ONLY;
+
+ if (IS_FID_DIRECTORY_FILE (&FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+ } else if (IS_FID_NORMAL_FILE (&FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_ARCHIVE;
+ }
+
+ if (IS_FID_HIDDEN_FILE (&FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_HIDDEN;
+ }
+
+ //
+ // Check if file has System bit set (bit 10)
+ //
+ if (FileEntry.IcbTag.Flags & (1 << 10)) {
+ FileInfo->Attribute |= EFI_FILE_SYSTEM;
+ }
+
+ FileInfo->FileSize = FileEntry.InformationLength;
+ FileInfo->PhysicalSize = FileEntry.InformationLength;
+
+ FileInfo->CreateTime.Year = FileEntry.AccessTime.Year;
+ FileInfo->CreateTime.Month = FileEntry.AccessTime.Month;
+ FileInfo->CreateTime.Day = FileEntry.AccessTime.Day;
+ FileInfo->CreateTime.Hour = FileEntry.AccessTime.Hour;
+ FileInfo->CreateTime.Minute = FileEntry.AccessTime.Second;
+ FileInfo->CreateTime.Second = FileEntry.AccessTime.Second;
+ FileInfo->CreateTime.Nanosecond =
+ FileEntry.AccessTime.HundredsOfMicroseconds;
+
+ //
+ // For OSTA UDF compliant media, the time within the UDF_TIMESTAMP
+ // structures should be interpreted as Local Time. Use
+ // EFI_UNSPECIFIED_TIMEZONE for Local Time.
+ //
+ FileInfo->CreateTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
+ FileInfo->CreateTime.Daylight = EFI_TIME_ADJUST_DAYLIGHT;
+
+ //
+ // As per ECMA-167 specification, the Modification Time should be identical
+ // to the content of the Access Time field.
+ //
+ CopyMem (
+ (VOID *)&FileInfo->ModificationTime,
+ (VOID *)&FileInfo->CreateTime,
+ sizeof (EFI_TIME)
+ );
+
+ //
+ // Since we're accessing a DVD read-only disc - the Last Access Time
+ // field, obviously, should be the same as Create Time.
+ //
+ CopyMem (
+ (VOID *)&FileInfo->LastAccessTime,
+ (VOID *)&FileInfo->CreateTime,
+ sizeof (EFI_TIME)
+ );
+
+ StrCpy (FileInfo->FileName, FileName);
+
+ //
+ // Update the current position to the next directory entry
+ //
+ PrivFileData->FilePosition++;
+
+ *BufferSize = FileInfoLength;
+ Status = EFI_SUCCESS;
+ } else if (IS_FID_DELETED_FILE (ParentFileIdentifierDesc)) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ Status = EFI_VOLUME_CORRUPTED;
+ }
+
+Exit:
+ if (FileName) {
+ FreePool ((VOID *)FileName);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+
+NoDirectoryEntriesLeft:
+ *BufferSize = 0;
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
}
/**
@@ -637,7 +995,30 @@ UdfGetPosition (
OUT UINT64 *Position
)
{
- return EFI_UNSUPPORTED;
+ EFI_STATUS Status;
+ PRIVATE_UDF_FILE_DATA *PrivFileData;
+
+ Status = EFI_SUCCESS;
+
+ PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+ //
+ // As per UEFI spec, if the file handle is a directory, then the current file
+ // position has no meaning and the operation is not supported.
+ //
+ if (IS_FID_DIRECTORY_FILE (
+ &PrivFileData->File.FileIdentifierDesc
+ )
+ ) {
+ Status = EFI_UNSUPPORTED;
+ } else {
+ //
+ // The file is not a directory. So, return its position.
+ //
+ *Position = PrivFileData->FilePosition;
+ }
+
+ return Status;
}
/**
@@ -657,7 +1038,51 @@ UdfSetPosition (
IN UINT64 Position
)
{
- return EFI_UNSUPPORTED;
+ EFI_STATUS Status;
+ PRIVATE_UDF_FILE_DATA *PrivFileData;
+ UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc;
+ UDF_FILE_ENTRY *FileEntry;
+
+ Status = EFI_SUCCESS;
+
+ PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
+
+ if (PrivFileData->IsRootDirectory) {
+ FileIdentifierDesc =
+ &PrivFileData->Root.FileIdentifierDesc;
+ FileEntry = &PrivFileData->Root.FileEntry;
+ } else {
+ FileIdentifierDesc = &PrivFileData->File.FileIdentifierDesc;
+ FileEntry = &PrivFileData->File.FileEntry;
+ }
+
+ if (IS_FID_DIRECTORY_FILE (FileIdentifierDesc)) {
+ //
+ // If the file handle is a directory, the _only_ position that may be set
is
+ // zero. This has no effect of starting the read proccess of the directory
+ // entries over.
+ //
+ if (Position != 0) {
+ Status = EFI_UNSUPPORTED;
+ } else {
+ PrivFileData->FilePosition = Position;
+ PrivFileData->NextEntryOffset = 0;
+ }
+ } else if (IS_FID_NORMAL_FILE (FileIdentifierDesc)) {
+ //
+ // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be
+ // set to the EOF.
+ //
+ if (Position == 0xFFFFFFFFFFFFFFFF) {
+ PrivFileData->FilePosition = FileEntry->InformationLength - 1;
+ } else {
+ PrivFileData->FilePosition = Position;
+ }
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ return Status;
}
/**
--
1.9.3
------------------------------------------------------------------------------
Slashdot TV.
Video for Nerds. Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel