From patchwork Thu Jun 10 19:08:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 1490627 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::b3e; helo=mail-yb1-xb3e.google.com; envelope-from=swupdate+bncbcl4hcw73qcbbvwgrgdamgqervuf7jy@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=YMo0uQwi; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=NU2vmLmI; dkim-atps=neutral Received: from mail-yb1-xb3e.google.com (mail-yb1-xb3e.google.com [IPv6:2607:f8b0:4864:20::b3e]) (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 4G1D6f2G2cz9sRN for ; Fri, 11 Jun 2021 05:09:13 +1000 (AEST) Received: by mail-yb1-xb3e.google.com with SMTP id w130-20020a25df880000b02905327e5d7f43sf815474ybg.3 for ; Thu, 10 Jun 2021 12:09:13 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1623352150; cv=pass; d=google.com; s=arc-20160816; b=e668DvoYqQVaM4PgCJSHt2rW5VCN4BIyO7KJUROJeICsk4Fd4MA52xuGw4CYP53DAE pIKoM3uvRTifO9q55wX7+CtOs9fZkV+qTlpdzrV/vCS7qvL//pOjDxVzLMo52pntOOnx zg5oSyf/KFGdubzkMhRMLKzIeAskZYc9qWz4JUDmGE4AeY01/qMZcoKCjvuddcD6maZK qI4/lbKefhsM1iuIySKJXJ6BfXtNgSkYImkIKF7J2a+LSCKkT9wSLtvKMzTcvykf2nsl RhIpJS+2+4SmweQyiv8pF+PAKV46VFs3quS4LfwYUr/VtMsWXl7uep9revaS2urc1m4r kjkw== 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=XW4GTYNS1Iq5YCwt8mrT6MdHUQMqDxFBhQtSDFYrww8=; b=KJw/Bq1E3bHwUSvPEITliMki8PB4+PELhEg/PKYWzh35dW2gI/DjARCdyXGvj4Nw20 S6Yx740gtWTHfIvlWi3ZNo0BwGuRMpmZLAxcF/HX2X4/wjd+a5HkfnYVmpEpPBeD5R/S xieFyEewCVL/Hn6N2VdIkqnMbuDqkfWivwl8B9ZWBTjH5rJqE5kf+DoMXN8UW2yVlLsk 3Q15xk0g08HTahqwOo99OlECkG0kMCD+QoTshNiXpJZZZ/Sqt1oMG7zjyveK2GQJLwPm cOib5hXrjNH+X/3gItldHU5QqAowXWiseg9WfBgT4eBN1WWsIzA6A7PVlrDQoIetKs1R UXuw== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=nBNZuYiX; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::330 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=XW4GTYNS1Iq5YCwt8mrT6MdHUQMqDxFBhQtSDFYrww8=; b=YMo0uQwilNrbvWt9EBS6i/Sgr6SUzcQgVYXCAoNJHHSokCqeKE8aonN+YAkwtYTCV0 431X61zxK9UU9aIB4tmiVStE2FWq22117nk/AOnxxwwq0oKZjwPBnn8M6RqofvaQBDEi PSQsiJo5yEqFLu5DICJ7QJiBXjvGokSuJDShkQACNMPRvYIXrp94n8oUhE0gSo7UpopI SO26T/3Naq6YEWzlMtD2YC1UR3dBtr54a1PoOfUUXX4PiZTu0JDPoeC1TfLhMPE8B9fC 4cc689qNEnOkowkf+3ylzhBVxJrDPpQ+1UU19tTHR3akE2odjTfBFcNSLbCd9MnQY6O+ p0jg== 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=XW4GTYNS1Iq5YCwt8mrT6MdHUQMqDxFBhQtSDFYrww8=; b=NU2vmLmI1l/HjLnaUSvs/LO8XDg16NqcU8Mo3UtXvoseW1DO72IKSAgv10Q92DKJ9P r4zTWprxrANWNQIJjT1GgucOF4e5+ALEad2diULTv3KDq2xy0KAIGlktkgdfmcw4XK4O aRu2Cc+qn1USHAHTGYHJszBlUv/DckeXLQEF9qYrliwrq5FkpjhRCFHGLRrwF5NyiefZ +T1ifsOUsH1Y9nF3ACVOryXqDGnd8CIlwXJGIuQJV7B6JbvDDtLJ7RrZQMz831CA8LQO 8JRWeIK2Kp0DJ+/eQF+b5y3DbWAu7/8yz7jCc6GDDTyovGvK1lQhtGwI+FnVPwKYxHRF nr6Q== 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=XW4GTYNS1Iq5YCwt8mrT6MdHUQMqDxFBhQtSDFYrww8=; b=rTk7IojJ4bSK3DxrEqKILlGb9eMcRD8n7YebG0JiHD+d26CaCOOQ6Uctn0ZzJZkjh7 KlBc4czTmnGLbMPftP0XcYiaR72yqqH3CY0+qaQtz7QVNb7IdukhizEY5k10Y1mh/EW5 aHevrAQ++oTXGdlpFVSUIThEvvcXZat6zxR+TsXBwKD0cva0+89rNyxRavBVYAZZ/u/o /DlVrGqBFZUXp5R3aZTI2ik6T9HDpHsDPicjv2pquLZ5udCRV0EktAFO3sAQRBUcEQG8 gw/PrYrwV5RLs8ciIuOeXSkyum5uVVWOAba3sN6Iy16zi9PtPueUlDIRCYw/8w+J6mEr rHmg== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM531f2NusCtAIwjZBudd341n6dJTEyr9w9qGwy9vvTa9Uk/SStole nnYOsrUOUqCd+DzldFZTZ1A= X-Google-Smtp-Source: ABdhPJzsugLYp3B7KPsnKQ9oqd+v/d1U1tp0LRVvZKBkaoEowqMQCk6S6YDVfbeQLnp84iWNzkt1+A== X-Received: by 2002:a25:690e:: with SMTP id e14mr392947ybc.39.1623352150271; Thu, 10 Jun 2021 12:09:10 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a25:874d:: with SMTP id e13ls4338549ybn.0.gmail; Thu, 10 Jun 2021 12:09:09 -0700 (PDT) X-Received: by 2002:a05:6902:726:: with SMTP id l6mr512595ybt.68.1623352149809; Thu, 10 Jun 2021 12:09:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1623352149; cv=none; d=google.com; s=arc-20160816; b=Zo6//Ss32MW/njd12K+6TlWSNKgo77/+C5Q0b7MijoyEQTwejxCjN95DtoTB5jXKxn 7AarWuqp6MrGxqLSGwkJ+jmwy2t4CyXz7oCKgrMxg9NuX2HUTzSHGBdg5Vw4Fa2BQj2q VA80fMOyEIvZzMBPu4oG7FhQpQ9u3mTXzs4SW7V6iSP+H/E1kax7ZnhKXvePI4qmanj1 InMu2IDS9S0AqG/E2AcH/JhbuozzRHz/BcMjpdEK8Ik9/PSTYW3Cx+9n0/R0270WAotF NHH3k1mif+9Enk30iSGe67fXZNwmPH4TPfC+bB0jAzy4m5ayed431qPH1whNgLt6Euyq fNlw== 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=B4YeOTRVVBZUVHWCdjQE0OAuPOATapajGmsVKr0HMD8=; b=oNs9Z8fnac2gqR9Y/VlYPTwoyKrFqKF526DzGCLcRXMzsk3xus/P45p1530p4Tlhic /dX3ta6VCFuLdRBm65rgaZD/MUK6lMPz/kCw3VdoXE5nVe9Tuybist3Wn+WyDmqH8cFV 3+j6AUHqgZFea5lXHh2fIJJdWLGO92LDzALHWsrxLxcYVDfTCCxX4v6GgDWBGUPoU7Oe Upn7hKZ4BY2SIHRniYTVd6eibtavuPSR1YvP+juYwlmOotS245n+PCBPqAgKNuybQWbS 3daEgX0nkAQewHZMKkfya6tp5cqEfkgrrkZFkNt2zhUnX3EGRGuhMETBb7f65KklABw7 v8ZQ== ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=nBNZuYiX; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::330 as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from mail-ot1-x330.google.com (mail-ot1-x330.google.com. [2607:f8b0:4864:20::330]) by gmr-mx.google.com with ESMTPS id r9si503617ybb.1.2021.06.10.12.09.09 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 10 Jun 2021 12:09:09 -0700 (PDT) Received-SPF: pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::330 as permitted sender) client-ip=2607:f8b0:4864:20::330; Received: by mail-ot1-x330.google.com with SMTP id 36-20020a9d0ba70000b02902e0a0a8fe36so730304oth.8 for ; Thu, 10 Jun 2021 12:09:09 -0700 (PDT) X-Received: by 2002:a9d:7987:: with SMTP id h7mr3811185otm.98.1623352148792; Thu, 10 Jun 2021 12:09:08 -0700 (PDT) Received: from localhost.localdomain ([172.58.173.74]) by smtp.gmail.com with ESMTPSA id y17sm698856oih.54.2021.06.10.12.09.07 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 10 Jun 2021 12:09:08 -0700 (PDT) From: James Hilliard To: swupdate@googlegroups.com Cc: James Hilliard Subject: [swupdate] [PATCH v3 1/1] Add support for setting hybrid dos partition entries. Date: Thu, 10 Jun 2021 15:08:58 -0400 Message-Id: <20210610190858.61528-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=nBNZuYiX; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::330 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 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 | 609 ++++++++++++++++++++++++++++-------- 2 files changed, 493 insertions(+), 128 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..55ad112 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 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; /*