From patchwork Thu Jun 10 21:05:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 1490649 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com (client-ip=2607:f8b0:4864:20::439; helo=mail-pf1-x439.google.com; envelope-from=swupdate+bncbcl4hcw73qcbbhh5rgdamgqebuzff5y@googlegroups.com; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.a=rsa-sha256 header.s=20161025 header.b=NeswuYdh; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=kvbXId2L; dkim-atps=neutral Received: from mail-pf1-x439.google.com (mail-pf1-x439.google.com [IPv6:2607:f8b0:4864:20::439]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4G1Ghv3mCwz9s1l for ; Fri, 11 Jun 2021 07:05:35 +1000 (AEST) Received: by mail-pf1-x439.google.com with SMTP id l3-20020a056a001403b02902ea25d3c7d4sf1923300pfu.15 for ; Thu, 10 Jun 2021 14:05:35 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1623359132; cv=pass; d=google.com; s=arc-20160816; b=PrSDBQefmEZChQ0XhXoo+UrEBeyCv2atP3OwYRwX2XiZvVGgYm5nppmvIPCAKwmNDb ee0SAGq/C487XMlRWpra1KjvRuZw7tNG1hY9Mn0avr1zDROSKP+TzWrsD8XT+P33Db5z M8m3dVgIhNwcPI8b75PKx1qJIAxkQr9RZjWV1lTkA/IVgByqxxkHNhkZIUisMLtMbGJh 5tMDHo9NJn8KrfjX6ka93DBeMVU5DeMVtzhYlMmk9AlczRFqJbrZMwAgX/jQGuNJVD6N 4oibfdSlGZkVUeZEbsbo056IXJ/nZiCZ/Lg7yku/gPa3A72X4RztKMZYV5b/HCMeyZUn a4Pw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:mime-version:message-id:date :subject:cc:to:from:sender:dkim-signature:dkim-signature; bh=mwWJTAG+Lm+HaPX3fyGHShRnrGUQ/2Rrs187TT4cA7c=; b=o0Cxdnc1TOegA/6cePvKn2jn0Obk6ju5MnMxqJy733AK36fxRryxAFaLgl1sS15cuu JmbzLywTr2A7PerVoLaUxO3I+ae8jZNfLwhDerSS3HoVtIt4knqsf0O9JUGMeDuJLeMQ puGb+RXxkfZ/5qBZ2EUaeh3PQq15+chgM/lSuBDewLKsK58lw/2TyTRXrhXyEeQdZ1zt CYyIutMJTa6py1p9bOUaCafQ9ypDtaRN5SfgSdnQvj2agKBmG879PzcwNnLMCatyxXXg MuAIuydwLjC59pnh1dF7bEaix7Cfk++w0uGOw1kPh5KX4eUnO58JrOZaG6fiBssKdGpv E2jA== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ZLVmum39; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::22c as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:mime-version :x-original-sender:x-original-authentication-results:precedence :mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=mwWJTAG+Lm+HaPX3fyGHShRnrGUQ/2Rrs187TT4cA7c=; b=NeswuYdhpUiep7PgTUQj95A19y8Gp3iFo7tEZikuOr5f3UMAwzK0HVtwtcDNumPzzt hLJ1Aiw7O2KfR5epMKwFoxrdUaH60rXIdDbOyWwfYN0Q5ifx5h4VvLFKAehY+vxZPngX BcONNGdSTL4cLSa6wThO4ZQHmYyReGFF45BlK6nFYFp+5xEyQKFjlTc8+Wo5NMJmNCSa I5QVTG1kU1pOaVIBI7XC7FVcY0Ex5XKWKS0vHZ8KXIzJdHSGugWDos3D4YWoIe5rOGC+ amDFNtEF+CbkclDFfT+LlF0ShJPlCKfdKlJd49CDmsy4ivVg9ilBRPKMVOUiX1S8cF04 uc+w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-subscribe:list-unsubscribe; bh=mwWJTAG+Lm+HaPX3fyGHShRnrGUQ/2Rrs187TT4cA7c=; b=kvbXId2LrJyRM6wsMucwhF6JblCUAwKBW6PTPdLa6AtxtQjhLw8WjSEdWWVgrXlEYX MYMS3SVCsNTItZDG33urY0U0ADYNLDfZawrXNdJ0LijpblfTvzeOKYD/dCF6HxLy3+X2 +XkOAzkNvXVF6C311QkZTDjwQ3d9pEwmVBBJbc5haeCuHQ8/uKdaHNDGpSl508KpjcCr BSHrXpluZsC5JiB0KjrCGjYDZUeG4BwwxQJWmuBpCdrTT8XNQvNK91P66t3ZuJX1JLXb FC5PjzevcDE/e0qVhIxozOjyOdxrUi4EpCPQRcJSg2VhnxnVxnIjbW/7DiSJ6QyKcY4R nbNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=sender:x-gm-message-state:from:to:cc:subject:date:message-id :mime-version:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:x-spam-checked-in-group:list-post :list-help:list-archive:list-subscribe:list-unsubscribe; bh=mwWJTAG+Lm+HaPX3fyGHShRnrGUQ/2Rrs187TT4cA7c=; b=Tp4x73rneALw5X23Pvwy5xpaAlFvBI0xvIGdjoW3hbaDjaNhjwx7k87dfOAx4OAQ28 k6jciz/tu8Pqf56+g/Wvm1Gwuy/GWMWVpa1ctI74Um6XQFYHUX7zYrSocFgCyPptTl9o pa8qYBrWDybssmCjRWi907At+Msw/RTbUiYzEmNqUweSE4MOVhKQqzdCROSCbkuSmi0W F5/swSiDFp3gh8A1Dz8sc3+X7lxdBjvb9olJFq1m0va2tHUaQtQtT6GeN7YDcEH1qpYT 0XvYJ7HhhYWtZf6OTd9xZBAyCCbaNynSK5pizxpfNgztZfnGPfOOe0tZdV87Fu9Gw5ci FsNQ== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM531zrTpDlFY2HwcRTtWZLNDs90nULqmIGHQZB+JA2QvmOsjeNtxo DFxhuPeQ2AkrgV3UW9D6ESs= X-Google-Smtp-Source: ABdhPJyJi65vOQGlRB9Pxu81S/EkjIPEYzsjz0unXj2In70IuGDGvHf0dDe6W1xYrjKl+mm0sn/wwQ== X-Received: by 2002:a17:902:7587:b029:109:69bd:3eae with SMTP id j7-20020a1709027587b029010969bd3eaemr651112pll.40.1623359132236; Thu, 10 Jun 2021 14:05:32 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a65:4186:: with SMTP id a6ls3386178pgq.6.gmail; Thu, 10 Jun 2021 14:05:31 -0700 (PDT) X-Received: by 2002:a63:3e84:: with SMTP id l126mr340011pga.70.1623359131412; Thu, 10 Jun 2021 14:05:31 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1623359131; cv=none; d=google.com; s=arc-20160816; b=mXDrXitITMDoHfw/jYD2KlSxGfm+awKETl6g/uzw9CZnGE+1oRvSzwDwhOyMGfDvyp d+IABbXJxl2yg/vQbWzLq/JC7Z7XE8VICbAX5Lu3QcVzh6lYbLRKNT77XsYHzsas5Hva b6e180jh98p0n2dNqQaS5C2kWBs/wJ/1QCsG9Y2ohAZBjdSlX5zFeDKUg9B2ygPAnpxi STfkG97Sz6ilRrtdsWlcO3h08x+rpIhPbUPRye9tPcddLfOvn38mlwriyUNTlDBQgT/l bTPybKCtnS0QlUToH8ugCVFb+n9YOp2gGBcqhDfqxCCGWihSFJwrgY9hPPRswF7tRoR/ VNdQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:dkim-signature; bh=rasXrb25oiecjvH30OF624JGfXV4UjHSnBiwjwt/gqQ=; b=CZU7qAk/gv/qg0s5lzkvNCzIecOZnjEYf3TGuyJhZlxVkizrhQ7vJIt9fkNxgVBpd+ Eeb3BG2jSMDS1zqm9WJXfXBWitqAtola5dR8DoO17YfSpRZgPvPX7Z9fcedpvfDNTEPV ilWwjvpS0I3AX24d2ap4ExRDrzI9K1am5/6eHhxfYMWbbnDy1w1zRdbqQ1/6mTErrUUW Z8g2y6j3P+oFKtQ7chrTFFOgen52A7Y998nOlQWiyQrN72PfDNC4YGlbZWdx/ffXSw6J AUGTKURexrq3gzHgbFAw25N3/ziuxa/ahoDz/HiOAQ/AFMooA8O90E7kZ8ZH6G7rwMkF f92g== ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ZLVmum39; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::22c as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from mail-oi1-x22c.google.com (mail-oi1-x22c.google.com. [2607:f8b0:4864:20::22c]) by gmr-mx.google.com with ESMTPS id nu4si117713pjb.0.2021.06.10.14.05.31 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 10 Jun 2021 14:05:31 -0700 (PDT) Received-SPF: pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::22c as permitted sender) client-ip=2607:f8b0:4864:20::22c; Received: by mail-oi1-x22c.google.com with SMTP id v22so3604003oic.2 for ; Thu, 10 Jun 2021 14:05:31 -0700 (PDT) X-Received: by 2002:aca:2215:: with SMTP id b21mr280443oic.94.1623359130428; Thu, 10 Jun 2021 14:05:30 -0700 (PDT) Received: from localhost.localdomain ([172.58.12.192]) by smtp.gmail.com with ESMTPSA id s2sm721839oom.17.2021.06.10.14.05.27 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 10 Jun 2021 14:05:30 -0700 (PDT) From: James Hilliard To: swupdate@googlegroups.com Cc: James Hilliard Subject: [swupdate] [PATCH v4 1/1] Add support for setting hybrid dos partition entries. Date: Thu, 10 Jun 2021 17:05:12 -0400 Message-Id: <20210610210512.62221-1-james.hilliard1@gmail.com> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 X-Original-Sender: james.hilliard1@gmail.com X-Original-Authentication-Results: gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=ZLVmum39; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::22c as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , This adds the ability to set dos partition entries for gpt disks. This is mostly useful for booting on GPT disks from boards that don't natively support GPT. Config example: partition-1 = [ "size=550M", "start=2048", "name=boot", "type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B", "fstype=vfat", "dostype=0x0C" ]; This approach uses hybrid aware wrapper functions and structures to encapsulate the hybrid partition handling differences better. Signed-off-by: James Hilliard --- Changes v3 -> v4: - initialize cxt to NULL Changes v2 -> v3: - check for valid cxt and device fd before calling fdisk_deassign_device Changes v1 -> v2: - fix hybrid lbtype check - fix dostype parttype --- doc/source/handlers.rst | 12 + handlers/diskpart_handler.c | 611 ++++++++++++++++++++++++++++-------- 2 files changed, 494 insertions(+), 129 deletions(-) diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst index f35fbf3..81f61df 100644 --- a/doc/source/handlers.rst +++ b/doc/source/handlers.rst @@ -795,6 +795,18 @@ supported: | | | It is the hex code for DOS (MBR) partition table | | | | or it is the string identifier in case of GPT. | +-------------+----------+----------------------------------------------------+ + | dostype | string | Type of DOS (MBR) partition entry when using a | + | | | table with a "gpt" labeltype. | + | | | Using this option will create a hybrid MBR table. | + | | | It is the hex code for DOS (MBR) partition table. | + | | | This would typically be used when one wants to use | + | | | a GPT formatted disk with a board that requires a | + | | | dos table entry for initial bootstrapping. | + | | | Note: A maximum of 3 partitions can have a dostype | + | | | specified, this limit only applies to dos table | + | | | entries and does not affect partitions without a | + | | | dostype specified. | + +-------------+----------+----------------------------------------------------+ | fstype | string | Optional filesystem type to be created on the | | | | partition. If no fstype key is given, no file | | | | will be created on the corresponding partition. | diff --git a/handlers/diskpart_handler.c b/handlers/diskpart_handler.c index 62a45df..acd21d4 100644 --- a/handlers/diskpart_handler.c +++ b/handlers/diskpart_handler.c @@ -36,6 +36,16 @@ static inline int ext_mkfs_short(const char *device_name, const char *fstype) { } #endif +/* + * We will only have a parent in hybrid mode. + */ +#define IS_HYBRID(cxt) fdisk_get_parent(cxt) + +/* + * Get the parent if it exists, otherwise context is already the parent. + */ +#define PARENT(cxt) fdisk_get_parent(cxt) ? fdisk_get_parent(cxt) : cxt + struct supported_filesystems { const char *fstype; int (*mkfs) (const char *device_name, const char *fstype); @@ -60,7 +70,8 @@ enum partfield { PART_START, PART_TYPE, PART_NAME, - PART_FSTYPE + PART_FSTYPE, + PART_DOSTYPE }; const char *fields[] = { @@ -68,7 +79,8 @@ const char *fields[] = { [PART_START] = "start", [PART_TYPE] = "type", [PART_NAME] = "name", - [PART_FSTYPE] = "fstype" + [PART_FSTYPE] = "fstype", + [PART_DOSTYPE] = "dostype" }; struct partition_data { @@ -78,6 +90,7 @@ struct partition_data { char type[SWUPDATE_GENERAL_STRING_SIZE]; char name[SWUPDATE_GENERAL_STRING_SIZE]; char fstype[SWUPDATE_GENERAL_STRING_SIZE]; + char dostype[SWUPDATE_GENERAL_STRING_SIZE]; LIST_ENTRY(partition_data) next; }; LIST_HEAD(listparts, partition_data); @@ -90,23 +103,200 @@ struct hnd_priv { struct listparts listparts; /* list of partitions */ }; -/** - * diskpart_set_partition - set values in a fdisk_partition - * @cxt: libfdisk context - * @pa: pointer to fdisk_partition to be changed - * @part: structure with values to be set, read from sw-description - * - * return 0 if ok - */ +struct create_table { + bool parent; + bool child; +}; + +struct diskpart_table { + struct fdisk_table *parent; + struct fdisk_table *child; +}; + +static char *diskpart_get_lbtype(struct img_type *img) +{ + return dict_get_value(&img->properties, "labeltype"); +} + +static int diskpart_assign_label(struct fdisk_context *cxt, struct img_type *img, + struct hnd_priv priv, struct create_table *createtable, unsigned long hybrid) +{ + char *lbtype = diskpart_get_lbtype(img); + int ret = 0; + + /* + * Check partition table + */ + if (!fdisk_has_label(cxt)) { + WARN("%s does not contain a recognized partition table", + img->device); + ret = fdisk_create_disklabel(cxt, lbtype); + if (ret) { + ERROR("Failed to create disk label"); + return ret; + } + createtable->parent = true; + if (hybrid) + createtable->child = true; + } else if (lbtype) { + if (!strcmp(lbtype, "gpt")) { + priv.labeltype = FDISK_DISKLABEL_GPT; + } else { + priv.labeltype = FDISK_DISKLABEL_DOS; + } + + if (!fdisk_is_labeltype(cxt, priv.labeltype)) { + WARN("Partition table of different type, setting to %s, all data lost !", + lbtype); + ret = fdisk_create_disklabel(cxt, lbtype); + if (ret) { + ERROR("Failed to create disk label"); + return ret; + } + createtable->parent = true; + if (hybrid) + createtable->child = true; + } + } + + return ret; +} + +static int diskpart_assign_context(struct fdisk_context **cxt,struct img_type *img, + struct hnd_priv priv, unsigned long hybrid, struct create_table *createtable) +{ + struct fdisk_context *parent; + int ret = 0; + + /* + * Parent context, accessed through the child context when + * used in hybrid mode. + */ + parent = fdisk_new_context(); + if (!parent) { + ERROR("Failed to allocate libfdisk context"); + return -ENOMEM; + } + + /* + * The library uses dialog driven partitioning by default. + * Disable as we don't support interactive dialogs. + */ + ret = fdisk_disable_dialogs(parent, 1); + if (ret) { + ERROR("Failed to disable dialogs"); + return ret; + } + + /* + * fdisk_new_nested_context requires the device to be assigned. + */ + ret = fdisk_assign_device(parent, img->device, 0); + if (ret == -EACCES) { + ERROR("no access to %s", img->device); + return ret; + } + + /* + * fdisk_new_nested_context requires the parent label to be set. + */ + ret = diskpart_assign_label(parent, img, priv, createtable, hybrid); + if (ret) + return ret; + + if (hybrid) { + /* + * Child context which we will use for the hybrid dos + * table in GPT mode. + * + * This also lets us access the parent context. + */ + *cxt = fdisk_new_nested_context(parent, "dos"); + if (!cxt) { + ERROR("Failed to allocate libfdisk nested context"); + return -ENOMEM; + } + + /* + * The library uses dialog driven partitioning by default. + * Disable as we don't support interactive dialogs. + */ + ret = fdisk_disable_dialogs(*cxt, 1); + if (ret) { + ERROR("Failed to disable nested dialogs"); + return ret; + } + } else { + /* + * Use the parent context directly when not in hybrid mode. + */ + *cxt = parent; + } + + return ret; +} + +static struct diskpart_table *diskpart_new_table(struct fdisk_context *cxt) +{ + struct diskpart_table *tb = NULL; + + tb = calloc(1, sizeof(*tb)); + if (!tb) + return NULL; + + tb->parent = fdisk_new_table(); + if (!tb->parent) { + free(tb); + return NULL; + } + + if (IS_HYBRID(cxt)) { + tb->child = fdisk_new_table(); + if (!tb->child) { + fdisk_unref_table(tb->parent); + free(tb); + return NULL; + } + } + + return tb; +} + +static void diskpart_unref_table(struct diskpart_table *tb) +{ + if (!tb) + return; + + if (tb->child) + fdisk_unref_table(tb->child); + + if (tb->parent) + fdisk_unref_table(tb->parent); + + free(tb); +} + +static int diskpart_get_partitions(struct fdisk_context *cxt, struct diskpart_table *tb, + struct create_table *createtable) +{ + int ret = 0; + + if (fdisk_get_partitions(PARENT(cxt), &tb->parent)) + createtable->parent = true; + + if (IS_HYBRID(cxt) && fdisk_get_partitions(cxt, &tb->child)) + createtable->child = true; + + return ret; +} + static int diskpart_set_partition(struct fdisk_partition *pa, struct partition_data *part, unsigned long sector_size, struct fdisk_parttype *parttype) { - int ret = 0; + int ret; - if (!sector_size) - sector_size = 1; fdisk_partition_unset_partno(pa); fdisk_partition_unset_size(pa); fdisk_partition_unset_start(pa); @@ -117,7 +307,7 @@ static int diskpart_set_partition(struct fdisk_partition *pa, if (part->partno != LIBFDISK_INIT_UNDEF(part->partno)) ret |= fdisk_partition_set_partno(pa, part->partno); else - ret |= fdisk_partition_partno_follow_default(pa, 1); + ret |= -EINVAL; if (strlen(part->name)) ret |= fdisk_partition_set_name(pa, part->name); if (part->size != LIBFDISK_INIT_UNDEF(part->size)) @@ -131,6 +321,85 @@ static int diskpart_set_partition(struct fdisk_partition *pa, return ret; } +static int diskpart_set_hybrid_partition(struct fdisk_partition *pa, + struct partition_data *part, + struct fdisk_parttype *parttype, + struct fdisk_table *tb) +{ + /* + * Lookup the parent partition by partition number so that we + * can align the nested/hybrid partition entries properly. + */ + struct fdisk_partition *parent = fdisk_table_get_partition_by_partno(tb, part->partno); + int ret = 0; + + if (!parent) { + ERROR("I cannot find parent for hybrid partition %zu(%s)", part->partno, part->name); + return -EINVAL; + }; + + fdisk_partition_unset_partno(pa); + fdisk_partition_unset_size(pa); + fdisk_partition_unset_start(pa); + fdisk_partition_size_explicit(pa, 1); + if (fdisk_partition_has_start(parent)) + ret = fdisk_partition_set_start(pa, fdisk_partition_get_start(parent)); + else + ret = -EINVAL; + ret |= fdisk_partition_partno_follow_default(pa, 1); + if (strlen(part->name)) + ret |= fdisk_partition_set_name(pa, part->name); + if (fdisk_partition_has_size(parent)) + ret |= fdisk_partition_set_size(pa, fdisk_partition_get_size(parent)); + else + ret |= -EINVAL; + + if (parttype) + ret |= fdisk_partition_set_type(pa, parttype); + + return ret; +} + +static int diskpart_append_hybrid_pmbr(struct fdisk_label *lb, struct fdisk_table *tb) +{ + struct fdisk_partition *pa; + struct fdisk_parttype *parttype; + int ret = 0; + + pa = fdisk_new_partition(); + fdisk_partition_unset_partno(pa); + fdisk_partition_unset_size(pa); + fdisk_partition_unset_start(pa); + fdisk_partition_size_explicit(pa, 1); + + /* + * Place the hybrid PMBR over the GPT header + */ + ret = fdisk_partition_set_start(pa, 1); + ret |= fdisk_partition_set_size(pa, 33); + + /* + * Set type to 0xEE(Intel EFI GUID Partition Table) for hybrid PMBR + */ + parttype = fdisk_label_get_parttype_from_code(lb, 0xee); + ret |= fdisk_partition_set_type(pa, parttype); + + /* + * Just append the hybrid PMBR entry at the end since Linux will + * run in GPT mode if any primary DOS entry is 0xEE. + */ + ret |= fdisk_partition_partno_follow_default(pa, 1); + if (ret) + return ret; + + if ((ret = fdisk_table_add_partition(tb, pa)) < 0) { + ERROR("Failed to append hybrid PMBR to table"); + } + fdisk_unref_partition(pa); + + return ret; +} + /* * Return true if partition differs */ @@ -173,7 +442,30 @@ static bool diskpart_partition_cmp(struct fdisk_partition *firstpa, struct fdisk return false; } -static int diskpart_fill_table(struct fdisk_context *cxt, struct fdisk_table *tb, +static int diskpart_reload_table(struct fdisk_context *cxt, struct fdisk_table *tb) +{ + int ret = 0; + + ret = fdisk_delete_all_partitions(cxt); + if (ret) { + ERROR("Partition table cannot be deleted: %d", ret); + return ret; + } + ret = fdisk_apply_table(cxt, tb); + if (ret) { + ERROR("Partition table cannot be applied: %d", ret); + return ret; + } + fdisk_reset_table(tb); + ret = fdisk_get_partitions(cxt, &tb); + if (ret) { + ERROR("Error loading applied table %d:", ret); + return ret; + } + return ret; +} + +static int diskpart_fill_table(struct fdisk_context *cxt, struct diskpart_table *tb, struct partition_data *part, struct hnd_priv priv) { struct fdisk_parttype *parttype; @@ -181,13 +473,15 @@ static int diskpart_fill_table(struct fdisk_context *cxt, struct fdisk_table *tb unsigned long sector_size; int ret = 0; - lb = fdisk_get_label(cxt, NULL); + lb = fdisk_get_label(PARENT(cxt), NULL); if (!lb) { ERROR("Failed to load label"); return -EINVAL; } - sector_size = fdisk_get_sector_size(cxt); + sector_size = fdisk_get_sector_size(PARENT(cxt)); + if (!sector_size) + sector_size = 1; LIST_FOREACH(part, &priv.listparts, next) { struct fdisk_partition *newpa; @@ -196,7 +490,7 @@ static int diskpart_fill_table(struct fdisk_context *cxt, struct fdisk_table *tb /* * GPT uses strings instead of hex code for partition type */ - if (fdisk_is_label(cxt, GPT)) { + if (fdisk_is_label(PARENT(cxt), GPT)) { parttype = fdisk_label_get_parttype_from_string(lb, part->type); if (!parttype) parttype = fdisk_label_get_parttype_from_string(lb, GPT_DEFAULT_ENTRY_TYPE); @@ -207,13 +501,65 @@ static int diskpart_fill_table(struct fdisk_context *cxt, struct fdisk_table *tb if (ret) { WARN("I cannot set all partition's parameters"); } - if ((ret = fdisk_table_add_partition(tb, newpa)) < 0) { + if ((ret = fdisk_table_add_partition(tb->parent, newpa)) < 0) { ERROR("I cannot add partition %zu(%s): %d", part->partno, part->name, ret); } fdisk_unref_partition(newpa); if (ret) return ret; } + + /* + * Reload parent table against the context to populate default values. + * We must do this before adding hybrid entries so we can derive nested values. + */ + ret = diskpart_reload_table(PARENT(cxt), tb->parent); + if (ret) + return ret; + + if (IS_HYBRID(cxt)) { + lb = fdisk_get_label(cxt, "dos"); + if (!lb) { + ERROR("Failed to load hybrid label"); + return -EINVAL; + } + + LIST_FOREACH(part, &priv.listparts, next) { + if (strlen(part->dostype)) { + struct fdisk_partition *newpa; + + newpa = fdisk_new_partition(); + + parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->dostype, 16)); + if (!parttype) { + ERROR("I cannot add hybrid partition %zu(%s) invalid dostype: %s", + part->partno, part->name, part->dostype); + } + ret = diskpart_set_hybrid_partition(newpa, part, parttype, tb->parent); + if (ret) { + WARN("I cannot set all hybrid partition's parameters"); + } + if ((ret = fdisk_table_add_partition(tb->child, newpa)) < 0) { + ERROR("I cannot add hybrid partition %zu(%s): %d", part->partno, part->name, ret); + } + fdisk_unref_partition(newpa); + if (ret) + return ret; + } + } + /* + * Add PMBR after other entries since bootloaders should not care about its position. + */ + ret = diskpart_append_hybrid_pmbr(lb, tb->child); + if (ret) + return ret; + /* + * Reload child table against the context to fully populate remaining values. + */ + ret = diskpart_reload_table(cxt, tb->child); + if (ret) + return ret; + } return ret; } @@ -258,44 +604,94 @@ static int diskpart_table_cmp(struct fdisk_table *tb, struct fdisk_table *oldtb) return ret; } -static int diskpart_reload_table(struct fdisk_context *cxt, struct fdisk_table *tb) +static int diskpart_compare_tables(struct diskpart_table *tb, struct diskpart_table *oldtb, + struct create_table *createtable) { int ret = 0; - ret = fdisk_delete_all_partitions(cxt); - if (ret) { - ERROR("Partition table cannot be deleted: %d", ret); - return ret; + /* + * A partiton table was found on disk, now compares the two tables + * to check if they differ. + */ + if (!createtable->parent) { + ret = diskpart_table_cmp(tb->parent, oldtb->parent); + if (ret < 0) + return ret; + else if (ret) + createtable->parent = true; } - ret = fdisk_apply_table(cxt, tb); - if (ret) { - ERROR("Partition table cannot be applied: %d", ret); - return ret; + + if (tb->child && !createtable->child) { + ret = diskpart_table_cmp(tb->child, oldtb->child); + if (ret < 0) + return ret; + else if (ret) + createtable->child = true; } - fdisk_reset_table(tb); - ret = fdisk_get_partitions(cxt, &tb); - if (ret) { - ERROR("Error loading applied table %d:", ret); - return ret; + + ret = 0; + + return ret; +} + +static int diskpart_write_table(struct fdisk_context *cxt, struct create_table *createtable) +{ + int ret = 0; + + if (createtable->parent || createtable->child) + TRACE("Partitions on disk differ, write to disk;"); + else + TRACE("Same partition table on disk, do not touch partition table !"); + + if (createtable->child) { + if (!IS_HYBRID(cxt)) { + ERROR("Internal fault, tried to create nested table but disk is not hybrid."); + return -EINVAL; + } + /* + * Everything done, write into disk + */ + ret = fdisk_write_disklabel(cxt); + if (ret) + ERROR("Nested partition table cannot be written on disk"); + if (fdisk_reread_partition_table(cxt)) + WARN("Nested partition table cannot be reread from the disk, be careful !"); + if (ret) + return ret; + } + + if (createtable->parent) { + /* + * Everything done, write into disk + */ + ret = fdisk_write_disklabel(PARENT(cxt)); + if (ret) + ERROR("Partition table cannot be written on disk"); + if (fdisk_reread_partition_table(PARENT(cxt))) + WARN("Table cannot be reread from the disk, be careful !"); + if (ret) + return ret; } + return ret; } static int diskpart(struct img_type *img, void __attribute__ ((__unused__)) *data) { - char *lbtype = dict_get_value(&img->properties, "labeltype"); + char *lbtype = diskpart_get_lbtype(img); struct dict_list *parts; struct dict_list_elem *elem; - struct fdisk_context *cxt; + struct fdisk_context *cxt = NULL; struct partition_data *part; struct partition_data *tmp; - struct fdisk_table *tb = NULL; - struct fdisk_table *oldtb = NULL; + struct diskpart_table *tb = NULL; + struct diskpart_table *oldtb = NULL; int ret = 0; unsigned long i; + unsigned long hybrid = 0; struct hnd_priv priv = {FDISK_DISKLABEL_DOS}; - bool createtable = false; + struct create_table *createtable = (struct create_table *)calloc(1, sizeof(struct create_table)); if (!lbtype || (strcmp(lbtype, "gpt") && strcmp(lbtype, "dos"))) { ERROR("Just GPT or DOS partition table are supported"); @@ -307,28 +703,6 @@ static int diskpart(struct img_type *img, return -EINVAL; } - cxt = fdisk_new_context(); - if (!cxt) { - ERROR("Failed to allocate libfdisk context"); - return -ENOMEM; - } - - /* - * The library uses dialog driven partitioning by default. - * Disable as we don't support interactive dialogs. - */ - ret = fdisk_disable_dialogs(cxt, 1); - if (ret) { - ERROR("Failed to disable dialogs"); - goto handler_release; - } - - ret = fdisk_assign_device(cxt, img->device, 0); - if (ret == -EACCES) { - ERROR("no access to %s", img->device); - goto handler_release; - } - struct dict_entry *entry; LIST_FOREACH(entry, &img->properties, next) { parts = &entry->list; @@ -376,12 +750,29 @@ static int diskpart(struct img_type *img, case PART_FSTYPE: strncpy(part->fstype, equal, sizeof(part->fstype)); break; + case PART_DOSTYPE: + strncpy(part->dostype, equal, sizeof(part->dostype)); + hybrid++; + break; } } } elem = LIST_NEXT(elem, next); } + /* + * Hybrid entries must use the primary DOS/MBR partition table, + * this has a maximum of four partitions, however we must reserve + * one for the hybrid PMBR entry so that GPT aware software will + * read the GPT table properly. + */ + if (hybrid > 3) { + ERROR("I cannot add hybrid partition %zu(%s): hybrid dos partition limit of 3 exceeded", + part->partno, strlen(part->name) ? part->name : "UNDEF NAME"); + ret = -EINVAL; + goto handler_exit; + } + TRACE("partition-%zu:%s size %" PRIu64 " start %zu type %s", part->partno != LIBFDISK_INIT_UNDEF(part->partno) ? part->partno : 0, strlen(part->name) ? part->name : "UNDEF NAME", @@ -410,101 +801,63 @@ static int diskpart(struct img_type *img, } } - /* - * Check partition table - */ - if (!fdisk_has_label(cxt)) { - WARN("%s does not contain a recognized partition table", - img->device); - ret = fdisk_create_disklabel(cxt, lbtype); - if (ret) { - ERROR("Failed to create disk label"); - goto handler_release; - } - createtable = true; - } else if (lbtype) { - if (!strcmp(lbtype, "gpt")) - priv.labeltype = FDISK_DISKLABEL_GPT; - else - priv.labeltype = FDISK_DISKLABEL_DOS; - - if (!fdisk_is_labeltype(cxt, priv.labeltype)) { - WARN("Partition table of different type, setting to %s, all data lost !", - lbtype); - ret = fdisk_create_disklabel(cxt, lbtype); - if (ret) { - ERROR("Failed to create disk label"); - goto handler_release; - } - createtable = true; - } + if (hybrid && (!lbtype || strcmp(lbtype, "gpt"))) { + ERROR("Partitions have hybrid(dostype) entries but labeltype is not gpt !"); + ret = -EINVAL; + goto handler_release; } + ret = diskpart_assign_context(&cxt, img, priv, hybrid, createtable); + if (ret == -EACCES) + goto handler_release; + else if (ret) + goto handler_exit; + /* * Create a new in-memory partition table to be compared * with the table on the disk, and applied if differs */ - tb = fdisk_new_table(); - - if (fdisk_get_partitions(cxt, &oldtb)) - createtable = true; - + tb = diskpart_new_table(cxt); if (!tb) { ERROR("OOM creating new table !"); ret = -ENOMEM; goto handler_exit; } + oldtb = calloc(1, sizeof(*oldtb)); + if (!oldtb) { + ERROR("OOM loading partitions !"); + return -ENOMEM; + } + /* - * Fill the new in-memory partition table from the partition list. + * Fill the old in-memory partition table from the disk. */ - ret = diskpart_fill_table(cxt, tb, part, priv); + ret = diskpart_get_partitions(cxt, oldtb, createtable); if (ret) goto handler_exit; /* - * Reload new table against the context to populate default values - * so that we can compare partitions properly. + * Fill the new in-memory partition table from the partition list. */ - ret = diskpart_reload_table(cxt, tb); + ret = diskpart_fill_table(cxt, tb, part, priv); if (ret) goto handler_exit; - /* - * A partiton table was found on disk, now compares the two tables - * to check if they differ. - */ - if (!createtable) { - ret = diskpart_table_cmp(tb, oldtb); - if (ret < 0) - goto handler_exit; - else if (ret) - createtable = true; - } - - if (createtable) { - TRACE("Partitions on disk differ, write to disk;"); + ret = diskpart_compare_tables(tb, oldtb, createtable); + if (ret) + goto handler_exit; - /* - * Everything done, write into disk - */ - ret = fdisk_write_disklabel(cxt); - if (ret) - ERROR("Partition table cannot be written on disk"); - if (fdisk_reread_partition_table(cxt)) - WARN("Table cannot be reread from the disk, be careful !"); - } else { - ret = 0; - TRACE("Same partition table on disk, do not touch partition table !"); - } + ret = diskpart_write_table(cxt, createtable); handler_exit: if (tb) - fdisk_unref_table(tb); + diskpart_unref_table(tb); if (oldtb) - fdisk_unref_table(oldtb); - if (fdisk_deassign_device(cxt, 0)) - WARN("Error deassign device %s", img->device); + diskpart_unref_table(oldtb); + if (cxt && fdisk_get_devfd(cxt) >= 0) + if (fdisk_deassign_device(cxt, 0)) + WARN("Error deassign device %s", img->device); handler_release: fdisk_unref_context(cxt); @@ -519,7 +872,7 @@ handler_release: #ifdef CONFIG_DISKFORMAT /* Create filesystems */ - if (!ret && createtable) { + if (!ret && createtable->parent) { LIST_FOREACH(part, &priv.listparts, next) { int index; /*