Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for GPT partition tables #406

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions src/ExFatLib/ExFatPartition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
#define DBG_FILE "ExFatPartition.cpp"
#include "../common/DebugMacros.h"
#include "../common/PartitionTable.h"
#include "ExFatLib.h"
//------------------------------------------------------------------------------
// return 0 if error, 1 if no space, else start cluster.
Expand Down Expand Up @@ -269,29 +270,18 @@ int32_t ExFatPartition::freeClusterCount() {
bool ExFatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) {
pbs_t* pbs;
BpbExFat_t* bpb;
MbrSector_t* mbr;
m_fatType = 0;
m_blockDev = dev;
cacheInit(m_blockDev);
// if part == 0 assume super floppy with FAT boot sector in sector zero
// if part > 0 assume mbr volume with partition table
// if part > 0 read MBR / GPT partition table
if (part) {
if (part > 4) {
DBG_FAIL_MACRO;
goto fail;
}
mbr = reinterpret_cast<MbrSector_t*>
(dataCachePrepare(0, FsCache::CACHE_FOR_READ));
if (!mbr) {
DBG_FAIL_MACRO;
goto fail;
}
MbrPart_t* mp = mbr->part + part - 1;
if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
volStart = partitionTableGetVolumeStartSector(m_dataCache, part);

if (!volStart) {
DBG_FAIL_MACRO;
goto fail;
}
volStart = getLe32(mp->relativeSectors);
}
pbs = reinterpret_cast<pbs_t*>
(dataCachePrepare(volStart, FsCache::CACHE_FOR_READ));
Expand Down
20 changes: 5 additions & 15 deletions src/FatLib/FatPartition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <string.h>
#define DBG_FILE "FatPartition.cpp"
#include "../common/DebugMacros.h"
#include "../common/PartitionTable.h"
#include "FatLib.h"
//------------------------------------------------------------------------------
bool FatPartition::allocateCluster(uint32_t current, uint32_t* next) {
Expand Down Expand Up @@ -396,7 +397,6 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) {
m_blockDev = dev;
pbs_t* pbs;
BpbFat32_t* bpb;
MbrSector_t* mbr;
uint8_t tmp;
m_fatType = 0;
m_allocSearchStart = 1;
Expand All @@ -405,24 +405,14 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) {
m_fatCache.init(dev);
#endif // USE_SEPARATE_FAT_CACHE
// if part == 0 assume super floppy with FAT boot sector in sector zero
// if part > 0 assume mbr volume with partition table
// if part > 0 read MBR/GPT partition table
if (part) {
if (part > 4) {
DBG_FAIL_MACRO;
goto fail;
}
mbr = reinterpret_cast<MbrSector_t*>
(dataCachePrepare(0, FsCache::CACHE_FOR_READ));
if (!mbr) {
DBG_FAIL_MACRO;
goto fail;
}
MbrPart_t* mp = mbr->part + part - 1;
if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
volStart = partitionTableGetVolumeStartSector(m_cache, part);

if (!volStart) {
DBG_FAIL_MACRO;
goto fail;
}
volStart = getLe32(mp->relativeSectors);
}
pbs = reinterpret_cast<pbs_t*>
(dataCachePrepare(volStart, FsCache::CACHE_FOR_READ));
Expand Down
30 changes: 30 additions & 0 deletions src/common/FsStructs.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,36 @@ typedef struct masterBootRecordSector {
uint8_t signature[2];
} MbrSector_t;
//------------------------------------------------------------------------------
// GPT partition structures based on https://github.com/KirollousMoheb/GUID-Partition-Table-Parser
// Copyright (c) 2022 Kirollous Moheb, under MIT License
typedef struct
{
uint8_t part_type_guid[16];
uint8_t unique_part_guid[16];
uint8_t first_lba[8];
uint8_t last_lba[8];
uint8_t attr_flags[8];
uint8_t part_name[72];
} GPT_PartitionEntry_t;
//------------------------------------------------------------------------------
typedef struct
{
uint8_t signature[8];
uint8_t revision[4];
uint8_t header_size[4];
uint8_t crc32[4];
uint8_t reserved[4];
uint8_t current_lba[8];
uint8_t backup_lba[8];
uint8_t first_usable_lba[8];
uint8_t last_usable_lba[8];
uint8_t disk_guid[16];
uint8_t part_entry_start_lba[8];
uint8_t num_part_entries[4];
uint8_t part_entry_size[4];
uint8_t crc32_part_array[4];
} GPT_Header_t;
//------------------------------------------------------------------------------
typedef struct partitionBootSector {
uint8_t jmpInstruction[3];
char oemName[8];
Expand Down
103 changes: 103 additions & 0 deletions src/common/PartitionTable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Copyright (c) 2011-2022 Bill Greiman
*
* This file is part of the SdFat library for SD memory cards.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#include "PartitionTable.h"
#include "DebugMacros.h"
#include "FsStructs.h"

// Read MBR format partition table, retrieve the start sector number of a volume
static uint32_t partitionTableGetVolumeStartSectorMBR(FsCache& fscache, uint8_t part)
{
if (part <= 0 || part > 4) {
DBG_FAIL_MACRO;
return 0;
}

const uint8_t *sector = fscache.prepare(0, FsCache::CACHE_FOR_READ);
const MbrSector_t *mbr = reinterpret_cast<const MbrSector_t*>(sector);
if (!mbr) {
DBG_FAIL_MACRO;
return 0;
}

const MbrPart_t *mp = mbr->part + part - 1;
if (mp->type == 0 || getLe32(mp->totalSectors) == 0) {
DBG_FAIL_MACRO;
return 0;
}

return getLe32(mp->relativeSectors);
}

// Read GPT format partition table, retrieve the start sector number of a volume
static uint32_t partitionTableGetVolumeStartSectorGPT(FsCache& fscache, uint8_t part)
{
if (part <= 0 || part > 4) {
DBG_FAIL_MACRO;
return 0;
}

const uint8_t *sector = fscache.prepare(1, FsCache::CACHE_FOR_READ);
const GPT_Header_t *gpt = reinterpret_cast<const GPT_Header_t*>(sector);

if (getLe64(gpt->signature) != 0x5452415020494645ULL) {
DBG_FAIL_MACRO;
return 0;
}

// First 4 partition table entries are in LBA 2
sector = fscache.prepare(2, FsCache::CACHE_FOR_READ);
const GPT_PartitionEntry_t *partentry =
reinterpret_cast<const GPT_PartitionEntry_t*>(sector + 128 * (part - 1));

uint64_t startSector = getLe64(partentry->first_lba);
uint32_t startSector32 = static_cast<uint32_t>(startSector);
if (startSector32 != startSector)
{
// Currently limited to 2^32 sectors = 2 TB by other parts of SdFat code.
DBG_FAIL_MACRO;
return 0;
}

return startSector32;
}

uint32_t partitionTableGetVolumeStartSector(FsCache& fscache, uint8_t part)
{
uint32_t start;

// Check for GPT partition table first, because it has clearly identifiable
// signature. It is also common for GPT-partitioned drives to have MBR-style
// fallback boot record at the start.
start = partitionTableGetVolumeStartSectorGPT(fscache, part);

if (start == 0)
{
start = partitionTableGetVolumeStartSectorMBR(fscache, part);
}

return start;
}
40 changes: 40 additions & 0 deletions src/common/PartitionTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2011-2022 Bill Greiman
*
* This file is part of the SdFat library for SD memory cards.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PartitionTable_h
#define PartitionTable_h

#include "FsCache.h"

/**
* Parse either MBR or GPT format partition table to get the first sector
* number of a volume.
* \param[in] fscache Cache instance used to read the disk.
* \param[in] part Index of partition to read, 1 to 4.
* \return First sector of volume, or 0 if partition table could not be parsed.
*/
uint32_t partitionTableGetVolumeStartSector(FsCache& fscache, uint8_t part);

#endif