This is an automated email from the ASF dual-hosted git repository. jerpelea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git
commit f5e40d38092c5de472ea90c44448c04dc574e948 Author: Jiuzhu Dong <dongjiuz...@xiaomi.com> AuthorDate: Sat Sep 25 22:24:13 2021 +0800 fsutils/mkgpt: support gpt partition format Signed-off-by: Jiuzhu Dong <dongjiuz...@xiaomi.com> --- fsutils/mkgpt/Kconfig | 12 + fsutils/mkgpt/Make.defs | 23 ++ fsutils/mkgpt/Makefile | 32 +++ fsutils/mkgpt/mkgpt.c | 599 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 666 insertions(+) diff --git a/fsutils/mkgpt/Kconfig b/fsutils/mkgpt/Kconfig new file mode 100644 index 0000000..b27545d --- /dev/null +++ b/fsutils/mkgpt/Kconfig @@ -0,0 +1,12 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config FSUTILS_MKGPT + bool "mkgpt utility" + default n + select BCH + select DEV_URANDOM + ---help--- + Enables support for the mkgpt utility diff --git a/fsutils/mkgpt/Make.defs b/fsutils/mkgpt/Make.defs new file mode 100644 index 0000000..9f92d81 --- /dev/null +++ b/fsutils/mkgpt/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/fsutils/mkgpt/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_FSUTILS_MKGPT),) +CONFIGURED_APPS += $(APPDIR)/fsutils/mkgpt +endif diff --git a/fsutils/mkgpt/Makefile b/fsutils/mkgpt/Makefile new file mode 100644 index 0000000..1379ac4 --- /dev/null +++ b/fsutils/mkgpt/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/fsutils/mkgpt/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# mkgpt built-in application info + +PROGNAME = mkgpt +PRIORITY = 100 +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_FSUTILS_MKGPT) + +MAINSRC = mkgpt.c + +include $(APPDIR)/Application.mk diff --git a/fsutils/mkgpt/mkgpt.c b/fsutils/mkgpt/mkgpt.c new file mode 100644 index 0000000..ac27448 --- /dev/null +++ b/fsutils/mkgpt/mkgpt.c @@ -0,0 +1,599 @@ +/**************************************************************************** + * apps/fsutils/mkgpt/mkgpt.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <crc32.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/ioctl.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef FAR +#define FAR +#endif + +#define EFI_VERSION 0x00010000 +#define EFI_MAGIC "EFI PART" +#define EFI_ENTRIES 128 +#define EFI_NAMELEN 36 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct efi_header_s +{ + uint8_t magic[8]; + uint32_t version; + uint32_t header_sz; + uint32_t crc32; + uint32_t reserved; + uint64_t header_lba; + uint64_t backup_lba; + uint64_t first_lba; + uint64_t last_lba; + uint8_t volume_uuid[16]; + uint64_t entries_lba; + uint32_t entries_count; + uint32_t entries_size; + uint32_t entries_crc32; +} +__attribute__ ((packed)); + +struct efi_entry_s +{ + uint8_t type_uuid[16]; + uint8_t uniq_uuid[16]; + uint64_t first_lba; + uint64_t last_lba; + uint64_t attr; + uint16_t name[EFI_NAMELEN]; +}; + +struct efi_ptable_s +{ + uint8_t mbr[512]; + union + { + struct efi_header_s header; + uint8_t block[512]; + }; + + struct efi_entry_s entry[EFI_ENTRIES]; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint8_t g_partition_type_uuid[16] = +{ + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + +static const uint8_t g_partition_type_efi[16] = +{ + 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, + 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b, +}; + +static const uint8_t g_partition_type_linux[16] = +{ + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + +static const uint8_t g_partition_type_swap[16] = +{ + 0x6d, 0xfd, 0x57, 0x06, 0xab, 0xa4, 0xc4, 0x43, + 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void get_uuid(FAR uint8_t *uuid) +{ + int fd; + fd = open("/dev/urandom", O_RDONLY); + if (fd > 0) + { + read(fd, uuid, 16); + close(fd); + } +} + +static void init_mbr(FAR uint8_t *mbr, uint32_t blocks) +{ + /* nonbootable */ + + mbr[0x1be] = 0x00; + + /* bogus CHS */ + + mbr[0x1bf] = 0x00; + mbr[0x1c0] = 0x01; + mbr[0x1c1] = 0x00; + + /* GPT partition */ + + mbr[0x1c2] = 0xee; + + /* bogus CHS */ + + mbr[0x1c3] = 0xfe; + mbr[0x1c4] = 0xff; + mbr[0x1c5] = 0xff; + + /* start */ + + mbr[0x1c6] = 0x01; + mbr[0x1c7] = 0x00; + mbr[0x1c8] = 0x00; + mbr[0x1c9] = 0x00; + + memcpy(mbr + 0x1ca, &blocks, sizeof(uint32_t)); + + /* signature */ + + mbr[0x1fe] = 0x55; + mbr[0x1ff] = 0xaa; +} + +int add_ptn(FAR struct efi_ptable_s *ptbl, uint64_t first, uint64_t last, + FAR const char *name, FAR const uint8_t *type) +{ + FAR struct efi_header_s *hdr = &ptbl->header; + FAR struct efi_entry_s *entry = ptbl->entry; + int n; + + if (first < 34) + { + fprintf(stderr, "partition '%s' overlaps partition table\n", name); + return -1; + } + + if (last > hdr->last_lba) + { + fprintf(stderr, "partition '%s' does not fit on disk\n", name); + return -1; + } + + if (hdr->entries_count > EFI_ENTRIES) + { + fprintf(stderr, "out of partition table entries\n"); + return -1; + } + + memcpy(entry[hdr->entries_count].type_uuid, type, 16); + get_uuid(entry[hdr->entries_count].uniq_uuid); + entry[hdr->entries_count].first_lba = first; + entry[hdr->entries_count].last_lba = last; + entry[hdr->entries_count].attr = 1ull << 63; + for (n = 0; n < EFI_NAMELEN && *name; n++) + { + entry[hdr->entries_count].name[n] = *name++; + } + + hdr->entries_count++; + return 0; +} + +static int usage(void) +{ + fprintf(stderr, + "usage: mkgpt write <disk> [ <partition> ]*\n" + " mkgpt read <disk>\n" + "\n" + "partition: [<name>]:<size>[kmg][=linux|=swap|=efi]\n" + " or: @<file-of-partitions>\n" + ); + return 0; +} + +static void show(FAR struct efi_ptable_s *ptbl) +{ + FAR struct efi_entry_s *entry = ptbl->entry; + char name[EFI_NAMELEN + 1]; + unsigned n; + unsigned m; + + fprintf(stderr, "ptn start block end block name\n"); + fprintf(stderr, "---- ------------- ------------- --------------------\n"); + + for (n = 0; n < ptbl->header.entries_count; n++) + { + for (m = 0; m < EFI_NAMELEN; m++) + { + name[m] = entry[n].name[m] & 127; + } + + name[m] = 0; + fprintf(stderr, "#%03d %13" PRIu64 " %13" PRIu64" %s\n", + n + 1, entry[n].first_lba, entry[n].last_lba, name); + } +} + +static uint64_t parse_size(FAR const char *sz) +{ + size_t l = strlen(sz); + uint64_t n = strtoull(sz, 0, 0); + + if (l) + { + switch (sz[l - 1]) + { + case 'k': + case 'K': + n *= 1024; + break; + case 'm': + case 'M': + n *= 1024 * 1024; + break; + case 'g': + case 'G': + n *= 1024 * 1024 * 1024; + break; + } + } + + return n; +} + +static int parse_ptn(FAR struct efi_ptable_s *ptbl, FAR char *x, + FAR uint64_t *lba) +{ + FAR const uint8_t *type = g_partition_type_uuid; + FAR char *y = strchr(x, ':'); + FAR char *z; + uint64_t sz; + + if (!y) + { + fprintf(stderr, "invalid partition entry: %s\n", x); + return -1; + } + + *y++ = 0; + z = strchr(y, '='); + if (z) + { + *z++ = 0; + if (!strcmp(z, "efi")) + { + type = g_partition_type_efi; + } + else if (!strcmp(z, "linux")) + { + type = g_partition_type_linux; + } + else if (!strcmp(z, "swap")) + { + type = g_partition_type_swap; + } + } + + if (*y == '0') + { + sz = ptbl->header.last_lba - *lba; + } + else + { + sz = parse_size(y); + if (sz & 511) + { + fprintf(stderr, "partition size must be multiple of 512\n"); + return -1; + } + + sz /= 512; + } + + if (sz == 0) + { + fprintf(stderr, "zero size partitions not allowed\n"); + return -1; + } + + if (x[0] && add_ptn(ptbl, *lba, *lba + sz - 1, x, type)) + { + return -1; + } + + *lba += sz; + return 0; +} + +static void update_crc32(FAR struct efi_ptable_s *ptbl) +{ + uint32_t n; + + n = crc32part((FAR const uint8_t *)ptbl->entry, + sizeof(ptbl->entry[0]) * + ptbl->header.entries_count, ~0l) ^ ~0l; + ptbl->header.entries_crc32 = n; + + ptbl->header.crc32 = 0; + n = crc32part((FAR const uint8_t *)&ptbl->header, + sizeof(ptbl->header), ~0l) ^ ~0l; + ptbl->header.crc32 = n; +} + +static int verify_gpt_pratition(FAR struct efi_ptable_s *ptbl) +{ + FAR struct efi_header_s *hdr = &ptbl->header; + uint32_t orgcrc; + + if ((ptbl->mbr[0x1c2] != 0xee && ptbl->mbr[0x1c2] != 0x00) || + ptbl->mbr[0x1fe] != 0x55 || ptbl->mbr[0x1ff] != 0xaa) + { + fprintf(stderr, "Invalid Protective or Hybrid MBR(%x)\n", + ptbl->mbr[0x1c2]); + return -1; + } + + if (memcmp(hdr->magic, EFI_MAGIC, sizeof(hdr->magic))) + { + fprintf(stderr, "GPT: Header signature is wrong\n"); + return -1; + } + + orgcrc = hdr->crc32; + hdr->crc32 = 0; + if (orgcrc != (crc32part((FAR const uint8_t *)hdr, + sizeof(*hdr), ~0l) ^ ~0l)) + { + fprintf(stderr, "GPT: Header CRC is wrong\n"); + return -1; + } + + hdr->crc32 = orgcrc; + if (hdr->header_lba != 1) + { + fprintf(stderr, "GPT: Header starting lba incorrect\n"); + return -1; + } + + if (hdr->entries_crc32 != (crc32part((FAR const uint8_t *)ptbl->entry, + sizeof(ptbl->entry[0]) * ptbl->header.entries_count, ~0l) ^ ~0l)) + { + fprintf(stderr, "GPT: Header entry signature is wrong\n"); + return -1; + } + + show(ptbl); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char **argv) +{ + FAR struct efi_header_s *hdr; + FAR struct efi_ptable_s *ptbl; + FAR const char *device; + struct stat s; + uint64_t sz; + uint64_t lba; + uint64_t off; + int ret = -1; + int fd; + bool verify = false; + + if (argc < 2) + { + return usage(); + } + + device = argv[2]; + if (!strcmp(argv[1], "write") && argc >= 3) + { + argc -= 2; + argv += 2; + } + else if (!strcmp(argv[1], "read")) + { + verify = true; + } + else + { + return usage(); + } + + fd = open(device, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "error: cannot open '%s'\n", device); + return ret; + } + + if (fstat(fd, &s)) + { + fprintf(stderr, "error: cannot stat '%s'\n", device); + goto err; + } + + sz = s.st_size; + if (sz & 511) + { + fprintf(stderr, "error: file size not multiple of 512\n"); + goto err; + } + + sz /= 512; + ptbl = calloc(1, sizeof(*ptbl)); + if (!ptbl) + { + goto err; + } + + if (verify) + { + if (read(fd, ptbl, sizeof(*ptbl)) != sizeof(*ptbl)) + { + fprintf(stderr, "error read primary partition table\n"); + goto out; + } + + ret = verify_gpt_pratition(ptbl); + goto out; + } + + init_mbr(ptbl->mbr, sz - 1); + hdr = &ptbl->header; + memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic)); + hdr->version = EFI_VERSION; + hdr->header_sz = sizeof(struct efi_header_s); + hdr->header_lba = 1; + hdr->backup_lba = sz - 1; + hdr->first_lba = 34; + hdr->last_lba = sz - 33; + get_uuid(hdr->volume_uuid); + hdr->entries_lba = 2; + hdr->entries_count = 0; + hdr->entries_size = sizeof(struct efi_entry_s); + lba = hdr->first_lba; + + while (argc > 1) + { + if (argv[1][0] == '@') + { + FAR char *p; + FAR FILE *f; + char line[256]; + + f = fopen(argv[1] + 1, "r"); + if (!f) + { + fprintf(stderr, "cannot read partitions from '%s\n", argv[1]); + goto out; + } + + while (fgets(line, sizeof(line), f)) + { + p = line + strlen(line); + while (p > line) + { + if (*--p > ' ') + { + break; + } + + *p = 0; + } + + p = line; + while (*p && (*p <= ' ')) + { + p++; + } + + if (*p == '#') + { + continue; + } + + if (*p == 0) + { + continue; + } + + if (parse_ptn(ptbl, p, &lba)) + { + fclose(f); + goto out; + } + } + + fclose(f); + } + else + { + if (parse_ptn(ptbl, argv[1], &lba)) + { + goto out; + } + } + + argc--; + argv++; + } + + update_crc32(ptbl); + show(ptbl); + off = (sz - 33) * 512; + if (write(fd, ptbl, sizeof(*ptbl)) != sizeof(*ptbl)) + { + fprintf(stderr, "error writing primary partition table\n"); + goto out; + } + + if (lseek(fd, off, SEEK_SET) != off) + { + fprintf(stderr, "error seeking to secondary partition table\n"); + goto out; + } + + /* reverse these for the secondary copy */ + + hdr->header_lba = sz -1; + hdr->backup_lba = 1; + update_crc32(ptbl); + + if (write(fd, &ptbl->entry, sizeof(ptbl->entry)) != + sizeof(ptbl->entry)) + { + fprintf(stderr, "error writing secondary partition table\n"); + goto out; + } + + if (write(fd, &ptbl->header, 512) != 512) + { + fprintf(stderr, "error writing secondary partition header\n"); + goto out; + } + + fsync(fd); + +out: + free(ptbl); +err: + close(fd); + return ret; +}