-
Notifications
You must be signed in to change notification settings - Fork 111
feat: Add a component for parsing external partition tables (IEC-333) #529
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
base: master
Are you sure you want to change the base?
feat: Add a component for parsing external partition tables (IEC-333) #529
Conversation
0d3df95
to
1589048
Compare
1589048
to
c13b7e6
Compare
c13b7e6
to
9a5f764
Compare
dbf974f
to
2c836ab
Compare
@igrr @pacucha42 please take a look |
a3c2056
to
8572c6f
Compare
case ESP_EXT_PART_TYPE_FAT16: | ||
case ESP_EXT_PART_TYPE_FAT32: | ||
// Set CHS values based on LBA start and end | ||
esp_mbr_lba_to_chs_arr(partition->chs_start, lba_start); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, provide an explanation why you fill the CHS fields in for LBA file-system types (as discussed 1:1, this is given mostly by the FatFS support of FDISK functionality). Tbh, it would be great to know the reasons behind, otherwise this operation looks useless
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do it for FAT partitions to stay as close to FATFS library behavior ESP-IDF is using. FATFS library can format partitions and generate MBR as well and it does fill CHS fields. However technically it isn't needed because modern systems should ignore it in favor of LBA.
// Set default arguments for MBR generation | ||
esp_mbr_generate_extra_args_t args = { | ||
.sector_size = part_list->sector_size != ESP_EXT_PART_SECTOR_SIZE_UNKNOWN ? part_list->sector_size : ESP_EXT_PART_SECTOR_SIZE_512B, // Default sector size | ||
.alignment = ESP_EXT_PART_ALIGN_1MiB, // Default alignment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we enforce 1MB partition alignment? Isn't this option a good candidate for a parameter setting? If not, why don't we use 4kB which would fit much better into usual IoT storage device requirements?
Sorry, I have missed ESP_EXT_PART_ALIGN_1MiB is just a default. Other questions are valid. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SD cards present themselves with minimal read/write size of 512B, which is 1 sector. However internally NAND page sizes can be from 4kB to 64kB. If the accessed data is misaligned on those boundaries, it can lead to slower performance. This sector and page sizes are also different for different storage media.
I used 1MiB as a default because it is dividable by all of those sizes and should be always properly aligned, plus this component is to be used with SD cards or maybe USB drives whose capacities are in from tens to thousands of gigabytes range. If this component is to be used with a media of smaller capacity, 4kiB still can be chosen.
The code base looks good to me, however, I left few questions here and there. Thanks for you efforts @adokitkat ! Generally, it would be great to provide more explanations and in-code comments. |
6f26de1
to
99bc9c1
Compare
bb471c0
to
7fe043a
Compare
9c1a86d
to
b901d2a
Compare
* - ESP_ERR_INVALID_ARG: dst or src is NULL. | ||
* - ESP_ERR_NO_MEM: Memory allocation failed. | ||
*/ | ||
esp_err_t esp_ext_part_list_deep_copy(esp_ext_part_list_t* dst, esp_ext_part_list_t* src); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering: what would be use-case for such a function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest I thought I need it for something in an example code but then I resolved the problem another way and the function stayed here. I just thought copying the structure is non-trivial due to pointers and malloc'd memory so I created this function (i.e. the logic is hidden here and if there were changes in the part list struct users wouldn't need to change anything in their code). However right now I am not sure if this functionality needs to be in the public API (if and why it is something users would need often), so I can remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One use case may be if the user wanted to load the MBR and make changes to it, while also keeping the original part list created from the MBR as "a reference" / a backup, so the changes would be made to a copy.
switch (part_list->signature.type) { | ||
case ESP_EXT_PART_LIST_SIGNATURE_MBR: | ||
uint32_t out = (uint32_t) part_list->signature.data[0]; | ||
memcpy(signature, &out, sizeof(uint32_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this line necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used memcpy
in case void* signature
points to to an array or something for some reason and out
is just a temporary variable so the code is cleaner (will probably be optimized out anyway?).
b901d2a
to
ed56bd8
Compare
LGTM, though the code still needs polishing. I left few comments, mostly minor things. Thanks @adokitkat |
867ba49
to
1353d2d
Compare
return ESP_ERR_INVALID_ARG; | ||
} | ||
|
||
memcpy(dst, src, sizeof(esp_ext_part_list_t)); // Copy the structure |
Check warning
Code scanning / clang-tidy
Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
} | ||
|
||
memcpy(dst, src, sizeof(esp_ext_part_list_t)); // Copy the structure | ||
memset(&dst->head, 0, sizeof(dst->head)); // Reset the head of the destination list |
Check warning
Code scanning / clang-tidy
Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memset_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
chs_bytes[2] = cylinder & 0xFF; | ||
|
||
if (chs != NULL) { | ||
memcpy(chs, chs_bytes, 3); |
Check warning
Code scanning / clang-tidy
Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
|
||
// Check if the partition entry is empty and if so, skip it | ||
if (item->info.type == ESP_EXT_PART_TYPE_NONE) { | ||
memset(partition, 0, sizeof(mbr_partition_t)); |
Check warning
Code scanning / clang-tidy
Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memset_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
} | ||
if (gap_index != i) { | ||
// Move the partition to the next available index | ||
memcpy(&mbr->partition_table[gap_index], partition, sizeof(mbr_partition_t)); |
Check warning
Code scanning / clang-tidy
Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
if (gap_index != i) { | ||
// Move the partition to the next available index | ||
memcpy(&mbr->partition_table[gap_index], partition, sizeof(mbr_partition_t)); | ||
memset(partition, 0, sizeof(mbr_partition_t)); // Clear the old entry |
Check warning
Code scanning / clang-tidy
Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memset_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning
3c3fd55
to
322bf92
Compare
Checklist
url
field definedDescription
This MR adds a component which can parse and generate external partition tables. Right now it only supports MBR. Adding support for another partition table like GPT is possible but not planned for now.
Parsing a partition table outputs
esp_ext_part_list_t
structure filled withesp_ext_part_list_item_t
items.When generating MBR, user can decide on sector size (512B, 4KiB) and partition alignment (4KiB, 1MiB). The defaults are 512B sector size and 1MiB partition alignment.
Note: Generating MBR doesn't mean formatting any partition on given sector span, however helper function could be created which would combine this behavior - each partition type has it's own formatting function (FATFS formats differently than LittleFS, etc.).
These are recognized types:
Future TODOs and could haves: