Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Paulo Alcantara <pca...@zytor.com> --- .../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 edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel