\r
#include "Partition.h"\r
\r
+#define MAX_CORRECTION_BLOCKS_NUM 512u\r
+\r
//\r
// C5BD4D42-1A76-4996-8956-73CDA326CD0A\r
//\r
/**\r
Find the anchor volume descriptor pointer.\r
\r
- @param[in] BlockIo BlockIo interface.\r
- @param[in] DiskIo DiskIo interface.\r
- @param[out] AnchorPoint Anchor volume descriptor pointer.\r
+ @param[in] BlockIo BlockIo interface.\r
+ @param[in] DiskIo DiskIo interface.\r
+ @param[out] AnchorPoint Anchor volume descriptor pointer.\r
+ @param[out] LastRecordedBlock Last recorded block.\r
\r
- @retval EFI_SUCCESS Anchor volume descriptor pointer found.\r
- @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
- @retval other Anchor volume descriptor pointer not found.\r
+ @retval EFI_SUCCESS Anchor volume descriptor pointer found.\r
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
+ @retval other Anchor volume descriptor pointer not found.\r
\r
**/\r
EFI_STATUS\r
FindAnchorVolumeDescriptorPointer (\r
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,\r
IN EFI_DISK_IO_PROTOCOL *DiskIo,\r
- OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint\r
+ OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint,\r
+ OUT EFI_LBA *LastRecordedBlock\r
)\r
{\r
- EFI_STATUS Status;\r
- UINT32 BlockSize;\r
- EFI_LBA EndLBA;\r
- EFI_LBA DescriptorLBAs[4];\r
- UINTN Index;\r
- UDF_DESCRIPTOR_TAG *DescriptorTag;\r
+ EFI_STATUS Status;\r
+ UINT32 BlockSize;\r
+ EFI_LBA EndLBA;\r
+ UDF_DESCRIPTOR_TAG *DescriptorTag;\r
+ UINTN AvdpsCount;\r
+ UINTN Size;\r
+ UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoints;\r
+ INTN Index;\r
+ UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPointPtr;\r
+ EFI_LBA LastAvdpBlockNum;\r
\r
+ //\r
+ // UDF 2.60, 2.2.3 Anchor Volume Descriptor Pointer\r
+ //\r
+ // An Anchor Volume Descriptor Pointer structure shall be recorded in at\r
+ // least 2 of the following 3 locations on the media: Logical Sector 256,\r
+ // N - 256 or N, where N is the last *addressable* sector of a volume.\r
+ //\r
+ // To figure out what logical sector N is, the SCSI commands READ CAPACITY and\r
+ // READ TRACK INFORMATION are used, however many drives or medias report their\r
+ // "last recorded block" wrongly. Although, READ CAPACITY returns the last\r
+ // readable data block but there might be unwritten blocks, which are located\r
+ // outside any track and therefore AVDP will not be found at block N.\r
+ //\r
+ // That said, we define a magic number of 512 blocks to be used as correction\r
+ // when attempting to find AVDP and define last block number.\r
+ //\r
BlockSize = BlockIo->Media->BlockSize;\r
EndLBA = BlockIo->Media->LastBlock;\r
- DescriptorLBAs[0] = 256;\r
- DescriptorLBAs[1] = EndLBA - 256;\r
- DescriptorLBAs[2] = EndLBA;\r
- DescriptorLBAs[3] = 512;\r
+ *LastRecordedBlock = EndLBA;\r
+ AvdpsCount = 0;\r
\r
- for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) {\r
- Status = DiskIo->ReadDisk (\r
- DiskIo,\r
- BlockIo->Media->MediaId,\r
- MultU64x32 (DescriptorLBAs[Index], BlockSize),\r
- sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER),\r
- (VOID *)AnchorPoint\r
- );\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
+ //\r
+ // Find AVDP at block 256\r
+ //\r
+ Status = DiskIo->ReadDisk (\r
+ DiskIo,\r
+ BlockIo->Media->MediaId,\r
+ MultU64x32 (256, BlockSize),\r
+ sizeof (*AnchorPoint),\r
+ AnchorPoint\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DescriptorTag = &AnchorPoint->DescriptorTag;\r
+\r
+ //\r
+ // Check if read block is a valid AVDP descriptor\r
+ //\r
+ if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {\r
+ DEBUG ((DEBUG_INFO, "%a: found AVDP at block %d\n", __FUNCTION__, 256));\r
+ AvdpsCount++;\r
+ }\r
+\r
+ //\r
+ // Find AVDP at block N - 256\r
+ //\r
+ Status = DiskIo->ReadDisk (\r
+ DiskIo,\r
+ BlockIo->Media->MediaId,\r
+ MultU64x32 ((UINT64)EndLBA - 256, BlockSize),\r
+ sizeof (*AnchorPoint),\r
+ AnchorPoint\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check if read block is a valid AVDP descriptor\r
+ //\r
+ if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer &&\r
+ ++AvdpsCount == 2) {\r
+ DEBUG ((DEBUG_INFO, "%a: found AVDP at block %Ld\n", __FUNCTION__,\r
+ EndLBA - 256));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Check if at least one AVDP was found in previous locations\r
+ //\r
+ if (AvdpsCount == 0) {\r
+ return EFI_VOLUME_CORRUPTED;\r
+ }\r
+\r
+ //\r
+ // Find AVDP at block N\r
+ //\r
+ Status = DiskIo->ReadDisk (\r
+ DiskIo,\r
+ BlockIo->Media->MediaId,\r
+ MultU64x32 ((UINT64)EndLBA, BlockSize),\r
+ sizeof (*AnchorPoint),\r
+ AnchorPoint\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check if read block is a valid AVDP descriptor\r
+ //\r
+ if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // No AVDP found at block N. Possibly drive/media returned bad last recorded\r
+ // block, or it is part of unwritten data blocks and outside any track.\r
+ //\r
+ // Search backwards for an AVDP from block N-1 through\r
+ // N-MAX_CORRECTION_BLOCKS_NUM. If any AVDP is found, then correct last block\r
+ // number for the new UDF partition child handle.\r
+ //\r
+ Size = MAX_CORRECTION_BLOCKS_NUM * BlockSize;\r
+\r
+ AnchorPoints = AllocateZeroPool (Size);\r
+ if (AnchorPoints == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Read consecutive MAX_CORRECTION_BLOCKS_NUM disk blocks\r
+ //\r
+ Status = DiskIo->ReadDisk (\r
+ DiskIo,\r
+ BlockIo->Media->MediaId,\r
+ MultU64x32 ((UINT64)EndLBA - MAX_CORRECTION_BLOCKS_NUM, BlockSize),\r
+ Size,\r
+ AnchorPoints\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Out_Free;\r
+ }\r
\r
- DescriptorTag = &AnchorPoint->DescriptorTag;\r
+ Status = EFI_VOLUME_CORRUPTED;\r
+\r
+ //\r
+ // Search for AVDP from blocks N-1 through N-MAX_CORRECTION_BLOCKS_NUM\r
+ //\r
+ for (Index = MAX_CORRECTION_BLOCKS_NUM - 2; Index >= 0; Index--) {\r
+ AnchorPointPtr = (VOID *)((UINTN)AnchorPoints + Index * BlockSize);\r
+\r
+ DescriptorTag = &AnchorPointPtr->DescriptorTag;\r
\r
//\r
- // Check if read LBA has a valid AVDP descriptor.\r
+ // Check if read block is a valid AVDP descriptor\r
//\r
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {\r
- return EFI_SUCCESS;\r
+ //\r
+ // Calculate last recorded block number\r
+ //\r
+ LastAvdpBlockNum = EndLBA - (MAX_CORRECTION_BLOCKS_NUM - Index);\r
+ DEBUG ((DEBUG_WARN, "%a: found AVDP at block %Ld\n", __FUNCTION__,\r
+ LastAvdpBlockNum));\r
+ DEBUG ((DEBUG_WARN, "%a: correcting last block from %Ld to %Ld\n",\r
+ __FUNCTION__, EndLBA, LastAvdpBlockNum));\r
+ //\r
+ // Save read AVDP from last block\r
+ //\r
+ CopyMem (AnchorPoint, AnchorPointPtr, sizeof (*AnchorPointPtr));\r
+ //\r
+ // Set last recorded block number\r
+ //\r
+ *LastRecordedBlock = LastAvdpBlockNum;\r
+ Status = EFI_SUCCESS;\r
+ break;\r
}\r
}\r
- //\r
- // No AVDP found.\r
- //\r
- return EFI_VOLUME_CORRUPTED;\r
+\r
+Out_Free:\r
+ FreePool (AnchorPoints);\r
+ return Status;\r
}\r
\r
/**\r
Find UDF logical volume location and whether it is supported by current EDK2\r
UDF file system implementation.\r
\r
- @param[in] BlockIo BlockIo interface.\r
- @param[in] DiskIo DiskIo interface.\r
- @param[in] AnchorPoint Anchor volume descriptor pointer.\r
- @param[out] MainVdsStartBlock Main VDS starting block number.\r
- @param[out] MainVdsEndBlock Main VDS ending block number.\r
+ @param[in] BlockIo BlockIo interface.\r
+ @param[in] DiskIo DiskIo interface.\r
+ @param[in] AnchorPoint Anchor volume descriptor pointer.\r
+ @param[in] LastRecordedBlock Last recorded block in media.\r
+ @param[out] MainVdsStartBlock Main VDS starting block number.\r
+ @param[out] MainVdsEndBlock Main VDS ending block number.\r
\r
- @retval EFI_SUCCESS UDF logical volume was found.\r
- @retval EFI_VOLUME_CORRUPTED UDF file system structures are corrupted.\r
- @retval EFI_UNSUPPORTED UDF logical volume is not supported.\r
- @retval other Failed to perform disk I/O.\r
+ @retval EFI_SUCCESS UDF logical volume was found.\r
+ @retval EFI_VOLUME_CORRUPTED UDF file system structures are corrupted.\r
+ @retval EFI_UNSUPPORTED UDF logical volume is not supported.\r
+ @retval other Failed to perform disk I/O.\r
\r
**/\r
EFI_STATUS\r
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,\r
IN EFI_DISK_IO_PROTOCOL *DiskIo,\r
IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint,\r
+ IN EFI_LBA LastRecordedBlock,\r
OUT UINT64 *MainVdsStartBlock,\r
OUT UINT64 *MainVdsEndBlock\r
)\r
{\r
EFI_STATUS Status;\r
UINT32 BlockSize;\r
- EFI_LBA LastBlock;\r
UDF_EXTENT_AD *ExtentAd;\r
UINT64 SeqBlocksNum;\r
UINT64 SeqStartBlock;\r
UDF_DESCRIPTOR_TAG *DescriptorTag;\r
\r
BlockSize = BlockIo->Media->BlockSize;\r
- LastBlock = BlockIo->Media->LastBlock;\r
ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent;\r
\r
//\r
// Also make sure it does not exceed maximum number of blocks in the disk.\r
//\r
SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize);\r
- if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastBlock + 1) {\r
+ if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastRecordedBlock + 1) {\r
return EFI_VOLUME_CORRUPTED;\r
}\r
\r
// Check for valid Volume Descriptor Sequence starting block number\r
//\r
SeqStartBlock = (UINT64)ExtentAd->ExtentLocation;\r
- if (SeqStartBlock > LastBlock ||\r
- SeqStartBlock + SeqBlocksNum - 1 > LastBlock) {\r
+ if (SeqStartBlock > LastRecordedBlock ||\r
+ SeqStartBlock + SeqBlocksNum - 1 > LastRecordedBlock) {\r
return EFI_VOLUME_CORRUPTED;\r
}\r
\r
*MainVdsStartBlock = GuardMainVdsStartBlock;\r
//\r
// We do not need to read either LVD or PD descriptors to know the last\r
- // valid block in the found UDF file system. It's already LastBlock.\r
+ // valid block in the found UDF file system. It's already\r
+ // LastRecordedBlock.\r
//\r
- *MainVdsEndBlock = LastBlock;\r
+ *MainVdsEndBlock = LastRecordedBlock;\r
\r
Status = EFI_SUCCESS;\r
}\r
OUT EFI_LBA *EndingLBA\r
)\r
{\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint;\r
+ EFI_LBA LastRecordedBlock;\r
\r
//\r
// Find UDF volume identifiers\r
//\r
// Find Anchor Volume Descriptor Pointer\r
//\r
- Status = FindAnchorVolumeDescriptorPointer (BlockIo, DiskIo, &AnchorPoint);\r
+ Status = FindAnchorVolumeDescriptorPointer (\r
+ BlockIo,\r
+ DiskIo,\r
+ &AnchorPoint,\r
+ &LastRecordedBlock\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
BlockIo,\r
DiskIo,\r
&AnchorPoint,\r
+ LastRecordedBlock,\r
(UINT64 *)StartingLBA,\r
(UINT64 *)EndingLBA\r
);\r