* [PATCH 01/12] partitions: dos: save indention level
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 02/12] partition: allocate struct partition_desc in parser Sascha Hauer
` (11 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Save an indention level by continuing early in the loop.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions/dos.c | 52 +++++++++++++++++++++--------------------
1 file changed, 27 insertions(+), 25 deletions(-)
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index 7472824b00..56ec1e3f48 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -190,36 +190,38 @@ static void dos_partition(void *buf, struct block_device *blk,
table = (struct partition_entry *)&buffer[446];
for (i = 0; i < 4; i++) {
+ int n;
+
pentry.first_sec = get_unaligned_le32(&table[i].partition_start);
pentry.size = get_unaligned_le32(&table[i].partition_size);
pentry.dos_partition_type = table[i].type;
- if (pentry.first_sec != 0) {
- int n = pd->used_entries;
- pd->parts[n].first_sec = pentry.first_sec;
- pd->parts[n].size = pentry.size;
- pd->parts[n].dos_partition_type = pentry.dos_partition_type;
- if (signature)
- sprintf(pd->parts[n].partuuid, "%08x-%02d",
- signature, i + 1);
- pd->used_entries++;
-
- if (is_extended_partition(&pentry)) {
- pd->parts[n].size = 2;
-
- if (!extended_partition)
- extended_partition = &pd->parts[n];
- else
- /*
- * An DOS MBR must only contain a single
- * extended partition. Just ignore all
- * but the first.
- */
- dev_warn(blk->dev, "Skipping additional extended partition\n");
- }
-
- } else {
+ if (pentry.first_sec == 0) {
dev_dbg(blk->dev, "Skipping empty partition %d\n", i);
+ continue;
+ }
+
+ n = pd->used_entries;
+ pd->parts[n].first_sec = pentry.first_sec;
+ pd->parts[n].size = pentry.size;
+ pd->parts[n].dos_partition_type = pentry.dos_partition_type;
+ if (signature)
+ sprintf(pd->parts[n].partuuid, "%08x-%02d",
+ signature, i + 1);
+ pd->used_entries++;
+
+ if (is_extended_partition(&pentry)) {
+ pd->parts[n].size = 2;
+
+ if (!extended_partition)
+ extended_partition = &pd->parts[n];
+ else
+ /*
+ * An DOS MBR must only contain a single
+ * extended partition. Just ignore all
+ * but the first.
+ */
+ dev_warn(blk->dev, "Skipping additional extended partition\n");
}
}
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 02/12] partition: allocate struct partition_desc in parser
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
2024-02-19 8:31 ` [PATCH 01/12] partitions: dos: save indention level Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 03/12] partition: allocate struct partition " Sascha Hauer
` (10 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Allocate struct partition_desc in parser rather than in the core. Doing
so allows the efi/dos partition code to embed the struct partition_desc
is a efi/dos specific struct type.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions.c | 10 ++++++----
common/partitions/dos.c | 14 ++++++++++++--
common/partitions/efi.c | 14 ++++++++++++--
common/partitions/parser.h | 3 ++-
4 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/common/partitions.c b/common/partitions.c
index cfcd0e080b..05ebde7ca3 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -113,13 +113,12 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
*/
int parse_partition_table(struct block_device *blk)
{
- struct partition_desc *pdesc;
+ struct partition_desc *pdesc = NULL;
int i;
int rc = 0;
struct partition_parser *parser;
uint8_t *buf;
- pdesc = xzalloc(sizeof(*pdesc));
buf = malloc(2 * SECTOR_SIZE);
rc = block_read(blk, buf, 0, 2);
@@ -132,7 +131,9 @@ int parse_partition_table(struct block_device *blk)
if (!parser)
goto on_error;
- parser->parse(buf, blk, pdesc);
+ pdesc = parser->parse(buf, blk);
+ if (!pdesc)
+ goto on_error;
if (!pdesc->used_entries)
goto on_error;
@@ -150,7 +151,8 @@ int parse_partition_table(struct block_device *blk)
on_error:
free(buf);
- free(pdesc);
+ if (pdesc)
+ parser->partition_free(pdesc);
return rc;
}
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index 56ec1e3f48..01a251201e 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -171,8 +171,7 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
* It seems at least on ARM this routine cannot use temp. stack space for the
* sector. So, keep the malloc/free.
*/
-static void dos_partition(void *buf, struct block_device *blk,
- struct partition_desc *pd)
+static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
{
struct partition_entry *table;
struct partition pentry;
@@ -181,6 +180,7 @@ static void dos_partition(void *buf, struct block_device *blk,
int i;
struct disk_signature_priv *dsp;
uint32_t signature = get_unaligned_le32(buf + 0x1b8);
+ struct partition_desc *pd;
if (signature)
sprintf(blk->cdev.diskuuid, "%08x", signature);
@@ -189,6 +189,8 @@ static void dos_partition(void *buf, struct block_device *blk,
table = (struct partition_entry *)&buffer[446];
+ pd = xzalloc(sizeof(*pd));
+
for (i = 0; i < 4; i++) {
int n;
@@ -244,10 +246,18 @@ static void dos_partition(void *buf, struct block_device *blk,
dev_add_param_uint32(blk->dev, "nt_signature",
dos_set_disk_signature, dos_get_disk_signature,
&dsp->signature, "%08x", dsp);
+
+ return pd;
+}
+
+static void dos_partition_free(struct partition_desc *pd)
+{
+ free(pd);
}
static struct partition_parser dos = {
.parse = dos_partition,
+ .partition_free = dos_partition_free,
.type = filetype_mbr,
};
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index d0e14d5abb..effe512949 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -431,14 +431,14 @@ static void part_set_efi_name(gpt_entry *pte, char *dest)
dest[i] = 0;
}
-static void efi_partition(void *buf, struct block_device *blk,
- struct partition_desc *pd)
+static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
{
gpt_header *gpt = NULL;
gpt_entry *ptes = NULL;
int i = 0;
int nb_part;
struct partition *pentry;
+ struct partition_desc *pd;
if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes)
goto out;
@@ -456,6 +456,8 @@ static void efi_partition(void *buf, struct block_device *blk,
nb_part = MAX_PARTITION;
}
+ pd = xzalloc(sizeof(*pd));
+
for (i = 0; i < nb_part; i++) {
if (!is_pte_valid(&ptes[i], last_lba(blk))) {
dev_dbg(blk->dev, "Invalid pte %d\n", i);
@@ -474,10 +476,18 @@ static void efi_partition(void *buf, struct block_device *blk,
out:
kfree(gpt);
kfree(ptes);
+
+ return pd;
+}
+
+static void efi_partition_free(struct partition_desc *pd)
+{
+ free(pd);
}
static struct partition_parser efi_partition_parser = {
.parse = efi_partition,
+ .partition_free = efi_partition_free,
.type = filetype_gpt,
};
diff --git a/common/partitions/parser.h b/common/partitions/parser.h
index 9cc41a7573..3eec2cdb21 100644
--- a/common/partitions/parser.h
+++ b/common/partitions/parser.h
@@ -32,7 +32,8 @@ struct partition_desc {
};
struct partition_parser {
- void (*parse)(void *buf, struct block_device *blk, struct partition_desc *pd);
+ struct partition_desc *(*parse)(void *buf, struct block_device *blk);
+ void (*partition_free)(struct partition_desc *pd);
enum filetype type;
struct list_head list;
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 03/12] partition: allocate struct partition in parser
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
2024-02-19 8:31 ` [PATCH 01/12] partitions: dos: save indention level Sascha Hauer
2024-02-19 8:31 ` [PATCH 02/12] partition: allocate struct partition_desc in parser Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 04/12] partition: efi: keep raw data Sascha Hauer
` (9 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Allocate struct partition in the parser rather than in the core
partition code. Doing so allows the parser to embed struct partition
in an implementation specific struct type.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions.c | 12 ++++----
common/partitions/dos.c | 56 ++++++++++++++++++++++----------------
common/partitions/efi.c | 13 +++++++--
common/partitions/parser.h | 5 ++--
4 files changed, 51 insertions(+), 35 deletions(-)
diff --git a/common/partitions.c b/common/partitions.c
index 05ebde7ca3..0d8c2849ab 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -27,8 +27,7 @@ static LIST_HEAD(partition_parser_list);
* @param no Partition number
* @return 0 on success
*/
-static int register_one_partition(struct block_device *blk,
- struct partition *part, int no)
+static int register_one_partition(struct block_device *blk, struct partition *part)
{
char *partition_name;
int ret;
@@ -38,7 +37,7 @@ static int register_one_partition(struct block_device *blk,
.size = part->size * SECTOR_SIZE,
};
- partition_name = basprintf("%s.%d", blk->cdev.name, no);
+ partition_name = basprintf("%s.%d", blk->cdev.name, part->num);
if (!partition_name)
return -ENOMEM;
@@ -117,6 +116,7 @@ int parse_partition_table(struct block_device *blk)
int i;
int rc = 0;
struct partition_parser *parser;
+ struct partition *part;
uint8_t *buf;
buf = malloc(2 * SECTOR_SIZE);
@@ -135,12 +135,12 @@ int parse_partition_table(struct block_device *blk)
if (!pdesc)
goto on_error;
- if (!pdesc->used_entries)
+ if (list_empty(&pdesc->partitions))
goto on_error;
/* at least one partition description found */
- for (i = 0; i < pdesc->used_entries; i++) {
- rc = register_one_partition(blk, &pdesc->parts[i], i);
+ list_for_each_entry(part, &pdesc->partitions, list) {
+ rc = register_one_partition(blk, part);
if (rc != 0)
dev_err(blk->dev,
"Failed to register partition %d on %s (%d)\n",
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index 01a251201e..7ca3c4e37e 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -113,10 +113,10 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
uint32_t ebr_sector = partition->first_sec;
struct partition_entry *table = (struct partition_entry *)&buf[0x1be];
unsigned partno = 5;
+ struct partition *pentry;
- while (pd->used_entries < ARRAY_SIZE(pd->parts)) {
+ while (1) {
int rc, i;
- int n = pd->used_entries;
dev_dbg(blk->dev, "expect EBR in sector %x\n", ebr_sector);
@@ -139,15 +139,19 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
}
/* /sanity checks */
+ pentry = xzalloc(sizeof(*pentry));
+
/* the first entry defines the extended partition */
- pd->parts[n].first_sec = ebr_sector +
+ pentry->first_sec = ebr_sector +
get_unaligned_le32(&table[0].partition_start);
- pd->parts[n].size = get_unaligned_le32(&table[0].partition_size);
- pd->parts[n].dos_partition_type = table[0].type;
+ pentry->size = get_unaligned_le32(&table[0].partition_size);
+ pentry->dos_partition_type = table[0].type;
+ pentry->num = partno;
if (signature)
- sprintf(pd->parts[n].partuuid, "%08x-%02u",
+ sprintf(pentry->partuuid, "%08x-%02u",
signature, partno);
- pd->used_entries++;
+
+ list_add_tail(&pentry->list, &pd->partitions);
partno++;
/* the second entry defines the start of the next ebr if != 0 */
@@ -174,7 +178,7 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
{
struct partition_entry *table;
- struct partition pentry;
+ struct partition *pentry;
struct partition *extended_partition = NULL;
uint8_t *buffer = buf;
int i;
@@ -190,33 +194,30 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
table = (struct partition_entry *)&buffer[446];
pd = xzalloc(sizeof(*pd));
+ INIT_LIST_HEAD(&pd->partitions);
for (i = 0; i < 4; i++) {
- int n;
+ uint64_t first_sec = get_unaligned_le32(&table[i].partition_start);
- pentry.first_sec = get_unaligned_le32(&table[i].partition_start);
- pentry.size = get_unaligned_le32(&table[i].partition_size);
- pentry.dos_partition_type = table[i].type;
-
- if (pentry.first_sec == 0) {
+ if (first_sec == 0) {
dev_dbg(blk->dev, "Skipping empty partition %d\n", i);
continue;
}
- n = pd->used_entries;
- pd->parts[n].first_sec = pentry.first_sec;
- pd->parts[n].size = pentry.size;
- pd->parts[n].dos_partition_type = pentry.dos_partition_type;
+ pentry = xzalloc(sizeof(*pentry));
+
+ pentry->first_sec = first_sec;
+ pentry->size = get_unaligned_le32(&table[i].partition_size);
+ pentry->dos_partition_type = table[i].type;
+
if (signature)
- sprintf(pd->parts[n].partuuid, "%08x-%02d",
- signature, i + 1);
- pd->used_entries++;
+ sprintf(pentry->partuuid, "%08x-%02d", signature, i + 1);
- if (is_extended_partition(&pentry)) {
- pd->parts[n].size = 2;
+ if (is_extended_partition(pentry)) {
+ pentry->size = 2;
if (!extended_partition)
- extended_partition = &pd->parts[n];
+ extended_partition = pentry;
else
/*
* An DOS MBR must only contain a single
@@ -225,6 +226,8 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
*/
dev_warn(blk->dev, "Skipping additional extended partition\n");
}
+
+ list_add_tail(&pentry->list, &pd->partitions);
}
if (extended_partition)
@@ -252,6 +255,11 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
static void dos_partition_free(struct partition_desc *pd)
{
+ struct partition *part, *tmp;
+
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list)
+ free(part);
+
free(pd);
}
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index effe512949..5612f6261b 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -438,7 +438,7 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
int i = 0;
int nb_part;
struct partition *pentry;
- struct partition_desc *pd;
+ struct partition_desc *pd = NULL;
if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes)
goto out;
@@ -457,6 +457,7 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
}
pd = xzalloc(sizeof(*pd));
+ INIT_LIST_HEAD(&pd->partitions);
for (i = 0; i < nb_part; i++) {
if (!is_pte_valid(&ptes[i], last_lba(blk))) {
@@ -464,14 +465,15 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
continue;
}
- pentry = &pd->parts[pd->used_entries];
+ pentry = xzalloc(sizeof(*pentry));
pentry->first_sec = le64_to_cpu(ptes[i].starting_lba);
pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec;
pentry->size++;
part_set_efi_name(&ptes[i], pentry->name);
snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid);
pentry->typeuuid = ptes[i].partition_type_guid;
- pd->used_entries++;
+ pentry->num = i;
+ list_add_tail(&pentry->list, &pd->partitions);
}
out:
kfree(gpt);
@@ -482,6 +484,11 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
static void efi_partition_free(struct partition_desc *pd)
{
+ struct partition *part, *tmp;
+
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list)
+ free(part);
+
free(pd);
}
diff --git a/common/partitions/parser.h b/common/partitions/parser.h
index 3eec2cdb21..ab593109e6 100644
--- a/common/partitions/parser.h
+++ b/common/partitions/parser.h
@@ -24,11 +24,12 @@ struct partition {
u8 dos_partition_type;
guid_t typeuuid;
};
+ struct list_head list;
+ int num;
};
struct partition_desc {
- int used_entries;
- struct partition parts[MAX_PARTITION];
+ struct list_head partitions;
};
struct partition_parser {
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 04/12] partition: efi: keep raw data
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (2 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 03/12] partition: allocate struct partition " Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 05/12] uuid: implement random uuid/guid Sascha Hauer
` (8 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Keep raw data for later partition write support.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions/efi.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 5612f6261b..84d55e6491 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -22,6 +22,16 @@
#include <efi/partition.h>
#include "parser.h"
+struct efi_partition_desc {
+ struct partition_desc pd; /* must be first */
+ gpt_header gpt;
+};
+
+struct efi_partition {
+ struct partition part; /* must be first */
+ gpt_entry pte;
+};
+
static const int force_gpt = IS_ENABLED(CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE);
/**
@@ -437,8 +447,9 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
gpt_entry *ptes = NULL;
int i = 0;
int nb_part;
+ struct efi_partition *epart;
struct partition *pentry;
- struct partition_desc *pd = NULL;
+ struct efi_partition_desc *epd = NULL;
if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes)
goto out;
@@ -456,8 +467,9 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
nb_part = MAX_PARTITION;
}
- pd = xzalloc(sizeof(*pd));
- INIT_LIST_HEAD(&pd->partitions);
+ epd = xzalloc(sizeof(*epd));
+ INIT_LIST_HEAD(&epd->pd.partitions);
+ epd->gpt = *gpt;
for (i = 0; i < nb_part; i++) {
if (!is_pte_valid(&ptes[i], last_lba(blk))) {
@@ -465,7 +477,8 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
continue;
}
- pentry = xzalloc(sizeof(*pentry));
+ epart = xzalloc(sizeof(*epart));
+ pentry = &epart->part;
pentry->first_sec = le64_to_cpu(ptes[i].starting_lba);
pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec;
pentry->size++;
@@ -473,13 +486,13 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid);
pentry->typeuuid = ptes[i].partition_type_guid;
pentry->num = i;
- list_add_tail(&pentry->list, &pd->partitions);
+ list_add_tail(&pentry->list, &epd->pd.partitions);
}
out:
kfree(gpt);
kfree(ptes);
- return pd;
+ return &epd->pd;
}
static void efi_partition_free(struct partition_desc *pd)
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 05/12] uuid: implement random uuid/guid
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (3 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 04/12] partition: efi: keep raw data Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 06/12] linux/sizes.h: add more defines Sascha Hauer
` (7 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
lib/uuid.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/lib/uuid.c b/lib/uuid.c
index db75be1725..60a651e330 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -8,9 +8,39 @@
#include <linux/uuid.h>
#include <module.h>
+#include <stdlib.h>
#include <linux/export.h>
const guid_t guid_null;
EXPORT_SYMBOL(guid_null);
const uuid_t uuid_null;
EXPORT_SYMBOL(uuid_null);
+
+/**
+ * generate_random_uuid - generate a random UUID
+ * @uuid: where to put the generated UUID
+ *
+ * Random UUID interface
+ *
+ * Used to create a Boot ID or a filesystem UUID/GUID, but can be
+ * useful for other kernel drivers.
+ */
+void generate_random_uuid(unsigned char uuid[16])
+{
+ get_random_bytes(uuid, 16);
+ /* Set UUID version to 4 --- truly random generation */
+ uuid[6] = (uuid[6] & 0x0F) | 0x40;
+ /* Set the UUID variant to DCE */
+ uuid[8] = (uuid[8] & 0x3F) | 0x80;
+}
+EXPORT_SYMBOL(generate_random_uuid);
+
+void generate_random_guid(unsigned char guid[16])
+{
+ get_random_bytes(guid, 16);
+ /* Set GUID version to 4 --- truly random generation */
+ guid[7] = (guid[7] & 0x0F) | 0x40;
+ /* Set the GUID variant to DCE */
+ guid[8] = (guid[8] & 0x3F) | 0x80;
+}
+EXPORT_SYMBOL(generate_random_guid);
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 06/12] linux/sizes.h: add more defines
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (4 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 05/12] uuid: implement random uuid/guid Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 07/12] partition: add PARTITION_LINUX_DATA_GUID define Sascha Hauer
` (6 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Update linux/sizes.h from Linux to get Terabyte defines.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
include/linux/sizes.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/include/linux/sizes.h b/include/linux/sizes.h
index 1d222daeab..08fe344bc4 100644
--- a/include/linux/sizes.h
+++ b/include/linux/sizes.h
@@ -50,5 +50,17 @@
#define SZ_8G _AC(0x200000000, ULL)
#define SZ_16G _AC(0x400000000, ULL)
#define SZ_32G _AC(0x800000000, ULL)
+#define SZ_64G _AC(0x1000000000, ULL)
+#define SZ_128G _AC(0x2000000000, ULL)
+#define SZ_256G _AC(0x4000000000, ULL)
+#define SZ_512G _AC(0x8000000000, ULL)
+
+#define SZ_1T _AC(0x10000000000, ULL)
+#define SZ_2T _AC(0x20000000000, ULL)
+#define SZ_4T _AC(0x40000000000, ULL)
+#define SZ_8T _AC(0x80000000000, ULL)
+#define SZ_16T _AC(0x100000000000, ULL)
+#define SZ_32T _AC(0x200000000000, ULL)
+#define SZ_64T _AC(0x400000000000, ULL)
#endif /* __LINUX_SIZES_H__ */
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 07/12] partition: add PARTITION_LINUX_DATA_GUID define
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (5 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 06/12] linux/sizes.h: add more defines Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 08/12] partitions: move parser.h to include/partitions.h Sascha Hauer
` (5 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
include/efi/partition.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/efi/partition.h b/include/efi/partition.h
index 0ca2a72eb9..40ff4c557f 100644
--- a/include/efi/partition.h
+++ b/include/efi/partition.h
@@ -45,6 +45,9 @@
#define PARTITION_BASIC_DATA_GUID \
GUID_INIT( 0xEBD0A0A2, 0xB9E5, 0x4433, \
0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
+#define PARTITION_LINUX_DATA_GUID \
+ GUID_INIT( 0x0FC63DAF, 0x8483, 0x4772, \
+ 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4)
#define PARTITION_LINUX_RAID_GUID \
GUID_INIT( 0xa19d880f, 0x05fc, 0x4d3b, \
0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 08/12] partitions: move parser.h to include/partitions.h
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (6 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 07/12] partition: add PARTITION_LINUX_DATA_GUID define Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 09/12] partitions: implement partition manipulation support Sascha Hauer
` (4 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
This will be needed by upcoming partition manipulation code, so move it
to include/
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions.c | 3 +--
common/partitions/dos.c | 3 +--
common/partitions/efi.c | 2 +-
common/partitions/parser.h => include/partitions.h | 0
4 files changed, 3 insertions(+), 5 deletions(-)
rename common/partitions/parser.h => include/partitions.h (100%)
diff --git a/common/partitions.c b/common/partitions.c
index 0d8c2849ab..abbe31ed11 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -15,8 +15,7 @@
#include <disks.h>
#include <filetype.h>
#include <linux/err.h>
-
-#include "partitions/parser.h"
+#include <partitions.h>
static LIST_HEAD(partition_parser_list);
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index 7ca3c4e37e..e5ff59f71a 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -17,8 +17,7 @@
#include <init.h>
#include <asm/unaligned.h>
#include <linux/err.h>
-
-#include "parser.h"
+#include <partitions.h>
enum {
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 84d55e6491..407c1a4d08 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -20,7 +20,7 @@
#include <linux/ctype.h>
#include <efi/partition.h>
-#include "parser.h"
+#include <partitions.h>
struct efi_partition_desc {
struct partition_desc pd; /* must be first */
diff --git a/common/partitions/parser.h b/include/partitions.h
similarity index 100%
rename from common/partitions/parser.h
rename to include/partitions.h
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 09/12] partitions: implement partition manipulation support
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (7 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 08/12] partitions: move parser.h to include/partitions.h Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 8:31 ` [PATCH 10/12] partitions: dos: " Sascha Hauer
` (3 subsequent siblings)
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
This adds a function API to manipulate partition tables. Basically
partition_table_new() and partition_table_read() are factored out from
existing code. Apart from that we will have partition_create() to
create a new partition and partition_remove() to remove a partition.
These functions do basic sanity checks like partition overlap checking,
access beyond device cheks and call into not yet existing efi or msdos
specific function hooks.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/Kconfig | 3 +
common/partitions.c | 156 ++++++++++++++++++++++++++++++++++++-------
include/partitions.h | 22 ++++++
3 files changed, 157 insertions(+), 24 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig
index ddca1e913b..d16c8696da 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -841,6 +841,9 @@ config PARTITION
bool
prompt "Enable Partitions"
+config PARTITION_MANIPULATION
+ bool
+
source "common/partitions/Kconfig"
config ENV_HANDLING
diff --git a/common/partitions.c b/common/partitions.c
index abbe31ed11..7bcd0973b8 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -102,6 +102,133 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
return NULL;
}
+struct partition_desc *partition_table_new(struct block_device *blk, const char *type)
+{
+ struct partition_desc *pdesc;
+ struct partition_parser *parser;
+
+ list_for_each_entry(parser, &partition_parser_list, list) {
+ if (!strcmp(parser->name, type))
+ goto found;
+ }
+
+ pr_err("Cannot find partition parser \"%s\"\n", type);
+
+ return ERR_PTR(-ENOSYS);
+
+found:
+ pdesc = parser->create(blk);
+ if (IS_ERR(pdesc))
+ return ERR_CAST(pdesc);
+
+ pdesc->parser = parser;
+
+ return pdesc;
+}
+
+struct partition_desc *partition_table_read(struct block_device *blk)
+{
+ struct partition_parser *parser;
+ struct partition_desc *pdesc = NULL;
+ uint8_t *buf;
+ int ret;
+
+ buf = malloc(2 * SECTOR_SIZE);
+
+ ret = block_read(blk, buf, 0, 2);
+ if (ret != 0) {
+ dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(ret));
+ goto err;
+ }
+
+ parser = partition_parser_get_by_filetype(buf);
+ if (!parser)
+ goto err;
+
+ pdesc = parser->parse(buf, blk);
+ pdesc->parser = parser;
+err:
+ free(buf);
+
+ return pdesc;
+}
+
+int partition_table_write(struct partition_desc *pdesc)
+{
+ if (!pdesc->parser->write)
+ return -ENOSYS;
+
+ return pdesc->parser->write(pdesc);
+}
+
+static bool overlap(uint64_t s1, uint64_t e1, uint64_t s2, uint64_t e2)
+{
+ if (e1 < s2)
+ return false;
+ if (s1 > e2)
+ return false;
+ return true;
+}
+
+int partition_create(struct partition_desc *pdesc, const char *name,
+ const char *fs_type, uint64_t lba_start, uint64_t lba_end)
+{
+ struct partition *part;
+
+ if (!pdesc->parser->mkpart)
+ return -ENOSYS;
+
+ if (lba_end < lba_start) {
+ pr_err("lba_end < lba_start: %llu < %llu\n", lba_end, lba_start);
+ return -EINVAL;
+ }
+
+ if (lba_end >= pdesc->blk->num_blocks) {
+ pr_err("lba_end exceeds device: %llu >= %llu\n", lba_end, pdesc->blk->num_blocks);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(part, &pdesc->partitions, list) {
+ if (overlap(part->first_sec,
+ part->first_sec + part->size - 1,
+ lba_start, lba_end)) {
+ pr_err("new partition %llu-%llu overlaps with partition %s (%llu-%llu)\n",
+ lba_start, lba_end, part->name, part->first_sec,
+ part->first_sec + part->size - 1);
+ return -EINVAL;
+ }
+ }
+
+ return pdesc->parser->mkpart(pdesc, name, fs_type, lba_start, lba_end);
+}
+
+int partition_remove(struct partition_desc *pdesc, int num)
+{
+ struct partition *part;
+
+ if (!pdesc->parser->rmpart)
+ return -ENOSYS;
+
+ list_for_each_entry(part, &pdesc->partitions, list) {
+ if (part->num == num)
+ return pdesc->parser->rmpart(pdesc, part);
+ }
+
+ pr_err("Partition %d doesn't exist\n", num);
+ return -ENOENT;
+}
+
+void partition_table_free(struct partition_desc *pdesc)
+{
+ pdesc->parser->partition_free(pdesc);
+}
+
+void partition_desc_init(struct partition_desc *pd, struct block_device *blk)
+{
+ pd->blk = blk;
+ INIT_LIST_HEAD(&pd->partitions);
+}
+
/**
* Try to collect partition information on the given block device
* @param blk Block device to examine
@@ -111,31 +238,14 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
*/
int parse_partition_table(struct block_device *blk)
{
- struct partition_desc *pdesc = NULL;
int i;
int rc = 0;
- struct partition_parser *parser;
struct partition *part;
- uint8_t *buf;
-
- buf = malloc(2 * SECTOR_SIZE);
+ struct partition_desc *pdesc;
- rc = block_read(blk, buf, 0, 2);
- if (rc != 0) {
- dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(rc));
- goto on_error;
- }
-
- parser = partition_parser_get_by_filetype(buf);
- if (!parser)
- goto on_error;
-
- pdesc = parser->parse(buf, blk);
+ pdesc = partition_table_read(blk);
if (!pdesc)
- goto on_error;
-
- if (list_empty(&pdesc->partitions))
- goto on_error;
+ return 0;
/* at least one partition description found */
list_for_each_entry(part, &pdesc->partitions, list) {
@@ -148,10 +258,8 @@ int parse_partition_table(struct block_device *blk)
rc = 0;
}
-on_error:
- free(buf);
- if (pdesc)
- parser->partition_free(pdesc);
+ partition_table_free(pdesc);
+
return rc;
}
diff --git a/include/partitions.h b/include/partitions.h
index ab593109e6..b5379db92a 100644
--- a/include/partitions.h
+++ b/include/partitions.h
@@ -28,18 +28,40 @@ struct partition {
int num;
};
+struct partition_parser;
+
struct partition_desc {
struct list_head partitions;
+ struct partition_parser *parser;
+ struct block_device *blk;
};
struct partition_parser {
struct partition_desc *(*parse)(void *buf, struct block_device *blk);
void (*partition_free)(struct partition_desc *pd);
+ struct partition_desc *(*create)(struct block_device *blk);
+ int (*mkpart)(struct partition_desc *pd, const char *name, const char *fs_type,
+ uint64_t start, uint64_t end);
+ int (*rmpart)(struct partition_desc *pd, struct partition *part);
+ int (*write)(struct partition_desc *pd);
+ int (*rename)(struct partition *part, const char *name);
+ int (*setguid)(struct partition *part, guid_t *guid);
enum filetype type;
struct list_head list;
+
+ const char *name;
};
+void partition_desc_init(struct partition_desc *pd, struct block_device *blk);
int partition_parser_register(struct partition_parser *p);
+struct partition_desc *partition_table_read(struct block_device *blk);
+struct partition_desc *partition_table_new(struct block_device *blk, const char *type);
+int partition_table_write(struct partition_desc *pdesc);
+int partition_create(struct partition_desc *pdesc, const char *name,
+ const char *fs_type, uint64_t lba_start, uint64_t lba_end);
+int partition_remove(struct partition_desc *pdesc, int num);
+void partition_table_free(struct partition_desc *pdesc);
+
#endif /* __PARTITIONS_PARSER_H__ */
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 10/12] partitions: dos: implement partition manipulation support
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (8 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 09/12] partitions: implement partition manipulation support Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-28 17:37 ` Ahmad Fatoum
2024-02-19 8:31 ` [PATCH 11/12] partitions: efi: " Sascha Hauer
` (2 subsequent siblings)
12 siblings, 1 reply; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
This fills in the msdos specific function hooks to add/remove
partitions.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions/dos.c | 256 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 243 insertions(+), 13 deletions(-)
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index e5ff59f71a..9bb51bce85 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -15,10 +15,26 @@
#include <common.h>
#include <disks.h>
#include <init.h>
+#include <stdlib.h>
#include <asm/unaligned.h>
#include <linux/err.h>
#include <partitions.h>
+struct dos_partition_desc {
+ struct partition_desc pd;
+ uint32_t signature;
+};
+
+struct dos_partition {
+ struct partition part;
+ bool extended;
+ bool logical;
+
+ uint8_t boot_indicator;
+ uint8_t chs_begin[3];
+ uint8_t type;
+ uint8_t chs_end[3];
+};
enum {
/* These three have identical behaviour; use the second one if DOS FDISK gets
@@ -105,13 +121,14 @@ static int dos_get_disk_signature(struct param_d *p, void *_priv)
return 0;
}
-static void dos_extended_partition(struct block_device *blk, struct partition_desc *pd,
+static void dos_extended_partition(struct block_device *blk, struct dos_partition_desc *dpd,
struct partition *partition, uint32_t signature)
{
uint8_t *buf = malloc(SECTOR_SIZE);
uint32_t ebr_sector = partition->first_sec;
struct partition_entry *table = (struct partition_entry *)&buf[0x1be];
unsigned partno = 5;
+ struct dos_partition *dpart;
struct partition *pentry;
while (1) {
@@ -138,7 +155,14 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
}
/* /sanity checks */
- pentry = xzalloc(sizeof(*pentry));
+ dpart = xzalloc(sizeof(*dpart));
+ dpart->logical = true;
+ dpart->boot_indicator = table[0].boot_indicator;
+ memcpy(dpart->chs_begin, table[0].chs_begin, sizeof(table[0].chs_begin));
+ dpart->type = table[0].type;
+ memcpy(dpart->chs_end, table[0].chs_end, sizeof(table[0].chs_end));
+
+ pentry = &dpart->part;
/* the first entry defines the extended partition */
pentry->first_sec = ebr_sector +
@@ -150,7 +174,7 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
sprintf(pentry->partuuid, "%08x-%02u",
signature, partno);
- list_add_tail(&pentry->list, &pd->partitions);
+ list_add_tail(&pentry->list, &dpd->pd.partitions);
partno++;
/* the second entry defines the start of the next ebr if != 0 */
@@ -177,13 +201,14 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
{
struct partition_entry *table;
+ struct dos_partition *dpart;
struct partition *pentry;
struct partition *extended_partition = NULL;
uint8_t *buffer = buf;
int i;
struct disk_signature_priv *dsp;
uint32_t signature = get_unaligned_le32(buf + 0x1b8);
- struct partition_desc *pd;
+ struct dos_partition_desc *dpd;
if (signature)
sprintf(blk->cdev.diskuuid, "%08x", signature);
@@ -192,8 +217,8 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
table = (struct partition_entry *)&buffer[446];
- pd = xzalloc(sizeof(*pd));
- INIT_LIST_HEAD(&pd->partitions);
+ dpd = xzalloc(sizeof(*dpd));
+ partition_desc_init(&dpd->pd, blk);
for (i = 0; i < 4; i++) {
uint64_t first_sec = get_unaligned_le32(&table[i].partition_start);
@@ -203,16 +228,26 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
continue;
}
- pentry = xzalloc(sizeof(*pentry));
+ dpart = xzalloc(sizeof(*dpart));
+ dpart->boot_indicator = table[i].boot_indicator;
+ memcpy(dpart->chs_begin, table[i].chs_begin, sizeof(table[i].chs_begin));
+ dpart->type = table[i].type;
+ memcpy(dpart->chs_end, table[i].chs_end, sizeof(table[i].chs_end));
+
+ pentry = &dpart->part;
pentry->first_sec = first_sec;
pentry->size = get_unaligned_le32(&table[i].partition_size);
pentry->dos_partition_type = table[i].type;
+ pentry->num = i + 1;
- if (signature)
+ if (signature) {
sprintf(pentry->partuuid, "%08x-%02d", signature, i + 1);
+ dpd->signature = signature;
+ }
if (is_extended_partition(pentry)) {
+ dpart->extended = true;
pentry->size = 2;
if (!extended_partition)
@@ -226,11 +261,11 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
dev_warn(blk->dev, "Skipping additional extended partition\n");
}
- list_add_tail(&pentry->list, &pd->partitions);
+ list_add_tail(&pentry->list, &dpd->pd.partitions);
}
if (extended_partition)
- dos_extended_partition(blk, pd, extended_partition, signature);
+ dos_extended_partition(blk, dpd, extended_partition, signature);
dsp = xzalloc(sizeof(*dsp));
dsp->blk = blk;
@@ -249,23 +284,218 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
dos_set_disk_signature, dos_get_disk_signature,
&dsp->signature, "%08x", dsp);
- return pd;
+ return &dpd->pd;
}
static void dos_partition_free(struct partition_desc *pd)
{
struct partition *part, *tmp;
- list_for_each_entry_safe(part, tmp, &pd->partitions, list)
- free(part);
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
+ struct dos_partition *dpart = container_of(part, struct dos_partition, part);
+
+ free(dpart);
+ }
free(pd);
}
+static __maybe_unused struct partition_desc *dos_partition_create_table(struct block_device *blk)
+{
+ struct dos_partition_desc *dpd = xzalloc(512);
+
+ partition_desc_init(&dpd->pd, blk);
+
+ dpd->signature = random32();
+
+ return &dpd->pd;
+}
+
+static int fs_type_to_type(const char *fstype)
+{
+ unsigned long type;
+ int ret;
+
+ if (!strcmp(fstype, "ext2"))
+ return 0x83;
+ if (!strcmp(fstype, "ext3"))
+ return 0x83;
+ if (!strcmp(fstype, "ext4"))
+ return 0x83;
+ if (!strcmp(fstype, "fat16"))
+ return 0xe;
+ if (!strcmp(fstype, "fat32"))
+ return 0xc;
+
+ ret = kstrtoul(fstype, 16, &type);
+ if (ret)
+ return ret;
+
+ if (type > 0xff)
+ return -EINVAL;
+
+ return type;
+}
+
+static __maybe_unused int dos_partition_mkpart(struct partition_desc *pd,
+ const char *name, const char *fs_type,
+ uint64_t start_lba, uint64_t end_lba)
+{
+ struct dos_partition *dpart;
+ struct partition *part, *part_extended = NULL;
+ int npart = 0, npart_logical = 0;
+ uint64_t size;
+ int mask_free = 0xf;
+ int type = fs_type_to_type(fs_type);
+
+ if (type < 0) {
+ pr_err("invalid fs_type \"%s\"\n", fs_type);
+ return -EINVAL;
+ }
+
+ if (start_lba < 1) {
+ pr_err("invalid start LBA, minimum is 1\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+
+ mask_free &= ~(1 << npart);
+
+ if (dpart->extended)
+ part_extended = part;
+ if (dpart->logical)
+ npart_logical++;
+ else
+ npart++;
+ }
+
+ if (!strcmp(name, "extended")) {
+ if (part_extended) {
+ pr_err("extended partition already exists\n");
+ return -ENOSPC;
+ }
+ goto create;
+ } else if (!strcmp(name, "primary")) {
+ if (npart == 4) {
+ pr_err("Can't create any more partitions\n");
+ return -ENOSPC;
+ }
+
+ goto create;
+ } else if (!strcmp(name, "logical")) {
+ if (!part_extended) {
+ pr_err("No extended partition\n");
+ return -EINVAL;
+ }
+
+ if (npart_logical >= 4) {
+ pr_err("Can't create any more partitions\n");
+ return -ENOSPC;
+ }
+
+ pr_err("Can't create logical partitions yet\n");
+ return -EINVAL;
+ } else {
+ pr_err("Invalid name \"%s\"\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+
+create:
+ dpart = xzalloc(sizeof(*dpart));
+ part = &dpart->part;
+
+ if (start_lba > UINT_MAX)
+ return -ENOSPC;
+ size = end_lba - start_lba + 1;
+
+ if (size > UINT_MAX)
+ return -ENOSPC;
+
+ dpart->type = fs_type_to_type(fs_type);
+
+ part->first_sec = start_lba;
+ part->size = size;
+ part->num = ffs(mask_free);
+
+ list_add_tail(&part->list, &pd->partitions);
+
+ return 0;
+}
+
+static __maybe_unused int dos_partition_rmpart(struct partition_desc *pd, struct partition *part)
+{
+ struct dos_partition *dpart = container_of(part, struct dos_partition, part);
+
+ list_del(&part->list);
+ free(dpart);
+
+ return 0;
+}
+
+static __maybe_unused int dos_partition_write(struct partition_desc *pd)
+{
+ struct dos_partition_desc *dpd = container_of(pd, struct dos_partition_desc, pd);
+ struct dos_partition *dpart;
+ struct partition *part;
+ void *buf;
+ struct partition_entry *table, *entry;
+ int ret;
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+ if (dpart->logical) {
+ pr_err("Cannot write tables with logical partitions yet\n");
+ return -EINVAL;
+ }
+ }
+
+ buf = read_mbr(pd->blk);
+ if (!buf)
+ return -EIO;
+
+ put_unaligned_le32(dpd->signature, buf + 0x1b8);
+
+ table = buf + 0x1be;
+ memset(table, 0x0, sizeof(*table) * 4);
+
+ *(u8 *)(buf + 0x1fe) = 0x55;
+ *(u8 *)(buf + 0x1ff) = 0xaa;
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+
+ entry = &table[part->num - 1];
+
+ entry->boot_indicator = dpart->boot_indicator;
+ memcpy(entry->chs_begin, dpart->chs_begin, sizeof(entry->chs_begin));
+ entry->type = dpart->type;
+ memcpy(entry->chs_end, dpart->chs_end, sizeof(entry->chs_end));
+ put_unaligned_le32(part->first_sec, &entry->partition_start);
+ put_unaligned_le32(part->size, &entry->partition_size);
+ }
+
+ ret = block_write(pd->blk, buf, 0, 1);
+
+ free(buf);
+
+ return ret;
+}
+
static struct partition_parser dos = {
.parse = dos_partition,
.partition_free = dos_partition_free,
+#ifdef CONFIG_PARTITION_MANIPULATION
+ .create = dos_partition_create_table,
+ .mkpart = dos_partition_mkpart,
+ .rmpart = dos_partition_rmpart,
+ .write = dos_partition_write,
+#endif
.type = filetype_mbr,
+ .name = "msdos",
};
static int dos_partition_init(void)
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 10/12] partitions: dos: implement partition manipulation support
2024-02-19 8:31 ` [PATCH 10/12] partitions: dos: " Sascha Hauer
@ 2024-02-28 17:37 ` Ahmad Fatoum
2024-02-29 7:16 ` Sascha Hauer
0 siblings, 1 reply; 18+ messages in thread
From: Ahmad Fatoum @ 2024-02-28 17:37 UTC (permalink / raw)
To: Sascha Hauer, Barebox List
Hello Sascha,
On 19.02.24 09:31, Sascha Hauer wrote:
> - pentry = xzalloc(sizeof(*pentry));
> + dpart = xzalloc(sizeof(*dpart));
> + dpart->boot_indicator = table[i].boot_indicator;
> + memcpy(dpart->chs_begin, table[i].chs_begin, sizeof(table[i].chs_begin));
> + dpart->type = table[i].type;
> + memcpy(dpart->chs_end, table[i].chs_end, sizeof(table[i].chs_end));
> +
> + pentry = &dpart->part;
>
> pentry->first_sec = first_sec;
> pentry->size = get_unaligned_le32(&table[i].partition_size);
> pentry->dos_partition_type = table[i].type;
> + pentry->num = i + 1;
I suspect this breaks my boot script for MBR systems.
Please revert to zero-based numbering.
Thanks,
Ahmad
>
> - if (signature)
> + if (signature) {
> sprintf(pentry->partuuid, "%08x-%02d", signature, i + 1);
> + dpd->signature = signature;
> + }
>
> if (is_extended_partition(pentry)) {
> + dpart->extended = true;
> pentry->size = 2;
>
> if (!extended_partition)
> @@ -226,11 +261,11 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
> dev_warn(blk->dev, "Skipping additional extended partition\n");
> }
>
> - list_add_tail(&pentry->list, &pd->partitions);
> + list_add_tail(&pentry->list, &dpd->pd.partitions);
> }
>
> if (extended_partition)
> - dos_extended_partition(blk, pd, extended_partition, signature);
> + dos_extended_partition(blk, dpd, extended_partition, signature);
>
> dsp = xzalloc(sizeof(*dsp));
> dsp->blk = blk;
> @@ -249,23 +284,218 @@ static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
> dos_set_disk_signature, dos_get_disk_signature,
> &dsp->signature, "%08x", dsp);
>
> - return pd;
> + return &dpd->pd;
> }
>
> static void dos_partition_free(struct partition_desc *pd)
> {
> struct partition *part, *tmp;
>
> - list_for_each_entry_safe(part, tmp, &pd->partitions, list)
> - free(part);
> + list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
> + struct dos_partition *dpart = container_of(part, struct dos_partition, part);
> +
> + free(dpart);
> + }
>
> free(pd);
> }
>
> +static __maybe_unused struct partition_desc *dos_partition_create_table(struct block_device *blk)
> +{
> + struct dos_partition_desc *dpd = xzalloc(512);
> +
> + partition_desc_init(&dpd->pd, blk);
> +
> + dpd->signature = random32();
> +
> + return &dpd->pd;
> +}
> +
> +static int fs_type_to_type(const char *fstype)
> +{
> + unsigned long type;
> + int ret;
> +
> + if (!strcmp(fstype, "ext2"))
> + return 0x83;
> + if (!strcmp(fstype, "ext3"))
> + return 0x83;
> + if (!strcmp(fstype, "ext4"))
> + return 0x83;
> + if (!strcmp(fstype, "fat16"))
> + return 0xe;
> + if (!strcmp(fstype, "fat32"))
> + return 0xc;
> +
> + ret = kstrtoul(fstype, 16, &type);
> + if (ret)
> + return ret;
> +
> + if (type > 0xff)
> + return -EINVAL;
> +
> + return type;
> +}
> +
> +static __maybe_unused int dos_partition_mkpart(struct partition_desc *pd,
> + const char *name, const char *fs_type,
> + uint64_t start_lba, uint64_t end_lba)
> +{
> + struct dos_partition *dpart;
> + struct partition *part, *part_extended = NULL;
> + int npart = 0, npart_logical = 0;
> + uint64_t size;
> + int mask_free = 0xf;
> + int type = fs_type_to_type(fs_type);
> +
> + if (type < 0) {
> + pr_err("invalid fs_type \"%s\"\n", fs_type);
> + return -EINVAL;
> + }
> +
> + if (start_lba < 1) {
> + pr_err("invalid start LBA, minimum is 1\n");
> + return -EINVAL;
> + }
> +
> + list_for_each_entry(part, &pd->partitions, list) {
> + dpart = container_of(part, struct dos_partition, part);
> +
> + mask_free &= ~(1 << npart);
> +
> + if (dpart->extended)
> + part_extended = part;
> + if (dpart->logical)
> + npart_logical++;
> + else
> + npart++;
> + }
> +
> + if (!strcmp(name, "extended")) {
> + if (part_extended) {
> + pr_err("extended partition already exists\n");
> + return -ENOSPC;
> + }
> + goto create;
> + } else if (!strcmp(name, "primary")) {
> + if (npart == 4) {
> + pr_err("Can't create any more partitions\n");
> + return -ENOSPC;
> + }
> +
> + goto create;
> + } else if (!strcmp(name, "logical")) {
> + if (!part_extended) {
> + pr_err("No extended partition\n");
> + return -EINVAL;
> + }
> +
> + if (npart_logical >= 4) {
> + pr_err("Can't create any more partitions\n");
> + return -ENOSPC;
> + }
> +
> + pr_err("Can't create logical partitions yet\n");
> + return -EINVAL;
> + } else {
> + pr_err("Invalid name \"%s\"\n", name);
> + return -EINVAL;
> + }
> +
> + return 0;
> +
> +create:
> + dpart = xzalloc(sizeof(*dpart));
> + part = &dpart->part;
> +
> + if (start_lba > UINT_MAX)
> + return -ENOSPC;
> + size = end_lba - start_lba + 1;
> +
> + if (size > UINT_MAX)
> + return -ENOSPC;
> +
> + dpart->type = fs_type_to_type(fs_type);
> +
> + part->first_sec = start_lba;
> + part->size = size;
> + part->num = ffs(mask_free);
> +
> + list_add_tail(&part->list, &pd->partitions);
> +
> + return 0;
> +}
> +
> +static __maybe_unused int dos_partition_rmpart(struct partition_desc *pd, struct partition *part)
> +{
> + struct dos_partition *dpart = container_of(part, struct dos_partition, part);
> +
> + list_del(&part->list);
> + free(dpart);
> +
> + return 0;
> +}
> +
> +static __maybe_unused int dos_partition_write(struct partition_desc *pd)
> +{
> + struct dos_partition_desc *dpd = container_of(pd, struct dos_partition_desc, pd);
> + struct dos_partition *dpart;
> + struct partition *part;
> + void *buf;
> + struct partition_entry *table, *entry;
> + int ret;
> +
> + list_for_each_entry(part, &pd->partitions, list) {
> + dpart = container_of(part, struct dos_partition, part);
> + if (dpart->logical) {
> + pr_err("Cannot write tables with logical partitions yet\n");
> + return -EINVAL;
> + }
> + }
> +
> + buf = read_mbr(pd->blk);
> + if (!buf)
> + return -EIO;
> +
> + put_unaligned_le32(dpd->signature, buf + 0x1b8);
> +
> + table = buf + 0x1be;
> + memset(table, 0x0, sizeof(*table) * 4);
> +
> + *(u8 *)(buf + 0x1fe) = 0x55;
> + *(u8 *)(buf + 0x1ff) = 0xaa;
> +
> + list_for_each_entry(part, &pd->partitions, list) {
> + dpart = container_of(part, struct dos_partition, part);
> +
> + entry = &table[part->num - 1];
> +
> + entry->boot_indicator = dpart->boot_indicator;
> + memcpy(entry->chs_begin, dpart->chs_begin, sizeof(entry->chs_begin));
> + entry->type = dpart->type;
> + memcpy(entry->chs_end, dpart->chs_end, sizeof(entry->chs_end));
> + put_unaligned_le32(part->first_sec, &entry->partition_start);
> + put_unaligned_le32(part->size, &entry->partition_size);
> + }
> +
> + ret = block_write(pd->blk, buf, 0, 1);
> +
> + free(buf);
> +
> + return ret;
> +}
> +
> static struct partition_parser dos = {
> .parse = dos_partition,
> .partition_free = dos_partition_free,
> +#ifdef CONFIG_PARTITION_MANIPULATION
> + .create = dos_partition_create_table,
> + .mkpart = dos_partition_mkpart,
> + .rmpart = dos_partition_rmpart,
> + .write = dos_partition_write,
> +#endif
> .type = filetype_mbr,
> + .name = "msdos",
> };
>
> static int dos_partition_init(void)
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 10/12] partitions: dos: implement partition manipulation support
2024-02-28 17:37 ` Ahmad Fatoum
@ 2024-02-29 7:16 ` Sascha Hauer
0 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-29 7:16 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: Barebox List
On Wed, Feb 28, 2024 at 06:37:45PM +0100, Ahmad Fatoum wrote:
> Hello Sascha,
>
> On 19.02.24 09:31, Sascha Hauer wrote:
> > - pentry = xzalloc(sizeof(*pentry));
> > + dpart = xzalloc(sizeof(*dpart));
> > + dpart->boot_indicator = table[i].boot_indicator;
> > + memcpy(dpart->chs_begin, table[i].chs_begin, sizeof(table[i].chs_begin));
> > + dpart->type = table[i].type;
> > + memcpy(dpart->chs_end, table[i].chs_end, sizeof(table[i].chs_end));
> > +
> > + pentry = &dpart->part;
> >
> > pentry->first_sec = first_sec;
> > pentry->size = get_unaligned_le32(&table[i].partition_size);
> > pentry->dos_partition_type = table[i].type;
> > + pentry->num = i + 1;
>
> I suspect this breaks my boot script for MBR systems.
> Please revert to zero-based numbering.
Err, right. This wasn't really done on purpose. I just wanted the
numbering of parted consistent to the Linux output and haven't thought
about the implication on the numbering barebox uses internally.
I've sent patches to revert it back. This means parted now has a
different numbering than its Linux pendant, but I think this is still
better than having parted in barebox start at 1 while barebox internally
uses 0.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 11/12] partitions: efi: implement partition manipulation support
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (9 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 10/12] partitions: dos: " Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-28 17:36 ` Ahmad Fatoum
2024-02-19 8:31 ` [PATCH 12/12] commands: add parted Sascha Hauer
2024-02-20 10:47 ` [PATCH 00/12] Partition table manipulation support Sascha Hauer
12 siblings, 1 reply; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
This fills in the partition manipulation function hooks to add/remove
efi (GPT) partitions and to write a GPT partition table. This depends on
te msdos partition writing support for generating a protective MBR.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/partitions/Kconfig | 1 +
common/partitions/efi.c | 277 ++++++++++++++++++++++++++++++++++++--
2 files changed, 265 insertions(+), 13 deletions(-)
diff --git a/common/partitions/Kconfig b/common/partitions/Kconfig
index 7f12383082..3bbcceedc1 100644
--- a/common/partitions/Kconfig
+++ b/common/partitions/Kconfig
@@ -19,6 +19,7 @@ config PARTITION_DISK_EFI
depends on PARTITION_DISK
select CRC32
select PRINTF_UUID
+ select PARTITION_DISK_DOS if PARTITION_MANIPULATION
bool "EFI: GPT partition support"
help
Add support to handle partitions in GUID Partition Table style.
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 407c1a4d08..67d4978244 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -23,13 +23,14 @@
#include <partitions.h>
struct efi_partition_desc {
- struct partition_desc pd; /* must be first */
- gpt_header gpt;
+ struct partition_desc pd;
+ gpt_header *gpt;
+ gpt_entry *ptes;
};
struct efi_partition {
- struct partition part; /* must be first */
- gpt_entry pte;
+ struct partition part;
+ gpt_entry *pte;
};
static const int force_gpt = IS_ENABLED(CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE);
@@ -90,6 +91,7 @@ static gpt_entry *alloc_read_gpt_entries(struct block_device *blk,
if (!count)
return NULL;
+
pte = kzalloc(count, GFP_KERNEL);
if (!pte)
return NULL;
@@ -216,7 +218,8 @@ static int is_gpt_valid(struct block_device *blk, u64 lba,
le32_to_cpu((*gpt)->sizeof_partition_entry));
if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
- dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed.\n");
+ dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed: 0x%08x 0x%08x\n",
+ crc, le32_to_cpu((*gpt)->partition_entry_array_crc32));
goto fail_ptes;
}
@@ -441,6 +444,19 @@ static void part_set_efi_name(gpt_entry *pte, char *dest)
dest[i] = 0;
}
+static void part_get_efi_name(gpt_entry *pte, const char *src)
+{
+ int i;
+ int len = strlen(src);
+
+ for (i = 0; i < GPT_PARTNAME_MAX_SIZE ; i++) {
+ if (i <= len)
+ pte->partition_name[i] = src[i];
+ else
+ pte->partition_name[i] = 0;
+ }
+}
+
static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
{
gpt_header *gpt = NULL;
@@ -468,8 +484,10 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
}
epd = xzalloc(sizeof(*epd));
- INIT_LIST_HEAD(&epd->pd.partitions);
- epd->gpt = *gpt;
+ partition_desc_init(&epd->pd, blk);
+
+ epd->gpt = gpt;
+ epd->ptes = ptes;
for (i = 0; i < nb_part; i++) {
if (!is_pte_valid(&ptes[i], last_lba(blk))) {
@@ -478,6 +496,7 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
}
epart = xzalloc(sizeof(*epart));
+ epart->pte = &ptes[i];
pentry = &epart->part;
pentry->first_sec = le64_to_cpu(ptes[i].starting_lba);
pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec;
@@ -485,30 +504,262 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
part_set_efi_name(&ptes[i], pentry->name);
snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid);
pentry->typeuuid = ptes[i].partition_type_guid;
- pentry->num = i;
+ pentry->num = i + 1;
list_add_tail(&pentry->list, &epd->pd.partitions);
}
out:
- kfree(gpt);
- kfree(ptes);
return &epd->pd;
}
static void efi_partition_free(struct partition_desc *pd)
{
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
struct partition *part, *tmp;
- list_for_each_entry_safe(part, tmp, &pd->partitions, list)
- free(part);
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
+ struct efi_partition *epart = container_of(part, struct efi_partition, part);
+
+ free(epart);
+ }
+
+ free(epd->ptes);
+ free(epd->gpt);
+ free(epd);
+}
+
+static __maybe_unused struct partition_desc *efi_partition_create_table(struct block_device *blk)
+{
+ struct efi_partition_desc *epd = xzalloc(sizeof(*epd));
+ gpt_header *gpt;
+
+ partition_desc_init(&epd->pd, blk);
+
+ epd->gpt = xzalloc(512);
+ gpt = epd->gpt;
+
+ gpt->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+ gpt->revision = cpu_to_le32(0x100);
+ gpt->header_size = cpu_to_le32(sizeof(*gpt));
+ gpt->my_lba = cpu_to_le64(1);
+ gpt->alternate_lba = cpu_to_le64(last_lba(blk));
+ gpt->first_usable_lba = cpu_to_le64(34);
+ gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - 34);;
+ generate_random_guid((unsigned char *)&gpt->disk_guid);
+ gpt->partition_entry_lba = cpu_to_le64(2);
+ gpt->num_partition_entries = cpu_to_le32(128);
+ gpt->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry));
+
+ pr_info("Created new disk label with GUID %pU\n", &gpt->disk_guid);
+
+ epd->ptes = xzalloc(128 * sizeof(gpt_entry));
+
+ return &epd->pd;
+}
+
+static guid_t partition_linux_data_guid = PARTITION_LINUX_DATA_GUID;
+static guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
+
+static const guid_t *fs_type_to_guid(const char *fstype)
+{
+ if (!strcmp(fstype, "ext2"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "ext3"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "ext4"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "fat16"))
+ return &partition_basic_data_guid;
+ if (!strcmp(fstype, "fat32"))
+ return &partition_basic_data_guid;
+
+ return NULL;
+}
+
+static __maybe_unused int efi_partition_mkpart(struct partition_desc *pd,
+ const char *name, const char *fs_type,
+ uint64_t start_lba, uint64_t end_lba)
+{
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
+ struct efi_partition *epart;
+ struct partition *part;
+ gpt_header *gpt = epd->gpt;
+ int num_parts = le32_to_cpu(gpt->num_partition_entries);
+ gpt_entry *pte;
+ int i;
+ const guid_t *guid;
+
+ if (start_lba < 34) {
+ pr_err("invalid start LBA %lld, minimum is 34\n", start_lba);
+ return -EINVAL;
+ }
+
+ if (end_lba >= last_lba(pd->blk) - 33) {
+ pr_err("invalid end LBA %lld, maximum is %lld\n", start_lba,
+ last_lba(pd->blk) - 33);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_parts; i++) {
+ if (!is_pte_valid(&epd->ptes[i], last_lba(pd->blk)))
+ break;
+ }
+
+ if (i == num_parts) {
+ pr_err("partition table is full\n");
+ return -ENOSPC;
+ }
+
+ guid = fs_type_to_guid(fs_type);
+ if (!guid) {
+ pr_err("Unknown fs type %s\n", fs_type);
+ return -EINVAL;
+ }
+
+ pte = &epd->ptes[i];
+ epart = xzalloc(sizeof(*epart));
+ part = &epart->part;
+
+ part->first_sec = start_lba;
+ part->size = end_lba - start_lba + 1;
+ part->typeuuid = *guid;
+
+ pte->partition_type_guid = *guid;
+ generate_random_guid((unsigned char *)&pte->unique_partition_guid);
+ pte->starting_lba = cpu_to_le64(start_lba);
+ pte->ending_lba = cpu_to_le64(end_lba);
+ part_get_efi_name(pte, name);
+ part_set_efi_name(pte, part->name);
+ part->num = i;
+
+ list_add_tail(&part->list, &pd->partitions);
+
+ return 0;
+}
+
+static __maybe_unused int efi_partition_rmpart(struct partition_desc *pd, struct partition *part)
+{
+ struct efi_partition *epart = container_of(part, struct efi_partition, part);
+
+ memset(epart->pte, 0, sizeof(*epart->pte));
+
+ list_del(&part->list);
+ free(epart);
+
+ return 0;
+}
+
+static int efi_protective_mbr(struct block_device *blk)
+{
+ struct partition_desc *pdesc;
+ int ret;
+
+ pdesc = partition_table_new(blk, "msdos");
+ if (IS_ERR(pdesc)) {
+ printf("Error: Cannot create partition table: %pe\n", pdesc);
+ return PTR_ERR(pdesc);
+ }
+
+ ret = partition_create(pdesc, "primary", "0xee", 1, last_lba(blk));
+ if (ret) {
+ pr_err("Cannot create partition: %pe\n", ERR_PTR(ret));
+ goto out;
+ }
+
+ ret = partition_table_write(pdesc);
+ if (ret) {
+ pr_err("Cannot write partition: %pe\n", ERR_PTR(ret));
+ goto out;
+ }
+out:
+ partition_table_free(pdesc);
+
+ return ret;
+}
+
+static __maybe_unused int efi_partition_write(struct partition_desc *pd)
+{
+ struct block_device *blk = pd->blk;
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
+ gpt_header *gpt = epd->gpt, *altgpt;
+ int ret;
+ uint32_t count;
+ uint64_t from, size;
+
+ if (le32_to_cpu(gpt->num_partition_entries) != 128) {
+ /*
+ * This is not yet properly implemented. At least writing of the
+ * alternative GPT is not correctly implemented for this case as
+ * we can't assume that the partition entries are written at
+ * last_lba() - 32, we would have to calculate that from the number
+ * of partition entries.
+ */
+ pr_err("num_partition_entries is != 128. This is not yet supported for writing\n");
+ return -EINVAL;
+ }
+
+ count = le32_to_cpu(gpt->num_partition_entries) *
+ le32_to_cpu(gpt->sizeof_partition_entry);
+
+ gpt->my_lba = cpu_to_le64(1);
+ gpt->partition_entry_array_crc32 = cpu_to_le32(efi_crc32(
+ (const unsigned char *)epd->ptes, count));
+ gpt->header_crc32 = 0;
+ gpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)gpt,
+ le32_to_cpu(gpt->header_size)));
+
+ ret = efi_protective_mbr(blk);
+ if (ret)
+ return ret;
+
+ ret = block_write(blk, gpt, 1, 1);
+ if (ret)
+ goto err_block_write;
+
+ from = le64_to_cpu(gpt->partition_entry_lba);
+ size = count / GPT_BLOCK_SIZE;
+
+ ret = block_write(blk, epd->ptes, from, size);
+ if (ret)
+ goto err_block_write;
+
+ altgpt = xmemdup(gpt, SECTOR_SIZE);
+
+ altgpt->alternate_lba = cpu_to_le64(1);
+ altgpt->my_lba = cpu_to_le64(last_lba(blk));
+ altgpt->partition_entry_lba = cpu_to_le64(last_lba(blk) - 32);
+ altgpt->header_crc32 = 0;
+ altgpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)altgpt,
+ le32_to_cpu(altgpt->header_size)));
+ ret = block_write(blk, altgpt, last_lba(blk), 1);
+
+ free(altgpt);
+
+ if (ret)
+ goto err_block_write;
+ ret = block_write(blk, epd->ptes, last_lba(blk) - 32, 32);
+ if (ret)
+ goto err_block_write;
+
+ return 0;
+
+err_block_write:
+ pr_err("Cannot write to block device: %pe\n", ERR_PTR(ret));
- free(pd);
+ return ret;
}
static struct partition_parser efi_partition_parser = {
.parse = efi_partition,
.partition_free = efi_partition_free,
+#ifdef CONFIG_PARTITION_MANIPULATION
+ .create = efi_partition_create_table,
+ .mkpart = efi_partition_mkpart,
+ .rmpart = efi_partition_rmpart,
+ .write = efi_partition_write,
+#endif
.type = filetype_gpt,
+ .name = "gpt",
};
static int efi_partition_init(void)
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 11/12] partitions: efi: implement partition manipulation support
2024-02-19 8:31 ` [PATCH 11/12] partitions: efi: " Sascha Hauer
@ 2024-02-28 17:36 ` Ahmad Fatoum
0 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2024-02-28 17:36 UTC (permalink / raw)
To: Sascha Hauer, Barebox List
Hello Sascha,
On 19.02.24 09:31, Sascha Hauer wrote:
> epart = xzalloc(sizeof(*epart));
> + epart->pte = &ptes[i];
> pentry = &epart->part;
> pentry->first_sec = le64_to_cpu(ptes[i].starting_lba);
> pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec;
> @@ -485,30 +504,262 @@ static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
> part_set_efi_name(&ptes[i], pentry->name);
> snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid);
> pentry->typeuuid = ptes[i].partition_type_guid;
> - pentry->num = i;
> + pentry->num = i + 1;
This breaks my boot scripts. For better or worse, barebox partitions were always
zero-indexed. This switches them to 1-indexed.
Thanks,
Ahmad
> list_add_tail(&pentry->list, &epd->pd.partitions);
> }
> out:
> - kfree(gpt);
> - kfree(ptes);
>
> return &epd->pd;
> }
>
> static void efi_partition_free(struct partition_desc *pd)
> {
> + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
> struct partition *part, *tmp;
>
> - list_for_each_entry_safe(part, tmp, &pd->partitions, list)
> - free(part);
> + list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
> + struct efi_partition *epart = container_of(part, struct efi_partition, part);
> +
> + free(epart);
> + }
> +
> + free(epd->ptes);
> + free(epd->gpt);
> + free(epd);
> +}
> +
> +static __maybe_unused struct partition_desc *efi_partition_create_table(struct block_device *blk)
> +{
> + struct efi_partition_desc *epd = xzalloc(sizeof(*epd));
> + gpt_header *gpt;
> +
> + partition_desc_init(&epd->pd, blk);
> +
> + epd->gpt = xzalloc(512);
> + gpt = epd->gpt;
> +
> + gpt->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
> + gpt->revision = cpu_to_le32(0x100);
> + gpt->header_size = cpu_to_le32(sizeof(*gpt));
> + gpt->my_lba = cpu_to_le64(1);
> + gpt->alternate_lba = cpu_to_le64(last_lba(blk));
> + gpt->first_usable_lba = cpu_to_le64(34);
> + gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - 34);;
> + generate_random_guid((unsigned char *)&gpt->disk_guid);
> + gpt->partition_entry_lba = cpu_to_le64(2);
> + gpt->num_partition_entries = cpu_to_le32(128);
> + gpt->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry));
> +
> + pr_info("Created new disk label with GUID %pU\n", &gpt->disk_guid);
> +
> + epd->ptes = xzalloc(128 * sizeof(gpt_entry));
> +
> + return &epd->pd;
> +}
> +
> +static guid_t partition_linux_data_guid = PARTITION_LINUX_DATA_GUID;
> +static guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
> +
> +static const guid_t *fs_type_to_guid(const char *fstype)
> +{
> + if (!strcmp(fstype, "ext2"))
> + return &partition_linux_data_guid;
> + if (!strcmp(fstype, "ext3"))
> + return &partition_linux_data_guid;
> + if (!strcmp(fstype, "ext4"))
> + return &partition_linux_data_guid;
> + if (!strcmp(fstype, "fat16"))
> + return &partition_basic_data_guid;
> + if (!strcmp(fstype, "fat32"))
> + return &partition_basic_data_guid;
> +
> + return NULL;
> +}
> +
> +static __maybe_unused int efi_partition_mkpart(struct partition_desc *pd,
> + const char *name, const char *fs_type,
> + uint64_t start_lba, uint64_t end_lba)
> +{
> + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
> + struct efi_partition *epart;
> + struct partition *part;
> + gpt_header *gpt = epd->gpt;
> + int num_parts = le32_to_cpu(gpt->num_partition_entries);
> + gpt_entry *pte;
> + int i;
> + const guid_t *guid;
> +
> + if (start_lba < 34) {
> + pr_err("invalid start LBA %lld, minimum is 34\n", start_lba);
> + return -EINVAL;
> + }
> +
> + if (end_lba >= last_lba(pd->blk) - 33) {
> + pr_err("invalid end LBA %lld, maximum is %lld\n", start_lba,
> + last_lba(pd->blk) - 33);
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < num_parts; i++) {
> + if (!is_pte_valid(&epd->ptes[i], last_lba(pd->blk)))
> + break;
> + }
> +
> + if (i == num_parts) {
> + pr_err("partition table is full\n");
> + return -ENOSPC;
> + }
> +
> + guid = fs_type_to_guid(fs_type);
> + if (!guid) {
> + pr_err("Unknown fs type %s\n", fs_type);
> + return -EINVAL;
> + }
> +
> + pte = &epd->ptes[i];
> + epart = xzalloc(sizeof(*epart));
> + part = &epart->part;
> +
> + part->first_sec = start_lba;
> + part->size = end_lba - start_lba + 1;
> + part->typeuuid = *guid;
> +
> + pte->partition_type_guid = *guid;
> + generate_random_guid((unsigned char *)&pte->unique_partition_guid);
> + pte->starting_lba = cpu_to_le64(start_lba);
> + pte->ending_lba = cpu_to_le64(end_lba);
> + part_get_efi_name(pte, name);
> + part_set_efi_name(pte, part->name);
> + part->num = i;
> +
> + list_add_tail(&part->list, &pd->partitions);
> +
> + return 0;
> +}
> +
> +static __maybe_unused int efi_partition_rmpart(struct partition_desc *pd, struct partition *part)
> +{
> + struct efi_partition *epart = container_of(part, struct efi_partition, part);
> +
> + memset(epart->pte, 0, sizeof(*epart->pte));
> +
> + list_del(&part->list);
> + free(epart);
> +
> + return 0;
> +}
> +
> +static int efi_protective_mbr(struct block_device *blk)
> +{
> + struct partition_desc *pdesc;
> + int ret;
> +
> + pdesc = partition_table_new(blk, "msdos");
> + if (IS_ERR(pdesc)) {
> + printf("Error: Cannot create partition table: %pe\n", pdesc);
> + return PTR_ERR(pdesc);
> + }
> +
> + ret = partition_create(pdesc, "primary", "0xee", 1, last_lba(blk));
> + if (ret) {
> + pr_err("Cannot create partition: %pe\n", ERR_PTR(ret));
> + goto out;
> + }
> +
> + ret = partition_table_write(pdesc);
> + if (ret) {
> + pr_err("Cannot write partition: %pe\n", ERR_PTR(ret));
> + goto out;
> + }
> +out:
> + partition_table_free(pdesc);
> +
> + return ret;
> +}
> +
> +static __maybe_unused int efi_partition_write(struct partition_desc *pd)
> +{
> + struct block_device *blk = pd->blk;
> + struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
> + gpt_header *gpt = epd->gpt, *altgpt;
> + int ret;
> + uint32_t count;
> + uint64_t from, size;
> +
> + if (le32_to_cpu(gpt->num_partition_entries) != 128) {
> + /*
> + * This is not yet properly implemented. At least writing of the
> + * alternative GPT is not correctly implemented for this case as
> + * we can't assume that the partition entries are written at
> + * last_lba() - 32, we would have to calculate that from the number
> + * of partition entries.
> + */
> + pr_err("num_partition_entries is != 128. This is not yet supported for writing\n");
> + return -EINVAL;
> + }
> +
> + count = le32_to_cpu(gpt->num_partition_entries) *
> + le32_to_cpu(gpt->sizeof_partition_entry);
> +
> + gpt->my_lba = cpu_to_le64(1);
> + gpt->partition_entry_array_crc32 = cpu_to_le32(efi_crc32(
> + (const unsigned char *)epd->ptes, count));
> + gpt->header_crc32 = 0;
> + gpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)gpt,
> + le32_to_cpu(gpt->header_size)));
> +
> + ret = efi_protective_mbr(blk);
> + if (ret)
> + return ret;
> +
> + ret = block_write(blk, gpt, 1, 1);
> + if (ret)
> + goto err_block_write;
> +
> + from = le64_to_cpu(gpt->partition_entry_lba);
> + size = count / GPT_BLOCK_SIZE;
> +
> + ret = block_write(blk, epd->ptes, from, size);
> + if (ret)
> + goto err_block_write;
> +
> + altgpt = xmemdup(gpt, SECTOR_SIZE);
> +
> + altgpt->alternate_lba = cpu_to_le64(1);
> + altgpt->my_lba = cpu_to_le64(last_lba(blk));
> + altgpt->partition_entry_lba = cpu_to_le64(last_lba(blk) - 32);
> + altgpt->header_crc32 = 0;
> + altgpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)altgpt,
> + le32_to_cpu(altgpt->header_size)));
> + ret = block_write(blk, altgpt, last_lba(blk), 1);
> +
> + free(altgpt);
> +
> + if (ret)
> + goto err_block_write;
> + ret = block_write(blk, epd->ptes, last_lba(blk) - 32, 32);
> + if (ret)
> + goto err_block_write;
> +
> + return 0;
> +
> +err_block_write:
> + pr_err("Cannot write to block device: %pe\n", ERR_PTR(ret));
>
> - free(pd);
> + return ret;
> }
>
> static struct partition_parser efi_partition_parser = {
> .parse = efi_partition,
> .partition_free = efi_partition_free,
> +#ifdef CONFIG_PARTITION_MANIPULATION
> + .create = efi_partition_create_table,
> + .mkpart = efi_partition_mkpart,
> + .rmpart = efi_partition_rmpart,
> + .write = efi_partition_write,
> +#endif
> .type = filetype_gpt,
> + .name = "gpt",
> };
>
> static int efi_partition_init(void)
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 12/12] commands: add parted
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (10 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 11/12] partitions: efi: " Sascha Hauer
@ 2024-02-19 8:31 ` Sascha Hauer
2024-02-19 9:38 ` Ulrich Ölmann
2024-02-20 10:47 ` [PATCH 00/12] Partition table manipulation support Sascha Hauer
12 siblings, 1 reply; 18+ messages in thread
From: Sascha Hauer @ 2024-02-19 8:31 UTC (permalink / raw)
To: Barebox List
This adds a parted command which behaves pretty much like the GNU parted
program. Unlike other partition manipulation programs parted has a quite
convenient command line API suitable for scripting. The tool supports
these commands:
print - print a partition table
mklabel - create a new partition table
rm - remove a partition
mkpart - create a partition
unit - change input/display units
refresh - refresh a partition table (barebox specific)
Multiple commands can be given on a single call so that a full partition
table including partitions can be created with a single command.
Examples include:
Print a partition table:
$ parted mmc0 print
create a new partition table:
$ parted mmc0 mklabel gpt
create a new partition table and add a partition beginning at offset
1MiB ending at offset 128MiB:
$ parted mmc0 mklabel gpt mkpart rootfs ext4 1MiB 128MiB
The same, using KiB as unit and printing the result at the end:
$ parted mmc0 unit KiB mklabel gpt mkpart rootfs ext4 1024 131072 print
The "refresh" command is barebox specific and is useful when for example
the alternate GPT is missing. This happens when an image is written.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
commands/Kconfig | 21 +++
commands/Makefile | 2 +-
commands/parted.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 396 insertions(+), 1 deletion(-)
create mode 100644 commands/parted.c
diff --git a/commands/Kconfig b/commands/Kconfig
index a6806f198e..819fb80411 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -655,6 +655,27 @@ config CMD_MOUNT
-o OPTIONS set file system OPTIONS
-v verbose
+config CMD_PARTED
+ tristate
+ depends on PARTITION
+ select PARTITION_MANIPULATION
+ prompt "parted"
+ help
+ parted - edit partition tables
+
+ Usage: parted <device> [command [options...]...]
+
+ parted is a partition manipulation program with a behaviour similar to
+ GNU Parted
+
+ commands:
+ print print partitions
+ mklabel <type> create a new partition table
+ rm <num> remove a partition
+ mkpart <name> <fstype> <start> <end> create a new partition
+ unit <unit> change display/input units
+ refresh refresh a partition table
+
config CMD_UBI
tristate
default y if MTD_UBI
diff --git a/commands/Makefile b/commands/Makefile
index 4924755500..b311410276 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -146,5 +146,5 @@ obj-$(CONFIG_CMD_UBSAN) += ubsan.o
obj-$(CONFIG_CMD_SELFTEST) += selftest.o
obj-$(CONFIG_CMD_TUTORIAL) += tutorial.o
obj-$(CONFIG_CMD_STACKSMASH) += stacksmash.o
-
+obj-$(CONFIG_CMD_PARTED) += parted.o
UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/parted.c b/commands/parted.c
new file mode 100644
index 0000000000..02bb1cff0c
--- /dev/null
+++ b/commands/parted.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <common.h>
+#include <command.h>
+#include <block.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <disks.h>
+#include <linux/sizes.h>
+#include <partitions.h>
+#include <linux/math64.h>
+
+static struct partition_desc *gpdesc;
+static bool table_needs_write;
+static const char *gunit_str = "KiB";
+static uint64_t gunit = 1024;
+
+struct unit {
+ const char *str;
+ uint64_t size;
+};
+
+static struct unit units[] = {
+ { .str = "B", .size = 1 },
+ { .str = "s", .size = 512 },
+ { .str = "KiB", .size = SZ_1K },
+ { .str = "MiB", .size = SZ_1M },
+ { .str = "GiB", .size = SZ_1G },
+ { .str = "TiB", .size = SZ_1T },
+ { .str = "KB", .size = 1000ULL },
+ { .str = "MB", .size = 1000ULL * 1000 },
+ { .str = "GB", .size = 1000ULL * 1000 * 1000 },
+ { .str = "TB", .size = 1000ULL * 1000 * 1000 * 1000 },
+ { .str = "k", .size = SZ_1K },
+ { .str = "K", .size = SZ_1K },
+ { .str = "M", .size = SZ_1M },
+ { .str = "G", .size = SZ_1G },
+};
+
+static int parted_strtoull(const char *str, uint64_t *val, uint64_t *mult)
+{
+ char *end;
+ int i;
+
+ *val = simple_strtoull(str, &end, 0);
+
+ if (!*end) {
+ *mult = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(units); i++) {
+ if (!strcmp(end, units[i].str)) {
+ *mult = units[i].size;
+ return 0;
+ }
+ }
+
+ printf("Error: Cannot read \"%s\" as number\n", str);
+
+ return -EINVAL;
+}
+
+static struct partition_desc *pdesc_get(struct block_device *blk)
+{
+ if (gpdesc)
+ return gpdesc;
+
+ gpdesc = partition_table_read(blk);
+ if (!gpdesc) {
+ printf("Cannot read partition table\n");
+ return NULL;
+ }
+
+ return gpdesc;
+}
+
+static int do_unit(struct block_device *blk, int argc, char *argv[])
+{
+ int i;
+
+ if (argc < 2) {
+ printf("Error: missing unit\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(units); i++) {
+ if (!strcmp(units[i].str, argv[1])) {
+ gunit_str = units[i].str;
+ gunit = units[i].size;
+ return 2;
+ }
+ }
+
+ printf("invalid unit: %s\n", argv[1]);
+
+ return -EINVAL;
+}
+
+static int do_print(struct block_device *blk, int argc, char *argv[])
+{
+ struct partition_desc *pdesc;
+ struct partition *part;
+
+ pdesc = pdesc_get(blk);
+ if (!pdesc) {
+ printf("Error: Cannot get partition table from %s\n", blk->cdev.name);
+ return -EINVAL;
+ }
+
+ printf("Disk /dev/%s: %s\n", blk->cdev.name,
+ size_human_readable(blk->num_blocks << SECTOR_SHIFT));
+ printf("Partition Table: %s\n", pdesc->parser->name);
+
+ printf("Number Start End Size Name\n");
+
+ list_for_each_entry(part, &pdesc->partitions, list) {
+ uint64_t start = part->first_sec << SECTOR_SHIFT;
+ uint64_t size = part->size << SECTOR_SHIFT;
+ uint64_t end = start + size - SECTOR_SIZE;
+
+ printf(" %3d %10llu%-3s %10llu%-3s %10llu%-3s %-36s\n",
+ part->num,
+ div64_u64(start, gunit), gunit_str,
+ div64_u64(end, gunit), gunit_str,
+ div64_u64(size, gunit), gunit_str,
+ part->name);
+ }
+
+ return 1;
+}
+
+static int do_mkpart(struct block_device *blk, int argc, char *argv[])
+{
+ struct partition_desc *pdesc;
+ uint64_t start, end;
+ const char *name, *fs_type;
+ int ret;
+ uint64_t mult;
+
+ if (argc < 5) {
+ printf("Error: Missing required arguments\n");
+ return -EINVAL;
+ }
+
+ name = argv[1];
+ fs_type = argv[2];
+
+ ret = parted_strtoull(argv[3], &start, &mult);
+ if (ret)
+ return ret;
+
+ ret = parted_strtoull(argv[4], &end, &mult);
+ if (ret)
+ return ret;
+
+ if (!mult)
+ mult = gunit;
+
+ start *= mult;
+ end *= mult;
+
+ /* If not on sector boundaries move start up and end down */
+ start = ALIGN(start, SECTOR_SIZE);
+ end = ALIGN_DOWN(end, SECTOR_SIZE);
+
+ /* convert to LBA */
+ start >>= SECTOR_SHIFT;
+ end >>= SECTOR_SHIFT;
+
+ /*
+ * When unit is >= KB then substract one sector for user convenience.
+ * It allows to start the next partition where the previous ends
+ */
+ if (mult >= 1000)
+ end -= 1;
+
+ pdesc = pdesc_get(blk);
+ if (!pdesc)
+ return -EINVAL;
+
+ ret = partition_create(pdesc, name, fs_type, start, end);
+
+ if (!ret)
+ table_needs_write = true;
+
+ return ret < 0 ? ret : 5;
+}
+
+static int do_rmpart(struct block_device *blk, int argc, char *argv[])
+{
+ struct partition_desc *pdesc;
+ unsigned long num;
+ int ret;
+
+ if (argc < 2) {
+ printf("Error: Expecting a partition number.\n");
+ return -EINVAL;
+ }
+
+ ret = kstrtoul(argv[1], 0, &num);
+ if (ret)
+ return ret;
+
+ pdesc = pdesc_get(blk);
+ if (!pdesc)
+ return -EINVAL;
+
+ ret = partition_remove(pdesc, num);
+ if (ret)
+ return ret;
+
+ table_needs_write = true;
+
+ return 2;
+}
+
+static int do_mklabel(struct block_device *blk, int argc, char *argv[])
+{
+ struct partition_desc *pdesc;
+
+ if (argc < 2) {
+ printf("Error: Expecting a disk label type.\n");
+ return -EINVAL;
+ }
+
+ pdesc = partition_table_new(blk, argv[1]);
+ if (IS_ERR(pdesc)) {
+ printf("Error: Cannot create partition table: %pe\n", pdesc);
+ return PTR_ERR(pdesc);
+ }
+
+ table_needs_write = true;
+
+ if (gpdesc)
+ partition_table_free(gpdesc);
+ gpdesc = pdesc;
+
+ return 2;
+}
+
+static int do_refresh(struct block_device *blk, int argc, char *argv[])
+{
+ struct partition_desc *pdesc;
+
+ pdesc = pdesc_get(blk);
+ if (!pdesc)
+ return -EINVAL;
+
+ table_needs_write = true;
+
+ return 1;
+}
+
+struct parted_command {
+ const char *name;
+ int (*command)(struct block_device *blk, int argc, char *argv[]);
+};
+
+struct parted_command parted_commands[] = {
+ {
+ .name = "mkpart",
+ .command = do_mkpart,
+ }, {
+ .name = "print",
+ .command = do_print,
+ }, {
+ .name = "rm",
+ .command = do_rmpart,
+ }, {
+ .name = "mklabel",
+ .command = do_mklabel,
+ }, {
+ .name = "unit",
+ .command = do_unit,
+ }, {
+ .name = "refresh",
+ .command = do_refresh,
+ },
+};
+
+static int parted_run_command(struct block_device *blk, int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(parted_commands); i++) {
+ struct parted_command *cmd = &parted_commands[i];
+
+ if (!strcmp(argv[0], cmd->name))
+ return cmd->command(blk, argc, argv);
+ }
+
+ printf("No such command: %s\n", argv[0]);
+
+ return COMMAND_ERROR;
+}
+
+static int do_parted(int argc, char *argv[])
+{
+ struct cdev *cdev;
+ struct block_device *blk;
+ int ret = 0;
+
+ table_needs_write = false;
+ gpdesc = NULL;
+
+ if (argc < 3)
+ return COMMAND_ERROR_USAGE;
+
+ cdev = cdev_open_by_name(argv[1], O_RDWR);
+ if (!cdev) {
+ printf("Cannot open %s\n", argv[1]);
+ return COMMAND_ERROR;
+ }
+
+ blk = cdev_get_block_device(cdev);
+ if (!blk) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ argc -= 2;
+ argv += 2;
+
+ while (argc) {
+ debug("---> run command %s\n", argv[0]);
+ ret = parted_run_command(blk, argc, argv);
+ if (ret < 0)
+ break;
+
+ argc -= ret;
+ argv += ret;
+
+ ret = 0;
+ }
+
+ if (!ret && gpdesc && table_needs_write)
+ ret = partition_table_write(gpdesc);
+
+err:
+ if (gpdesc)
+ partition_table_free(gpdesc);
+
+ cdev_close(cdev);
+
+ return ret;
+}
+
+BAREBOX_CMD_HELP_START(parted)
+BAREBOX_CMD_HELP_TEXT("parted is a partition manipulation program with a behaviour similar to")
+BAREBOX_CMD_HELP_TEXT("GNU Parted")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("commands:")
+BAREBOX_CMD_HELP_OPT ("print", "print partitions")
+BAREBOX_CMD_HELP_OPT ("mklabel <type>", "create a new partition table")
+BAREBOX_CMD_HELP_OPT ("rm <num>", "remove a partition")
+BAREBOX_CMD_HELP_OPT ("mkpart <name> <fstype> <start> <end>", "create a new partition")
+BAREBOX_CMD_HELP_OPT ("unit <unit>", "change display/input units")
+BAREBOX_CMD_HELP_OPT ("refresh", "refresh a partition table")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("<unit> can be one of \"s\" (sectors), \"B\" (bytes), \"kB\", \"MB\", \"GB\", \"TB\",")
+BAREBOX_CMD_HELP_TEXT("\"KiB\", \"MiB\", \"GiB\" or \"TiB\"")
+BAREBOX_CMD_HELP_TEXT("<type> must be \"gpt\"")
+BAREBOX_CMD_HELP_TEXT("<fstype> can be one of \"ext2\", \"ext3\", \"ext4\", \"fat16\" or \"fat32\"")
+BAREBOX_CMD_HELP_TEXT("<name> for MBR partition tables can be one of \"primary\", \"extended\" or")
+BAREBOX_CMD_HELP_TEXT("\"logical\". For GPT this is a name string.")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(parted)
+ .cmd = do_parted,
+ BAREBOX_CMD_DESC("edit partition tables")
+ BAREBOX_CMD_OPTS("<device> [command [options...]...]")
+ BAREBOX_CMD_GROUP(CMD_GRP_FILE)
+ BAREBOX_CMD_HELP(cmd_parted_help)
+BAREBOX_CMD_END
--
2.39.2
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 12/12] commands: add parted
2024-02-19 8:31 ` [PATCH 12/12] commands: add parted Sascha Hauer
@ 2024-02-19 9:38 ` Ulrich Ölmann
0 siblings, 0 replies; 18+ messages in thread
From: Ulrich Ölmann @ 2024-02-19 9:38 UTC (permalink / raw)
To: Sascha Hauer; +Cc: barebox
Hi Sascha,
On Mon, Feb 19 2024 at 09:31 +0100, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> This adds a parted command which behaves pretty much like the GNU parted
> program. Unlike other partition manipulation programs parted has a quite
> convenient command line API suitable for scripting. The tool supports
> these commands:
>
> print - print a partition table
> mklabel - create a new partition table
> rm - remove a partition
> mkpart - create a partition
> unit - change input/display units
> refresh - refresh a partition table (barebox specific)
>
> Multiple commands can be given on a single call so that a full partition
> table including partitions can be created with a single command.
> Examples include:
>
> Print a partition table:
>
> $ parted mmc0 print
>
> create a new partition table:
>
> $ parted mmc0 mklabel gpt
>
> create a new partition table and add a partition beginning at offset
> 1MiB ending at offset 128MiB:
>
> $ parted mmc0 mklabel gpt mkpart rootfs ext4 1MiB 128MiB
>
> The same, using KiB as unit and printing the result at the end:
>
> $ parted mmc0 unit KiB mklabel gpt mkpart rootfs ext4 1024 131072 print
>
> The "refresh" command is barebox specific and is useful when for example
> the alternate GPT is missing. This happens when an image is written.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> commands/Kconfig | 21 +++
> commands/Makefile | 2 +-
> commands/parted.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 396 insertions(+), 1 deletion(-)
> create mode 100644 commands/parted.c
>
> diff --git a/commands/Kconfig b/commands/Kconfig
> index a6806f198e..819fb80411 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -655,6 +655,27 @@ config CMD_MOUNT
> -o OPTIONS set file system OPTIONS
> -v verbose
>
> +config CMD_PARTED
> + tristate
> + depends on PARTITION
> + select PARTITION_MANIPULATION
> + prompt "parted"
> + help
> + parted - edit partition tables
> +
> + Usage: parted <device> [command [options...]...]
> +
> + parted is a partition manipulation program with a behaviour similar to
> + GNU Parted
> +
> + commands:
> + print print partitions
> + mklabel <type> create a new partition table
> + rm <num> remove a partition
> + mkpart <name> <fstype> <start> <end> create a new partition
> + unit <unit> change display/input units
> + refresh refresh a partition table
just a small nitpick that the last line's alignment could be enhanced:
s/refresh refresh a partition table/refresh refresh a partition table/
Best regards
Ulrich
> +
> config CMD_UBI
> tristate
> default y if MTD_UBI
> diff --git a/commands/Makefile b/commands/Makefile
> index 4924755500..b311410276 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -146,5 +146,5 @@ obj-$(CONFIG_CMD_UBSAN) += ubsan.o
> obj-$(CONFIG_CMD_SELFTEST) += selftest.o
> obj-$(CONFIG_CMD_TUTORIAL) += tutorial.o
> obj-$(CONFIG_CMD_STACKSMASH) += stacksmash.o
> -
> +obj-$(CONFIG_CMD_PARTED) += parted.o
> UBSAN_SANITIZE_ubsan.o := y
> diff --git a/commands/parted.c b/commands/parted.c
> new file mode 100644
> index 0000000000..02bb1cff0c
> --- /dev/null
> +++ b/commands/parted.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <common.h>
> +#include <command.h>
> +#include <block.h>
> +#include <getopt.h>
> +#include <fcntl.h>
> +#include <disks.h>
> +#include <linux/sizes.h>
> +#include <partitions.h>
> +#include <linux/math64.h>
> +
> +static struct partition_desc *gpdesc;
> +static bool table_needs_write;
> +static const char *gunit_str = "KiB";
> +static uint64_t gunit = 1024;
> +
> +struct unit {
> + const char *str;
> + uint64_t size;
> +};
> +
> +static struct unit units[] = {
> + { .str = "B", .size = 1 },
> + { .str = "s", .size = 512 },
> + { .str = "KiB", .size = SZ_1K },
> + { .str = "MiB", .size = SZ_1M },
> + { .str = "GiB", .size = SZ_1G },
> + { .str = "TiB", .size = SZ_1T },
> + { .str = "KB", .size = 1000ULL },
> + { .str = "MB", .size = 1000ULL * 1000 },
> + { .str = "GB", .size = 1000ULL * 1000 * 1000 },
> + { .str = "TB", .size = 1000ULL * 1000 * 1000 * 1000 },
> + { .str = "k", .size = SZ_1K },
> + { .str = "K", .size = SZ_1K },
> + { .str = "M", .size = SZ_1M },
> + { .str = "G", .size = SZ_1G },
> +};
> +
> +static int parted_strtoull(const char *str, uint64_t *val, uint64_t *mult)
> +{
> + char *end;
> + int i;
> +
> + *val = simple_strtoull(str, &end, 0);
> +
> + if (!*end) {
> + *mult = 0;
> + return 0;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(units); i++) {
> + if (!strcmp(end, units[i].str)) {
> + *mult = units[i].size;
> + return 0;
> + }
> + }
> +
> + printf("Error: Cannot read \"%s\" as number\n", str);
> +
> + return -EINVAL;
> +}
> +
> +static struct partition_desc *pdesc_get(struct block_device *blk)
> +{
> + if (gpdesc)
> + return gpdesc;
> +
> + gpdesc = partition_table_read(blk);
> + if (!gpdesc) {
> + printf("Cannot read partition table\n");
> + return NULL;
> + }
> +
> + return gpdesc;
> +}
> +
> +static int do_unit(struct block_device *blk, int argc, char *argv[])
> +{
> + int i;
> +
> + if (argc < 2) {
> + printf("Error: missing unit\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(units); i++) {
> + if (!strcmp(units[i].str, argv[1])) {
> + gunit_str = units[i].str;
> + gunit = units[i].size;
> + return 2;
> + }
> + }
> +
> + printf("invalid unit: %s\n", argv[1]);
> +
> + return -EINVAL;
> +}
> +
> +static int do_print(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + struct partition *part;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc) {
> + printf("Error: Cannot get partition table from %s\n", blk->cdev.name);
> + return -EINVAL;
> + }
> +
> + printf("Disk /dev/%s: %s\n", blk->cdev.name,
> + size_human_readable(blk->num_blocks << SECTOR_SHIFT));
> + printf("Partition Table: %s\n", pdesc->parser->name);
> +
> + printf("Number Start End Size Name\n");
> +
> + list_for_each_entry(part, &pdesc->partitions, list) {
> + uint64_t start = part->first_sec << SECTOR_SHIFT;
> + uint64_t size = part->size << SECTOR_SHIFT;
> + uint64_t end = start + size - SECTOR_SIZE;
> +
> + printf(" %3d %10llu%-3s %10llu%-3s %10llu%-3s %-36s\n",
> + part->num,
> + div64_u64(start, gunit), gunit_str,
> + div64_u64(end, gunit), gunit_str,
> + div64_u64(size, gunit), gunit_str,
> + part->name);
> + }
> +
> + return 1;
> +}
> +
> +static int do_mkpart(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + uint64_t start, end;
> + const char *name, *fs_type;
> + int ret;
> + uint64_t mult;
> +
> + if (argc < 5) {
> + printf("Error: Missing required arguments\n");
> + return -EINVAL;
> + }
> +
> + name = argv[1];
> + fs_type = argv[2];
> +
> + ret = parted_strtoull(argv[3], &start, &mult);
> + if (ret)
> + return ret;
> +
> + ret = parted_strtoull(argv[4], &end, &mult);
> + if (ret)
> + return ret;
> +
> + if (!mult)
> + mult = gunit;
> +
> + start *= mult;
> + end *= mult;
> +
> + /* If not on sector boundaries move start up and end down */
> + start = ALIGN(start, SECTOR_SIZE);
> + end = ALIGN_DOWN(end, SECTOR_SIZE);
> +
> + /* convert to LBA */
> + start >>= SECTOR_SHIFT;
> + end >>= SECTOR_SHIFT;
> +
> + /*
> + * When unit is >= KB then substract one sector for user convenience.
> + * It allows to start the next partition where the previous ends
> + */
> + if (mult >= 1000)
> + end -= 1;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + ret = partition_create(pdesc, name, fs_type, start, end);
> +
> + if (!ret)
> + table_needs_write = true;
> +
> + return ret < 0 ? ret : 5;
> +}
> +
> +static int do_rmpart(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + unsigned long num;
> + int ret;
> +
> + if (argc < 2) {
> + printf("Error: Expecting a partition number.\n");
> + return -EINVAL;
> + }
> +
> + ret = kstrtoul(argv[1], 0, &num);
> + if (ret)
> + return ret;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + ret = partition_remove(pdesc, num);
> + if (ret)
> + return ret;
> +
> + table_needs_write = true;
> +
> + return 2;
> +}
> +
> +static int do_mklabel(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> +
> + if (argc < 2) {
> + printf("Error: Expecting a disk label type.\n");
> + return -EINVAL;
> + }
> +
> + pdesc = partition_table_new(blk, argv[1]);
> + if (IS_ERR(pdesc)) {
> + printf("Error: Cannot create partition table: %pe\n", pdesc);
> + return PTR_ERR(pdesc);
> + }
> +
> + table_needs_write = true;
> +
> + if (gpdesc)
> + partition_table_free(gpdesc);
> + gpdesc = pdesc;
> +
> + return 2;
> +}
> +
> +static int do_refresh(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + table_needs_write = true;
> +
> + return 1;
> +}
> +
> +struct parted_command {
> + const char *name;
> + int (*command)(struct block_device *blk, int argc, char *argv[]);
> +};
> +
> +struct parted_command parted_commands[] = {
> + {
> + .name = "mkpart",
> + .command = do_mkpart,
> + }, {
> + .name = "print",
> + .command = do_print,
> + }, {
> + .name = "rm",
> + .command = do_rmpart,
> + }, {
> + .name = "mklabel",
> + .command = do_mklabel,
> + }, {
> + .name = "unit",
> + .command = do_unit,
> + }, {
> + .name = "refresh",
> + .command = do_refresh,
> + },
> +};
> +
> +static int parted_run_command(struct block_device *blk, int argc, char *argv[])
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(parted_commands); i++) {
> + struct parted_command *cmd = &parted_commands[i];
> +
> + if (!strcmp(argv[0], cmd->name))
> + return cmd->command(blk, argc, argv);
> + }
> +
> + printf("No such command: %s\n", argv[0]);
> +
> + return COMMAND_ERROR;
> +}
> +
> +static int do_parted(int argc, char *argv[])
> +{
> + struct cdev *cdev;
> + struct block_device *blk;
> + int ret = 0;
> +
> + table_needs_write = false;
> + gpdesc = NULL;
> +
> + if (argc < 3)
> + return COMMAND_ERROR_USAGE;
> +
> + cdev = cdev_open_by_name(argv[1], O_RDWR);
> + if (!cdev) {
> + printf("Cannot open %s\n", argv[1]);
> + return COMMAND_ERROR;
> + }
> +
> + blk = cdev_get_block_device(cdev);
> + if (!blk) {
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + argc -= 2;
> + argv += 2;
> +
> + while (argc) {
> + debug("---> run command %s\n", argv[0]);
> + ret = parted_run_command(blk, argc, argv);
> + if (ret < 0)
> + break;
> +
> + argc -= ret;
> + argv += ret;
> +
> + ret = 0;
> + }
> +
> + if (!ret && gpdesc && table_needs_write)
> + ret = partition_table_write(gpdesc);
> +
> +err:
> + if (gpdesc)
> + partition_table_free(gpdesc);
> +
> + cdev_close(cdev);
> +
> + return ret;
> +}
> +
> +BAREBOX_CMD_HELP_START(parted)
> +BAREBOX_CMD_HELP_TEXT("parted is a partition manipulation program with a behaviour similar to")
> +BAREBOX_CMD_HELP_TEXT("GNU Parted")
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("commands:")
> +BAREBOX_CMD_HELP_OPT ("print", "print partitions")
> +BAREBOX_CMD_HELP_OPT ("mklabel <type>", "create a new partition table")
> +BAREBOX_CMD_HELP_OPT ("rm <num>", "remove a partition")
> +BAREBOX_CMD_HELP_OPT ("mkpart <name> <fstype> <start> <end>", "create a new partition")
> +BAREBOX_CMD_HELP_OPT ("unit <unit>", "change display/input units")
> +BAREBOX_CMD_HELP_OPT ("refresh", "refresh a partition table")
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("<unit> can be one of \"s\" (sectors), \"B\" (bytes), \"kB\", \"MB\", \"GB\", \"TB\",")
> +BAREBOX_CMD_HELP_TEXT("\"KiB\", \"MiB\", \"GiB\" or \"TiB\"")
> +BAREBOX_CMD_HELP_TEXT("<type> must be \"gpt\"")
> +BAREBOX_CMD_HELP_TEXT("<fstype> can be one of \"ext2\", \"ext3\", \"ext4\", \"fat16\" or \"fat32\"")
> +BAREBOX_CMD_HELP_TEXT("<name> for MBR partition tables can be one of \"primary\", \"extended\" or")
> +BAREBOX_CMD_HELP_TEXT("\"logical\". For GPT this is a name string.")
> +BAREBOX_CMD_HELP_END
> +
> +BAREBOX_CMD_START(parted)
> + .cmd = do_parted,
> + BAREBOX_CMD_DESC("edit partition tables")
> + BAREBOX_CMD_OPTS("<device> [command [options...]...]")
> + BAREBOX_CMD_GROUP(CMD_GRP_FILE)
> + BAREBOX_CMD_HELP(cmd_parted_help)
> +BAREBOX_CMD_END
--
Pengutronix e.K. | Ulrich Ölmann |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 00/12] Partition table manipulation support
2024-02-19 8:31 [PATCH 00/12] Partition table manipulation support Sascha Hauer
` (11 preceding siblings ...)
2024-02-19 8:31 ` [PATCH 12/12] commands: add parted Sascha Hauer
@ 2024-02-20 10:47 ` Sascha Hauer
12 siblings, 0 replies; 18+ messages in thread
From: Sascha Hauer @ 2024-02-20 10:47 UTC (permalink / raw)
To: Barebox List, Sascha Hauer
On Mon, 19 Feb 2024 09:31:28 +0100, Sascha Hauer wrote:
> This series adds support for changing partition tables. There is both a
> C API and a new command to create partition tables, to create partitions
> or to delete partitions. Both MSDOS and GPT tables are supported,
> although MSDOS currently lacks support for logical partitions which is
> left as a future exercise.
>
> The partition manipulation command mimics GNU Parted which has a quite
> convenient API for use in scripts. Some examples for the command are:
>
> [...]
Applied, thanks!
[01/12] partitions: dos: save indention level
https://git.pengutronix.de/cgit/barebox/commit/?id=4accc68d2d51 (link may not be stable)
[02/12] partition: allocate struct partition_desc in parser
https://git.pengutronix.de/cgit/barebox/commit/?id=1018f6c694f0 (link may not be stable)
[03/12] partition: allocate struct partition in parser
https://git.pengutronix.de/cgit/barebox/commit/?id=57392a862d40 (link may not be stable)
[04/12] partition: efi: keep raw data
https://git.pengutronix.de/cgit/barebox/commit/?id=081561b017dd (link may not be stable)
[05/12] uuid: implement random uuid/guid
https://git.pengutronix.de/cgit/barebox/commit/?id=1428b9259ed5 (link may not be stable)
[06/12] linux/sizes.h: add more defines
https://git.pengutronix.de/cgit/barebox/commit/?id=9946237b7152 (link may not be stable)
[07/12] partition: add PARTITION_LINUX_DATA_GUID define
https://git.pengutronix.de/cgit/barebox/commit/?id=834131b9bae1 (link may not be stable)
[08/12] partitions: move parser.h to include/partitions.h
https://git.pengutronix.de/cgit/barebox/commit/?id=5843759d80f7 (link may not be stable)
[09/12] partitions: implement partition manipulation support
https://git.pengutronix.de/cgit/barebox/commit/?id=914f24316014 (link may not be stable)
[10/12] partitions: dos: implement partition manipulation support
https://git.pengutronix.de/cgit/barebox/commit/?id=8f48e6366cbb (link may not be stable)
[11/12] partitions: efi: implement partition manipulation support
https://git.pengutronix.de/cgit/barebox/commit/?id=bdc3cb54dc80 (link may not be stable)
[12/12] commands: add parted
https://git.pengutronix.de/cgit/barebox/commit/?id=7dd66e63d887 (link may not be stable)
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 18+ messages in thread