From patchwork Wed Jun 2 07:58:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hilliard X-Patchwork-Id: 1486498 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::c37; helo=mail-oo1-xc37.google.com; envelope-from=swupdate+bncbcl4hcw73qcbb77u3scqmgqeywcqh7a@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=dEqCwxBd; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=Fmf8HiXK; dkim-atps=neutral Received: from mail-oo1-xc37.google.com (mail-oo1-xc37.google.com [IPv6:2607:f8b0:4864:20::c37]) (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 4Fw1fC4KbCz9sCD for ; Wed, 2 Jun 2021 18:00:02 +1000 (AEST) Received: by mail-oo1-xc37.google.com with SMTP id p4-20020a4a48040000b029020eb67f7264sf981814ooa.23 for ; Wed, 02 Jun 2021 01:00:02 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1622620800; cv=pass; d=google.com; s=arc-20160816; b=bm6YsphAopJ8Z0T0fArIJXgt9sjmdWc4rciJnJZTaIBGRpWiYFPOFtkdav+UC46sZe o60n69nC8H76Uenpdfa5NZM2W3eeJQcmreUCa1rhwRfp3rP2D9C2ey5PXQxvSW/9+ZqW HwRojQ4hHSMTevJl9H/7YfOOGqBK1HVnsNLDaPlhJcKPmtlG5Y1UFmDhr3dfDGnFW0Vr 28bTB1L0JAz+okDsjneOOBwZS4Q+wVDkIacSPJPzSqDuspcX+V5SM/YIXy7vZTXXGVR4 I0d/w4Ugn8XkHK79BmreARR9p9Gu7ZWpEpdr/KU7VKJ+cdXOnGwpJNbuaL23tYp9bvpB zyug== 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=dW8lSaD/Qzhkp6z2Hq89d6hq+mQBHn7XzltaFNnAmMM=; b=s5e6+KrByWaH/ESl9wl7J15EHpNBi9dRfVsPs/hqzJJLbzpRK8taIT/r7ddvG/vnFY oPNB2V+7RGvGNWv+XQPczl4vcCBx4VDATCun4mOGny2NlzcUnEFqqc2zFCo4Py5RCj7U P4CyvOG1bryMQW3isWg5CyKWopL7kyrng3KpgYXETbmh4ivVfM801viWvOmZsuznsOpx 9t7UQJMZvYbiruYb91weQvcNupDGqldzFfFhmbThEzew9qwk1e+lVylJ1i1iXFAfR0f9 kcHDcm24xKRMq9XRdJh6bfPY8/uQi7BqVIW47MtoQyfOiZegjEhCvRj1MYOAdvo4vSZM spWA== ARC-Authentication-Results: i=2; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=cwngWO92; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::836 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=dW8lSaD/Qzhkp6z2Hq89d6hq+mQBHn7XzltaFNnAmMM=; b=dEqCwxBdcPgdpvvPFE58xzhFACJUP4e/w758bBlUPfbxC+xnU0/2ysszOLnLR4/MQj pZPy4dkXYG71BwRnjrHym0ukZwN5x9ByLmx2yifodYLtGsti6beXWm3MMKR5Qed3wi9i tDi8xuG2XBQSenubl/HzP8vfJu7hr+IAeb/jNuL75M1f6ls1ZWpiOZH/MLZ0Sn3dNpQ3 iUfOWoyMEM78N6aLI+h0ZOBXCazWGgmowC8Hhhc1PnM+1Jf4P2hcj/2Y7IPkIvt3MFzq +8OmEJtplihf6DsMswGGrXwgE1oOPo19nOmdKDXLqsCJVcAcuBriHhpUOc0mQwHS3niJ ZLwQ== 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=dW8lSaD/Qzhkp6z2Hq89d6hq+mQBHn7XzltaFNnAmMM=; b=Fmf8HiXKJePX3+JfGQrPM1Asa3AeKr1l1XiPtU0LnYeiThzT+VSmRs2SMeBhx/gBYR 3BSqw0jV9rNtDCC/kkqfBA2b03XoBbhg0NbkdtxXzSd3w+7zwNgRuEqNL1S1YZjMhq61 hhMtKRlbpCqQHRhtSWV8AKVjnGhVb2aKewyEmlmiL+NSrvDmSmKMUdQ9pzvkaQM6ZZoF h7jUhpB65BIT485jJlyqsMvRgGS+sblOxdh74oqlcoCky/g7oCjjMcGoOu33NEubXrCo OtLpd20OZoMgbPrHKqpWMQqkRFujACcZ2U04Hgq+j6QFSej4WrGsHTn0UDOhoJrAprHO jbug== 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=dW8lSaD/Qzhkp6z2Hq89d6hq+mQBHn7XzltaFNnAmMM=; b=HFc+sbYva8ermKkeGgr/2VO/l9gwa8ov/iSA+8KZF/20DOzU2j9LHuKmGV4bfElrnP tV/qcgdZtbt26eW45XaQRf6LoXxl662h1RqdzIYxYDVo+7J6QKf74Vm9UoJUW+ECcn+a RkDfInErWmUDDw/l4n3MOBkfhROwqSDx8d66c1sg06OAkUlknl1e+YOA2TvbHISfOLZG 4TdNLzmvDWplk8Dbkq8bn/VjFiszCTh777fXAgXFlKwpFfJaqSiggCvY6Vq9ikAL808z KRqjCXcXq2lJnKoIQZDizYzSwZiChhwoFXeXmXPMLFwFLUzFni8OtShvXYh0KmZQWwAz gQDg== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOAM533BJDJoDYUQI/7RUUDpERVyXyoAawg17JCbMu2wuKn3qexn8TYQ e8nKWgThbApg/P+mEntf5bw= X-Google-Smtp-Source: ABdhPJxGBq70Mubqb5RAUYZNHXFFO3qOqinyp8QA16ngrliwqgUgVRvlHZ5GlKMBiW/N64+TJjyLiw== X-Received: by 2002:aca:f5d7:: with SMTP id t206mr995485oih.75.1622620799843; Wed, 02 Jun 2021 00:59:59 -0700 (PDT) X-BeenThere: swupdate@googlegroups.com Received: by 2002:a9d:7b46:: with SMTP id f6ls6316461oto.2.gmail; Wed, 02 Jun 2021 00:59:59 -0700 (PDT) X-Received: by 2002:a9d:74c7:: with SMTP id a7mr25715066otl.42.1622620799338; Wed, 02 Jun 2021 00:59:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1622620799; cv=none; d=google.com; s=arc-20160816; b=iONWugwiL0OZhngxqCGz6IQ2k5kw+CJP7xeLrSS1FeAhHy2sf2ikLFwD50O+LRh4PZ N5AdA1cm5eTVY0HN/qaXRE5g95VN965KnGPke3DFVmArXlqC0HT3tLbQUblYafu8Qt2W 7BY9/Hcaf7NTrCTZMG9RZvpT9gbZswdx4lLbgm/HfKl4cZH7pYGj+pKClg2DMC0COADs MjbBxRrhjasphyVO4sM4E1mcB0qMZQDEHLrwnlUvtCrazjaslWvT0CTRIZmBtN7C7Fa4 024jXXXWOMvRkWlqmOWMrLlgyWj9C5nSHlomjC9J1lSQek0XscPsDOWjGiCub64eHNBe /HhQ== 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=tOov637rLNaYQUt+6039niJNL1NJjJuvX1tdSBt68/0=; b=r4Ely3zdCLjLsOiXW9pXLSf9r0jNLcqWArapw4SCv0fQki2i+MNjDlwCL2URi3zb/4 a3pLQHPJEFVDkWr0xIBZO0uC4FP8R7YUjXYo6tDMmoCEITpuyC4Or9uZzEZ+3qxAbXzC nHvAvrH3XinEgJmQBi1xq3euSirYUNcbzxEAFulyI9UlrbdPBKhV/WL8Vog2eRYtCIab ffrw18hmG2CJO2yOVeaDl/44bfJty4UHb//Qa0UxGsQ612PSLJUZ3LY8TXp5an50FurF 6QSjhVrJ31QkL+Lv2r4P+FI6JilWXWOXYKhxxin4wJZIp/y2J2iHyu7zs7T4lryLcGS4 mRAQ== ARC-Authentication-Results: i=1; gmr-mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=cwngWO92; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::836 as permitted sender) smtp.mailfrom=james.hilliard1@gmail.com; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from mail-qt1-x836.google.com (mail-qt1-x836.google.com. [2607:f8b0:4864:20::836]) by gmr-mx.google.com with ESMTPS id a25si2170977otp.1.2021.06.02.00.59.59 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 02 Jun 2021 00:59:59 -0700 (PDT) Received-SPF: pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::836 as permitted sender) client-ip=2607:f8b0:4864:20::836; Received: by mail-qt1-x836.google.com with SMTP id k19so1235651qta.2 for ; Wed, 02 Jun 2021 00:59:59 -0700 (PDT) X-Received: by 2002:a05:622a:170b:: with SMTP id h11mr24056048qtk.330.1622620798382; Wed, 02 Jun 2021 00:59:58 -0700 (PDT) Received: from localhost.localdomain (50-207-130-213-static.hfc.comcastbusiness.net. [50.207.130.213]) by smtp.gmail.com with ESMTPSA id s20sm12962077qke.2.2021.06.02.00.59.57 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 02 Jun 2021 00:59:58 -0700 (PDT) From: James Hilliard To: swupdate@googlegroups.com Cc: James Hilliard Subject: [swupdate] [PATCH 1/1] Add support for setting hybrid dos partition entries. Date: Wed, 2 Jun 2021 03:58:49 -0400 Message-Id: <20210602075849.94594-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=cwngWO92; spf=pass (google.com: domain of james.hilliard1@gmail.com designates 2607:f8b0:4864:20::836 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 --- doc/source/handlers.rst | 12 + handlers/diskpart_handler.c | 598 ++++++++++++++++++++++++++++-------- 2 files changed, 484 insertions(+), 126 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..9d3a393 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,59 @@ 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->type, 16)); + 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. + */ + diskpart_append_hybrid_pmbr(lb, tb->child); + /* + * 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 +598,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 +697,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 +744,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,99 +795,60 @@ 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 (!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); + diskpart_unref_table(oldtb); if (fdisk_deassign_device(cxt, 0)) WARN("Error deassign device %s", img->device); @@ -519,7 +865,7 @@ handler_release: #ifdef CONFIG_DISKFORMAT /* Create filesystems */ - if (!ret && createtable) { + if (!ret && createtable->parent) { LIST_FOREACH(part, &priv.listparts, next) { int index; /*