From fb9cffefa185c1566efb99ee0116dee4a8527bc5 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 16 Jun 2007 22:29:27 +0000 Subject: [PATCH] Bring the gpt labeling program in from FreeBSD. Obtained-from: FreeBSD / Marcel Moolenaar --- sbin/Makefile | 3 +- sbin/gpt/Makefile | 10 + sbin/gpt/add.c | 217 ++++++++++++++ sbin/gpt/create.c | 245 ++++++++++++++++ sbin/gpt/destroy.c | 113 ++++++++ sbin/gpt/gpt.8 | 336 ++++++++++++++++++++++ sbin/gpt/gpt.c | 697 +++++++++++++++++++++++++++++++++++++++++++++ sbin/gpt/gpt.h | 88 ++++++ sbin/gpt/label.c | 246 ++++++++++++++++ sbin/gpt/map.c | 216 ++++++++++++++ sbin/gpt/map.h | 64 +++++ sbin/gpt/migrate.c | 365 ++++++++++++++++++++++++ sbin/gpt/recover.c | 178 ++++++++++++ sbin/gpt/remove.c | 207 ++++++++++++++ sbin/gpt/show.c | 212 ++++++++++++++ 15 files changed, 3196 insertions(+), 1 deletion(-) create mode 100644 sbin/gpt/Makefile create mode 100644 sbin/gpt/add.c create mode 100644 sbin/gpt/create.c create mode 100644 sbin/gpt/destroy.c create mode 100644 sbin/gpt/gpt.8 create mode 100644 sbin/gpt/gpt.c create mode 100644 sbin/gpt/gpt.h create mode 100644 sbin/gpt/label.c create mode 100644 sbin/gpt/map.c create mode 100644 sbin/gpt/map.h create mode 100644 sbin/gpt/migrate.c create mode 100644 sbin/gpt/recover.c create mode 100644 sbin/gpt/remove.c create mode 100644 sbin/gpt/show.c diff --git a/sbin/Makefile b/sbin/Makefile index 9417fac27e..c7a0a171bb 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.5 (Berkeley) 3/31/94 # $FreeBSD: src/sbin/Makefile,v 1.77.2.9 2002/08/08 09:03:46 ru Exp $ -# $DragonFly: src/sbin/Makefile,v 1.15 2007/05/15 17:53:10 dillon Exp $ +# $DragonFly: src/sbin/Makefile,v 1.16 2007/06/16 22:29:25 dillon Exp $ # # XXX MISSING: icheck ncheck @@ -25,6 +25,7 @@ SUBDIR= adjkerntz \ fsdb \ fsirand \ growfs \ + gpt \ ifconfig \ init \ ip6fw \ diff --git a/sbin/gpt/Makefile b/sbin/gpt/Makefile new file mode 100644 index 0000000000..d367eda296 --- /dev/null +++ b/sbin/gpt/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD: src/sbin/gpt/Makefile,v 1.7 2005/09/01 02:49:20 marcel Exp $ +# $DragonFly: src/sbin/gpt/Makefile,v 1.1 2007/06/16 22:29:27 dillon Exp $ + +PROG= gpt +SRCS= add.c create.c destroy.c gpt.c label.c map.c migrate.c recover.c \ + remove.c show.c +WARNS?= 4 +MAN= gpt.8 + +.include diff --git a/sbin/gpt/add.c b/sbin/gpt/add.c new file mode 100644 index 0000000000..75be6a21eb --- /dev/null +++ b/sbin/gpt/add.c @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/add.c,v 1.15 2006/10/04 18:20:25 marcel Exp $ + * $DragonFly: src/sbin/gpt/add.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static uuid_t type; +static off_t block, size; +static unsigned int entry; + +static void +usage_add(void) +{ + + fprintf(stderr, + "usage: %s [-b lba] [-i index] [-s lba] [-t uuid] device ...\n", + getprogname()); + exit(1); +} + +static void +add(int fd) +{ + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *map; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + unsigned int i; + + gpt = map_find(MAP_TYPE_PRI_GPT_HDR); + if (gpt == NULL) { + warnx("%s: error: no primary GPT header; run create or recover", + device_name); + return; + } + + tpg = map_find(MAP_TYPE_SEC_GPT_HDR); + if (tpg == NULL) { + warnx("%s: error: no secondary GPT header; run recover", + device_name); + return; + } + + tbl = map_find(MAP_TYPE_PRI_GPT_TBL); + lbt = map_find(MAP_TYPE_SEC_GPT_TBL); + if (tbl == NULL || lbt == NULL) { + warnx("%s: error: run recover -- trust me", device_name); + return; + } + + hdr = gpt->map_data; + if (entry > le32toh(hdr->hdr_entries)) { + warnx("%s: error: index %u out of range (%u max)", device_name, + entry, le32toh(hdr->hdr_entries)); + return; + } + + if (entry > 0) { + i = entry - 1; + ent = (void*)((char*)tbl->map_data + i * + le32toh(hdr->hdr_entsz)); + if (!uuid_is_nil(&ent->ent_type, NULL)) { + warnx("%s: error: entry at index %u is not free", + device_name, entry); + return; + } + } else { + /* Find empty slot in GPT table. */ + for (i = 0; i < le32toh(hdr->hdr_entries); i++) { + ent = (void*)((char*)tbl->map_data + i * + le32toh(hdr->hdr_entsz)); + if (uuid_is_nil(&ent->ent_type, NULL)) + break; + } + if (i == le32toh(hdr->hdr_entries)) { + warnx("%s: error: no available table entries", + device_name); + return; + } + } + + map = map_alloc(block, size); + if (map == NULL) { + warnx("%s: error: no space available on device", device_name); + return; + } + + le_uuid_enc(&ent->ent_type, &type); + ent->ent_lba_start = htole64(map->map_start); + ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL); + + hdr->hdr_crc_table = htole32(crc32(tbl->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, gpt); + gpt_write(fd, tbl); + + hdr = tpg->map_data; + ent = (void*)((char*)lbt->map_data + i * le32toh(hdr->hdr_entsz)); + + le_uuid_enc(&ent->ent_type, &type); + ent->ent_lba_start = htole64(map->map_start); + ent->ent_lba_end = htole64(map->map_start + map->map_size - 1LL); + + hdr->hdr_crc_table = htole32(crc32(lbt->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, lbt); + gpt_write(fd, tpg); + + printf("%sp%u added\n", device_name, i + 1); +} + +int +cmd_add(int argc, char *argv[]) +{ + char *p; + int ch, fd; + + /* Get the migrate options */ + while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) { + switch(ch) { + case 'b': + if (block > 0) + usage_add(); + block = strtoll(optarg, &p, 10); + if (*p != 0 || block < 1) + usage_add(); + break; + case 'i': + if (entry > 0) + usage_add(); + entry = strtol(optarg, &p, 10); + if (*p != 0 || entry < 1) + usage_add(); + break; + case 's': + if (size > 0) + usage_add(); + size = strtoll(optarg, &p, 10); + if (*p != 0 || size < 1) + usage_add(); + break; + case 't': + if (!uuid_is_nil(&type, NULL)) + usage_add(); + if (parse_uuid(optarg, &type) != 0) + usage_add(); + break; + default: + usage_add(); + } + } + + if (argc == optind) + usage_add(); + + /* Create UFS partitions by default. */ + if (uuid_is_nil(&type, NULL)) { + uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS; + type = ufs; + } + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + add(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/create.c b/sbin/gpt/create.c new file mode 100644 index 0000000000..a7e6242e1a --- /dev/null +++ b/sbin/gpt/create.c @@ -0,0 +1,245 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/create.c,v 1.11 2005/08/31 01:47:19 marcel Exp $ + * $DragonFly: src/sbin/gpt/create.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int force; +static int primary_only; + +static void +usage_create(void) +{ + + fprintf(stderr, + "usage: %s [-fp] device ...\n", getprogname()); + exit(1); +} + +static void +create(int fd) +{ + uuid_t uuid; + off_t blocks, last; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *map; + struct mbr *mbr; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + unsigned int i; + + last = mediasz / secsz - 1LL; + + if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL || + map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) { + warnx("%s: error: device already contains a GPT", device_name); + return; + } + map = map_find(MAP_TYPE_MBR); + if (map != NULL) { + if (!force) { + warnx("%s: error: device contains a MBR", device_name); + return; + } + + /* Nuke the MBR in our internal map. */ + map->map_type = MAP_TYPE_UNUSED; + } + + /* + * Create PMBR. + */ + if (map_find(MAP_TYPE_PMBR) == NULL) { + if (map_free(0LL, 1LL) == 0) { + warnx("%s: error: no room for the PMBR", device_name); + return; + } + mbr = gpt_read(fd, 0LL, 1); + bzero(mbr, sizeof(*mbr)); + mbr->mbr_sig = htole16(MBR_SIG); + mbr->mbr_part[0].part_shd = 0xff; + mbr->mbr_part[0].part_ssect = 0xff; + mbr->mbr_part[0].part_scyl = 0xff; + mbr->mbr_part[0].part_typ = 0xee; + mbr->mbr_part[0].part_ehd = 0xff; + mbr->mbr_part[0].part_esect = 0xff; + mbr->mbr_part[0].part_ecyl = 0xff; + mbr->mbr_part[0].part_start_lo = htole16(1); + if (last > 0xffffffff) { + mbr->mbr_part[0].part_size_lo = htole16(0xffff); + mbr->mbr_part[0].part_size_hi = htole16(0xffff); + } else { + mbr->mbr_part[0].part_size_lo = htole16(last); + mbr->mbr_part[0].part_size_hi = htole16(last >> 16); + } + map = map_add(0LL, 1LL, MAP_TYPE_PMBR, mbr); + gpt_write(fd, map); + } + + /* Get the amount of free space after the MBR */ + blocks = map_free(1LL, 0LL); + if (blocks == 0LL) { + warnx("%s: error: no room for the GPT header", device_name); + return; + } + + /* Don't create more than parts entries. */ + if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) { + blocks = (parts * sizeof(struct gpt_ent)) / secsz; + if ((parts * sizeof(struct gpt_ent)) % secsz) + blocks++; + blocks++; /* Don't forget the header itself */ + } + + /* Never cross the median of the device. */ + if ((blocks + 1LL) > ((last + 1LL) >> 1)) + blocks = ((last + 1LL) >> 1) - 1LL; + + /* + * Get the amount of free space at the end of the device and + * calculate the size for the GPT structures. + */ + map = map_last(); + if (map->map_type != MAP_TYPE_UNUSED) { + warnx("%s: error: no room for the backup header", device_name); + return; + } + + if (map->map_size < blocks) + blocks = map->map_size; + if (blocks == 1LL) { + warnx("%s: error: no room for the GPT table", device_name); + return; + } + + blocks--; /* Number of blocks in the GPT table. */ + gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz)); + tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL, + calloc(blocks, secsz)); + if (gpt == NULL || tbl == NULL) + return; + + hdr = gpt->map_data; + memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); + hdr->hdr_revision = htole32(GPT_HDR_REVISION); + /* + * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus + * contains padding we must not include in the size. + */ + hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding)); + hdr->hdr_lba_self = htole64(gpt->map_start); + hdr->hdr_lba_alt = htole64(last); + hdr->hdr_lba_start = htole64(tbl->map_start + blocks); + hdr->hdr_lba_end = htole64(last - blocks - 1LL); + uuid_create(&uuid, NULL); + le_uuid_enc(&hdr->hdr_uuid, &uuid); + hdr->hdr_lba_table = htole64(tbl->map_start); + hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent)); + if (le32toh(hdr->hdr_entries) > parts) + hdr->hdr_entries = htole32(parts); + hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); + + ent = tbl->map_data; + for (i = 0; i < le32toh(hdr->hdr_entries); i++) { + uuid_create(&uuid, NULL); + le_uuid_enc(&ent[i].ent_uuid, &uuid); + } + + hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) * + le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, gpt); + gpt_write(fd, tbl); + + /* + * Create backup GPT if the user didn't suppress it. + */ + if (!primary_only) { + tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, + calloc(1, secsz)); + lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL, + tbl->map_data); + memcpy(tpg->map_data, gpt->map_data, secsz); + hdr = tpg->map_data; + hdr->hdr_lba_self = htole64(tpg->map_start); + hdr->hdr_lba_alt = htole64(gpt->map_start); + hdr->hdr_lba_table = htole64(lbt->map_start); + hdr->hdr_crc_self = 0; /* Don't ever forget this! */ + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + gpt_write(fd, lbt); + gpt_write(fd, tpg); + } +} + +int +cmd_create(int argc, char *argv[]) +{ + int ch, fd; + + while ((ch = getopt(argc, argv, "fp")) != -1) { + switch(ch) { + case 'f': + force = 1; + break; + case 'p': + primary_only = 1; + break; + default: + usage_create(); + } + } + + if (argc == optind) + usage_create(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + create(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/destroy.c b/sbin/gpt/destroy.c new file mode 100644 index 0000000000..b7fae7363d --- /dev/null +++ b/sbin/gpt/destroy.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/destroy.c,v 1.6 2005/08/31 01:47:19 marcel Exp $ + * $DragonFly: src/sbin/gpt/destroy.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int recoverable; + +static void +usage_destroy(void) +{ + + fprintf(stderr, + "usage: %s [-r] device ...\n", getprogname()); + exit(1); +} + +static void +destroy(int fd) +{ + map_t *pri_hdr, *sec_hdr; + + pri_hdr = map_find(MAP_TYPE_PRI_GPT_HDR); + sec_hdr = map_find(MAP_TYPE_SEC_GPT_HDR); + + if (pri_hdr == NULL && sec_hdr == NULL) { + warnx("%s: error: device doesn't contain a GPT", device_name); + return; + } + + if (recoverable && sec_hdr == NULL) { + warnx("%s: error: recoverability not possible", device_name); + return; + } + + if (pri_hdr != NULL) { + bzero(pri_hdr->map_data, secsz); + gpt_write(fd, pri_hdr); + } + + if (!recoverable && sec_hdr != NULL) { + bzero(sec_hdr->map_data, secsz); + gpt_write(fd, sec_hdr); + } +} + +int +cmd_destroy(int argc, char *argv[]) +{ + int ch, fd; + + while ((ch = getopt(argc, argv, "r")) != -1) { + switch(ch) { + case 'r': + recoverable = 1; + break; + default: + usage_destroy(); + } + } + + if (argc == optind) + usage_destroy(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + destroy(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/gpt.8 b/sbin/gpt/gpt.8 new file mode 100644 index 0000000000..fa2499a00c --- /dev/null +++ b/sbin/gpt/gpt.8 @@ -0,0 +1,336 @@ +.\" Copyright (c) 2002 Marcel Moolenaar +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/sbin/gpt/gpt.8,v 1.17 2006/06/22 22:22:32 marcel Exp $ +.\" $DragonFly: src/sbin/gpt/gpt.8,v 1.1 2007/06/16 22:29:27 dillon Exp $ +.\" +.Dd June 22, 2006 +.Os +.Dt GPT 8 +.Sh NAME +.Nm gpt +.Nd "GUID partition table maintenance utility" +.Sh SYNOPSIS +.Nm +.Op Ar general_options +.Ar command +.Op Ar command_options +.Ar device ... +.Sh DESCRIPTION +The +.Nm +utility provides the necessary functionality to manipulate GUID partition +tables (GPTs), but see +.Sx BUGS +below for how and where functionality is missing. +The basic usage model of the +.Nm +tool follows that of the +.Xr cvs 1 +tool. +The general options are described in the following paragraph. +The remaining paragraphs describe the individual commands with their options. +Here we conclude by mentioning that a +.Ar device +is either a special file +corresponding to a disk-like device or a regular file. +The command is applied to each +.Ar device +listed on the command line. +.Ss General Options +The general options allow the user to change default settings or otherwise +change the behaviour that is applicable to all commands. +Not all commands use all default settings, so some general options may not +have an effect on all commands. +.Pp +The +.Fl p Ar count +option allows the user to change the number of partitions the GPT can +accomodate. +This is used whenever a new GPT is created. +By default, the +.Nm +utility will create space for 128 partitions (or 32 sectors of 512 bytes). +.Pp +The +.Fl r +option causes the +.Nm +utility to open the device for reading only. +Currently this option is primarily useful for the +.Ic show +command, but the intent +is to use it to implement dry-run behaviour. +.Pp +The +.Fl v +option controls the verbosity level. +The level increases with every occurrence of this option. +There is no formalized definition of the different levels yet. +.Ss Commands +.Bl -tag -width indent +.\" ==== add ==== +.It Xo +.Nm +.Ic add +.Op Fl b Ar number +.Op Fl i Ar index +.Op Fl s Ar count +.Op Fl t Ar type +.Ar device ... +.Xc +The +.Ic add +command allows the user to add a new partition to an existing table. +By default, it will create a UFS partition covering the first available block +of an unused disk space. +The command-specific options can be used to control this behaviour. +.Pp +The +.Fl b Ar number +option allows the user to specify the starting (beginning) sector number of +the partition. +The minimum sector number is 1, but has to fall inside an unused region of +disk space that is covered by the GPT. +.Pp +The +.Fl i Ar index +option allows the user to specify which (free) entry in the GPT table is to +be used for the new partition. +By default, the first free entry is selected. +.Pp +The +.Fl s Ar count +option allows the user to specify the size of the partition in sectors. +The minimum size is 1. +.Pp +The +.Fl t Ar type +option allows the user to specify the partition type. +The type is given as an UUID, but +.Nm +accepts +.Cm efi , swap , ufs , hfs , linux +and +.Cm windows +as aliases for the most commonly used partition types. +.\" ==== create ==== +.It Nm Ic create Oo Fl fp Oc Ar device ... +The +.Ic create +command allows the user to create a new (empty) GPT. +By default, one cannot create a GPT when the device contains a MBR, +however this can be overridden with the +.Fl f +option. +If the +.Fl f +option is specified, an existing MBR is destroyed and any partitions +described by the MBR are lost. +.Pp +The +.Fl p +option tells +.Nm +to create only the primary table and not the backup table. +This option is only useful for debugging and should not be used otherwise. +.\" ==== destroy ==== +.It Nm Ic destroy Oo Fl r Oc Ar device ... +The +.Ic destroy +command allows the user to destroy an existing, possibly not empty GPT. +.Pp +The +.Fl r +option instructs +.Nm +to destroy the table in a way that it can be recovered. +.\" ==== label ==== +.It Xo +.Nm +.Ic label +.Op Fl a +.Aq Fl f Ar file | Fl l Ar label +.Ar device ... +.Xc +.It Xo +.Nm +.Ic label +.Op Fl b Ar number +.Op Fl i Ar index +.Op Fl s Ar count +.Op Fl t Ar type +.Aq Fl f Ar file | Fl l Ar label +.Ar device ... +.Xc +The +.Ic label +command allows the user to label any partitions that match the selection. +At least one of the following selection options must be specified. +.Pp +The +.Fl a +option specifies that all partitions should be labeled. +It is mutually exclusive with all other selection options. +.Pp +The +.Fl b Ar number +option selects the partition that starts at the given block number. +.Pp +The +.Fl i Ar index +option selects the partition with the given partition number. +.Pp +The +.Fl s Ar count +option selects all partitions that have the given size. +This can cause multiple partitions to be removed. +.Pp +The +.Fl t Ar type +option selects all partitions that have the given type. +The type is given as an UUID or by the aliases that the +.Ic add +command accepts. +This can cause multiple partitions to be removed. +.Pp +The +.Fl f Ar file +or +.Fl l Ar label +options specify the new label to be assigned to the selected partitions. +The +.Fl f Ar file +option is used to read the label from the specified file. +Only the first line is read from the file and the trailing newline +character is stripped. +If the file name is the dash or minus sign +.Pq Fl , +the label is read from +the standard input. +The +.Fl l Ar label +option is used to specify the label in the command line. +The label is assumed to be encoded in UTF-8. +.\" ==== migrate ==== +.It Nm Ic migrate Oo Fl fs Oc Ar device ... +The +.Ic migrate +command allows the user to migrate an MBR-based disk partitioning into a +GPT-based partitioning. +By default, the MBR is not migrated when it contains partitions of an unknown +type. +This can be overridden with the +.Fl f +option. +Specifying the +.Fl f +option will cause unknown partitions to be ignored and any data in it +to be lost. +.Pp +The +.Fl s +option prevents migrating +.Bx +disk labels into GPT partitions by creating +the GPT equivalent of a slice. +.\" ==== remove ==== +.It Nm Ic remove Oo Fl a Oc Ar device ... +.It Xo +.Nm +.Ic remove +.Op Fl b Ar number +.Op Fl i Ar index +.Op Fl s Ar count +.Op Fl t Ar type +.Ar device ... +.Xc +The +.Ic remove +command allows the user to remove any and all partitions that match the +selection. +It uses the same selection options as the +.Ic label +command. +See above for a description of these options. +Partitions are removed by clearing the partition type. +No other information is changed. +.\" ==== show ==== +.It Nm Ic show Oo Fl lu Oc Ar device ... +The +.Ic show +command displays the current partitioning on the listed devices and gives +an overall view of the disk contents. +With the +.Fl l +option the GPT partition label will be displayed instead of the GPT partition +type. +The option has no effect on non-GPT partitions. +With the +.Fl u +option the GPT partition type is displayed as an UUID instead of in a +user friendly form. +The +.Fl l +option takes precedence over the +.Fl u +option. +.El +.Sh SEE ALSO +.Xr fdisk 8 , +.Xr mount 8 , +.Xr newfs 8 , +.Xr swapon 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 5.0 +for ia64. +.Sh BUGS +The development of the +.Nm +utility is still work in progress. +Many necessary features are missing or partially implemented. +In practice this means that the manual page, supposed to describe these +features, is farther removed from being complete or useful. +As such, missing functionality is not even documented as missing. +However, it is believed that the currently present functionality is reliable +and stable enough that this tool can be used without bullet-proof footware if +one thinks one does not make mistakes. +.Pp +It is expected that the basic usage model does not change, but it is +possible that future versions will not be compatible in the strictest sense +of the word. +For example, the +.Fl p Ar count +option may be changed to a command option rather than a generic option. +There are only two commands that use it so there is a chance that the natural +tendency for people is to use it as a command option. +Also, options primarily intended for diagnostic or debug purposes may be +removed in future versions. +.Pp +Another possibility is that the current usage model is accompanied by +other interfaces to make the tool usable as a back-end. +This all depends on demand and thus feedback. diff --git a/sbin/gpt/gpt.c b/sbin/gpt/gpt.c new file mode 100644 index 0000000000..ca72cd3c21 --- /dev/null +++ b/sbin/gpt/gpt.c @@ -0,0 +1,697 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * CRC32 code derived from work by Gary S. Brown. + * + * $FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $ + * $DragonFly: src/sbin/gpt/gpt.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +char device_path[MAXPATHLEN]; +char *device_name; + +off_t mediasz; + +u_int parts; +u_int secsz; + +int readonly, verbose; + +static uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t +crc32(const void *buf, size_t size) +{ + const uint8_t *p; + uint32_t crc; + + p = buf; + crc = ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} + +uint8_t * +utf16_to_utf8(uint16_t *s16) +{ + static uint8_t *s8 = NULL; + static size_t s8len = 0; + size_t s8idx, s16idx, s16len; + uint32_t utfchar; + unsigned int c; + + s16len = 0; + while (s16[s16len++] != 0) + ; + if (s8len < s16len * 3) { + if (s8 != NULL) + free(s8); + s8len = s16len * 3; + s8 = calloc(s16len, 3); + } + s8idx = s16idx = 0; + while (s16idx < s16len) { + utfchar = le16toh(s16[s16idx++]); + if ((utfchar & 0xf800) == 0xd800) { + c = le16toh(s16[s16idx]); + if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00) + utfchar = 0xfffd; + else + s16idx++; + } + if (utfchar < 0x80) { + s8[s8idx++] = utfchar; + } else if (utfchar < 0x800) { + s8[s8idx++] = 0xc0 | (utfchar >> 6); + s8[s8idx++] = 0x80 | (utfchar & 0x3f); + } else if (utfchar < 0x10000) { + s8[s8idx++] = 0xe0 | (utfchar >> 12); + s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); + s8[s8idx++] = 0x80 | (utfchar & 0x3f); + } else if (utfchar < 0x200000) { + s8[s8idx++] = 0xf0 | (utfchar >> 18); + s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f); + s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f); + s8[s8idx++] = 0x80 | (utfchar & 0x3f); + } + } + return (s8); +} + +void +utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) +{ + size_t s16idx, s8idx, s8len; + uint32_t utfchar; + unsigned int c, utfbytes; + + s8len = 0; + while (s8[s8len++] != 0) + ; + s8idx = s16idx = 0; + utfbytes = 0; + do { + utfchar = 0; + c = s8[s8idx++]; + if ((c & 0xc0) != 0x80) { + /* Initial characters. */ + if (utfbytes != 0) { + /* Incomplete encoding. */ + s16[s16idx++] = 0xfffd; + if (s16idx == s16len) { + s16[--s16idx] = 0; + return; + } + } + if ((c & 0xf8) == 0xf0) { + utfchar = c & 0x07; + utfbytes = 3; + } else if ((c & 0xf0) == 0xe0) { + utfchar = c & 0x0f; + utfbytes = 2; + } else if ((c & 0xe0) == 0xc0) { + utfchar = c & 0x1f; + utfbytes = 1; + } else { + utfchar = c & 0x7f; + utfbytes = 0; + } + } else { + /* Followup characters. */ + if (utfbytes > 0) { + utfchar = (utfchar << 6) + (c & 0x3f); + utfbytes--; + } else if (utfbytes == 0) + utfbytes = -1; + } + if (utfbytes == 0) { + if (utfchar >= 0x10000 && s16idx + 2 >= s16len) + utfchar = 0xfffd; + if (utfchar >= 0x10000) { + s16[s16idx++] = 0xd800 | ((utfchar>>10)-0x40); + s16[s16idx++] = 0xdc00 | (utfchar & 0x3ff); + } else + s16[s16idx++] = utfchar; + if (s16idx == s16len) { + s16[--s16idx] = 0; + return; + } + } + } while (c != 0); +} + +void +le_uuid_dec(void const *buf, uuid_t *uuid) +{ + u_char const *p; + int i; + + p = buf; + uuid->time_low = le32dec(p); + uuid->time_mid = le16dec(p + 4); + uuid->time_hi_and_version = le16dec(p + 6); + uuid->clock_seq_hi_and_reserved = p[8]; + uuid->clock_seq_low = p[9]; + for (i = 0; i < _UUID_NODE_LEN; i++) + uuid->node[i] = p[10 + i]; +} + +void +le_uuid_enc(void *buf, uuid_t const *uuid) +{ + u_char *p; + int i; + + p = buf; + le32enc(p, uuid->time_low); + le16enc(p + 4, uuid->time_mid); + le16enc(p + 6, uuid->time_hi_and_version); + p[8] = uuid->clock_seq_hi_and_reserved; + p[9] = uuid->clock_seq_low; + for (i = 0; i < _UUID_NODE_LEN; i++) + p[10 + i] = uuid->node[i]; +} + +int +parse_uuid(const char *s, uuid_t *uuid) +{ + uint32_t status; + + uuid_from_string(s, uuid, &status); + if (status == uuid_s_ok) + return (0); + + switch (*s) { + case 'e': + if (strcmp(s, "efi") == 0) { + uuid_t efi = GPT_ENT_TYPE_EFI; + *uuid = efi; + return (0); + } + break; + case 'h': + if (strcmp(s, "hfs") == 0) { + uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS; + *uuid = hfs; + return (0); + } + break; + case 'l': + if (strcmp(s, "linux") == 0) { + uuid_t lnx = GPT_ENT_TYPE_MS_BASIC_DATA; + *uuid = lnx; + return (0); + } + break; + case 's': + if (strcmp(s, "swap") == 0) { + uuid_t sw = GPT_ENT_TYPE_FREEBSD_SWAP; + *uuid = sw; + return (0); + } + break; + case 'u': + if (strcmp(s, "ufs") == 0) { + uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS; + *uuid = ufs; + return (0); + } + break; + case 'w': + if (strcmp(s, "windows") == 0) { + uuid_t win = GPT_ENT_TYPE_MS_BASIC_DATA; + *uuid = win; + return (0); + } + break; + } + return (EINVAL); +} + +void* +gpt_read(int fd, off_t lba, size_t count) +{ + off_t ofs; + void *buf; + + count *= secsz; + buf = malloc(count); + if (buf == NULL) + return (NULL); + + ofs = lba * secsz; + if (lseek(fd, ofs, SEEK_SET) == ofs && + read(fd, buf, count) == (ssize_t)count) + return (buf); + + free(buf); + return (NULL); +} + +int +gpt_write(int fd, map_t *map) +{ + off_t ofs; + size_t count; + + count = map->map_size * secsz; + ofs = map->map_start * secsz; + if (lseek(fd, ofs, SEEK_SET) == ofs && + write(fd, map->map_data, count) == (ssize_t)count) + return (0); + return (-1); +} + +static int +gpt_mbr(int fd, off_t lba) +{ + struct mbr *mbr; + map_t *m, *p; + off_t size, start; + unsigned int i, pmbr; + + mbr = gpt_read(fd, lba, 1); + if (mbr == NULL) + return (-1); + + if (mbr->mbr_sig != htole16(MBR_SIG)) { + if (verbose) + warnx("%s: MBR not found at sector %llu", device_name, + (long long)lba); + free(mbr); + return (0); + } + + /* + * Differentiate between a regular MBR and a PMBR. This is more + * convenient in general. A PMBR is one with a single partition + * of type 0xee. + */ + pmbr = 0; + for (i = 0; i < 4; i++) { + if (mbr->mbr_part[i].part_typ == 0) + continue; + if (mbr->mbr_part[i].part_typ == 0xee) + pmbr++; + else + break; + } + if (pmbr && i == 4 && lba == 0) { + if (pmbr != 1) + warnx("%s: Suspicious PMBR at sector %llu", + device_name, (long long)lba); + else if (verbose > 1) + warnx("%s: PMBR at sector %llu", device_name, + (long long)lba); + p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr); + return ((p == NULL) ? -1 : 0); + } + if (pmbr) + warnx("%s: Suspicious MBR at sector %llu", device_name, + (long long)lba); + else if (verbose > 1) + warnx("%s: MBR at sector %llu", device_name, (long long)lba); + + p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr); + if (p == NULL) + return (-1); + for (i = 0; i < 4; i++) { + if (mbr->mbr_part[i].part_typ == 0 || + mbr->mbr_part[i].part_typ == 0xee) + continue; + start = le16toh(mbr->mbr_part[i].part_start_hi); + start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo); + size = le16toh(mbr->mbr_part[i].part_size_hi); + size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); + if (start == 0 && size == 0) { + warnx("%s: Malformed MBR at sector %llu", device_name, + (long long)lba); + continue; + } + /* start is relative to the offset of the MBR itself. */ + start += lba; + if (verbose > 2) + warnx("%s: MBR part: type=%d, start=%llu, size=%llu", + device_name, mbr->mbr_part[i].part_typ, + (long long)start, (long long)size); + if (mbr->mbr_part[i].part_typ != 15) { + m = map_add(start, size, MAP_TYPE_MBR_PART, p); + if (m == NULL) + return (-1); + m->map_index = i + 1; + } else { + if (gpt_mbr(fd, start) == -1) + return (-1); + } + } + return (0); +} + +static int +gpt_gpt(int fd, off_t lba) +{ + uuid_t type; + off_t size; + struct gpt_ent *ent; + struct gpt_hdr *hdr; + char *p, *s; + map_t *m; + size_t blocks, tblsz; + unsigned int i; + uint32_t crc; + + hdr = gpt_read(fd, lba, 1); + if (hdr == NULL) + return (-1); + + if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig))) + goto fail_hdr; + + crc = le32toh(hdr->hdr_crc_self); + hdr->hdr_crc_self = 0; + if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) { + if (verbose) + warnx("%s: Bad CRC in GPT header at sector %llu", + device_name, (long long)lba); + goto fail_hdr; + } + + tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz); + blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0); + + /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */ + p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks); + if (p == NULL) + return (-1); + + if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) { + if (verbose) + warnx("%s: Bad CRC in GPT table at sector %llu", + device_name, + (long long)le64toh(hdr->hdr_lba_table)); + goto fail_ent; + } + + if (verbose > 1) + warnx("%s: %s GPT at sector %llu", device_name, + (lba == 1) ? "Pri" : "Sec", (long long)lba); + + m = map_add(lba, 1, (lba == 1) + ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr); + if (m == NULL) + return (-1); + + m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1) + ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p); + if (m == NULL) + return (-1); + + if (lba != 1) + return (0); + + for (i = 0; i < le32toh(hdr->hdr_entries); i++) { + ent = (void*)(p + i * le32toh(hdr->hdr_entsz)); + if (uuid_is_nil(&ent->ent_type, NULL)) + continue; + + size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) + + 1LL; + if (verbose > 2) { + le_uuid_dec(&ent->ent_type, &type); + uuid_to_string(&type, &s, NULL); + warnx( + "%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s, + (long long)le64toh(ent->ent_lba_start), + (long long)size); + free(s); + } + m = map_add(le64toh(ent->ent_lba_start), size, + MAP_TYPE_GPT_PART, ent); + if (m == NULL) + return (-1); + m->map_index = i + 1; + } + return (0); + + fail_ent: + free(p); + + fail_hdr: + free(hdr); + return (0); +} + +int +gpt_open(const char *dev) +{ + struct stat sb; + int fd, mode; + + mode = readonly ? O_RDONLY : O_RDWR|O_EXCL; + + strlcpy(device_path, dev, sizeof(device_path)); + device_name = device_path; + + if ((fd = open(device_path, mode)) != -1) + goto found; + + snprintf(device_path, sizeof(device_path), "%s%s", _PATH_DEV, dev); + device_name = device_path + strlen(_PATH_DEV); + if ((fd = open(device_path, mode)) != -1) + goto found; + + return (-1); + + found: + if (fstat(fd, &sb) == -1) + goto close; + + if ((sb.st_mode & S_IFMT) != S_IFREG) { + struct partinfo partinfo; + + if (ioctl(fd, DIOCGPART, &partinfo) < 0) + goto close; + secsz = partinfo.media_blksize; + mediasz = partinfo.media_size; + } else { + secsz = 512; /* Fixed size for files. */ + if (sb.st_size % secsz) { + errno = EINVAL; + goto close; + } + mediasz = sb.st_size; + } + + /* + * We require an absolute minimum of 6 sectors. One for the MBR, + * 2 for the GPT header, 2 for the GPT table and one to hold some + * user data. Let's catch this extreme border case here so that + * we don't have to worry about it later. + */ + if (mediasz / secsz < 6) { + errno = ENODEV; + goto close; + } + + if (verbose) + warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu", + device_name, (long long)mediasz, secsz, + (long long)(mediasz / secsz)); + + map_init(mediasz / secsz); + + if (gpt_mbr(fd, 0LL) == -1) + goto close; + if (gpt_gpt(fd, 1LL) == -1) + goto close; + if (gpt_gpt(fd, mediasz / secsz - 1LL) == -1) + goto close; + + return (fd); + + close: + close(fd); + return (-1); +} + +void +gpt_close(int fd) +{ + /* XXX post processing? */ + close(fd); +} + +static struct { + int (*fptr)(int, char *[]); + const char *name; +} cmdsw[] = { + { cmd_add, "add" }, + { cmd_create, "create" }, + { cmd_destroy, "destroy" }, + { NULL, "help" }, + { cmd_label, "label" }, + { cmd_migrate, "migrate" }, + { cmd_recover, "recover" }, + { cmd_remove, "remove" }, + { NULL, "rename" }, + { cmd_show, "show" }, + { NULL, "verify" }, + { NULL, NULL } +}; + +static void +usage(void) +{ + const char *prgname = getprogname(); + + fprintf(stderr, + "usage: %s [-rv] [-p nparts] [options] ...\n" + " %s show \n", + prgname, prgname); + exit(1); +} + +static void +prefix(const char *cmd) +{ + char *pfx; + const char *prg; + + prg = getprogname(); + pfx = malloc(strlen(prg) + strlen(cmd) + 2); + /* Don't bother failing. It's not important */ + if (pfx == NULL) + return; + + sprintf(pfx, "%s %s", prg, cmd); + setprogname(pfx); +} + +int +main(int argc, char *argv[]) +{ + char *cmd, *p; + int ch, i; + + /* Get the generic options */ + while ((ch = getopt(argc, argv, "p:rv")) != -1) { + switch(ch) { + case 'p': + if (parts > 0) + usage(); + parts = strtol(optarg, &p, 10); + if (*p != 0 || parts < 1) + usage(); + break; + case 'r': + readonly = 1; + break; + case 'v': + verbose++; + break; + default: + usage(); + } + } + if (!parts) + parts = 128; + + if (argc == optind) + usage(); + + cmd = argv[optind++]; + for (i = 0; cmdsw[i].name != NULL && strcmp(cmd, cmdsw[i].name); i++); + + if (cmdsw[i].fptr == NULL) + errx(1, "unknown command: %s", cmd); + + prefix(cmd); + return ((*cmdsw[i].fptr)(argc, argv)); +} diff --git a/sbin/gpt/gpt.h b/sbin/gpt/gpt.h new file mode 100644 index 0000000000..2cc6f43176 --- /dev/null +++ b/sbin/gpt/gpt.h @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/gpt.h,v 1.11 2006/06/22 22:05:28 marcel Exp $ + * $DragonFly: src/sbin/gpt/gpt.h,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#ifndef _GPT_H_ +#define _GPT_H_ + +#include +#include + +#include + +void le_uuid_dec(void const *, uuid_t *); +void le_uuid_enc(void *, uuid_t const *); +int parse_uuid(const char *, uuid_t *); + +struct mbr_part { + uint8_t part_flag; /* bootstrap flags */ + uint8_t part_shd; /* starting head */ + uint8_t part_ssect; /* starting sector */ + uint8_t part_scyl; /* starting cylinder */ + uint8_t part_typ; /* partition type */ + uint8_t part_ehd; /* end head */ + uint8_t part_esect; /* end sector */ + uint8_t part_ecyl; /* end cylinder */ + uint16_t part_start_lo; /* absolute starting ... */ + uint16_t part_start_hi; /* ... sector number */ + uint16_t part_size_lo; /* partition size ... */ + uint16_t part_size_hi; /* ... in sectors */ +}; + +struct mbr { + uint16_t mbr_code[223]; + struct mbr_part mbr_part[4]; + uint16_t mbr_sig; +#define MBR_SIG 0xAA55 +}; + +extern char *device_name; +extern off_t mediasz; +extern u_int parts; +extern u_int secsz; +extern int readonly, verbose; + +uint32_t crc32(const void *, size_t); +void gpt_close(int); +int gpt_open(const char *); +void* gpt_read(int, off_t, size_t); +int gpt_write(int, map_t *); + +uint8_t *utf16_to_utf8(uint16_t *); +void utf8_to_utf16(const uint8_t *, uint16_t *, size_t); + +int cmd_add(int, char *[]); +int cmd_create(int, char *[]); +int cmd_destroy(int, char *[]); +int cmd_label(int, char *[]); +int cmd_migrate(int, char *[]); +int cmd_recover(int, char *[]); +int cmd_remove(int, char *[]); +int cmd_show(int, char *[]); + +#endif /* _GPT_H_ */ diff --git a/sbin/gpt/label.c b/sbin/gpt/label.c new file mode 100644 index 0000000000..50df19f250 --- /dev/null +++ b/sbin/gpt/label.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2005 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/label.c,v 1.3 2006/10/04 18:20:25 marcel Exp $ + * $DragonFly: src/sbin/gpt/label.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int all; +static uuid_t type; +static off_t block, size; +static unsigned int entry; +static uint8_t *name; + +static void +usage_label(void) +{ + const char *common = "<-l label | -f file> device ..."; + + fprintf(stderr, + "usage: %s -a %s\n" + " %s [-b lba] [-i index] [-s lba] [-t uuid] %s\n", + getprogname(), common, getprogname(), common); + exit(1); +} + +static void +label(int fd) +{ + uuid_t uuid; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *m; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + unsigned int i; + + gpt = map_find(MAP_TYPE_PRI_GPT_HDR); + if (gpt == NULL) { + warnx("%s: error: no primary GPT header; run create or recover", + device_name); + return; + } + + tpg = map_find(MAP_TYPE_SEC_GPT_HDR); + if (tpg == NULL) { + warnx("%s: error: no secondary GPT header; run recover", + device_name); + return; + } + + tbl = map_find(MAP_TYPE_PRI_GPT_TBL); + lbt = map_find(MAP_TYPE_SEC_GPT_TBL); + if (tbl == NULL || lbt == NULL) { + warnx("%s: error: run recover -- trust me", device_name); + return; + } + + /* Relabel all matching entries in the map. */ + for (m = map_first(); m != NULL; m = m->map_next) { + if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) + continue; + if (entry > 0 && entry != m->map_index) + continue; + if (block > 0 && block != m->map_start) + continue; + if (size > 0 && size != m->map_size) + continue; + + i = m->map_index - 1; + + hdr = gpt->map_data; + ent = (void*)((char*)tbl->map_data + i * + le32toh(hdr->hdr_entsz)); + le_uuid_dec(&ent->ent_type, &uuid); + if (!uuid_is_nil(&type, NULL) && + !uuid_equal(&type, &uuid, NULL)) + continue; + + /* Label the primary entry. */ + utf8_to_utf16(name, ent->ent_name, 36); + + hdr->hdr_crc_table = htole32(crc32(tbl->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, gpt); + gpt_write(fd, tbl); + + hdr = tpg->map_data; + ent = (void*)((char*)lbt->map_data + i * + le32toh(hdr->hdr_entsz)); + + /* Label the secundary entry. */ + utf8_to_utf16(name, ent->ent_name, 36); + + hdr->hdr_crc_table = htole32(crc32(lbt->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, lbt); + gpt_write(fd, tpg); + + printf("%sp%u labeled\n", device_name, m->map_index); + } +} + +static void +name_from_file(const char *fn) +{ + FILE *f; + char *p; + size_t maxlen = 1024; + size_t len; + + if (strcmp(fn, "-") != 0) { + f = fopen(fn, "r"); + if (f == NULL) + err(1, "unable to open file %s", fn); + } else + f = stdin; + name = malloc(maxlen); + len = fread(name, 1, maxlen - 1, f); + if (ferror(f)) + err(1, "unable to read label from file %s", fn); + if (f != stdin) + fclose(f); + name[len] = '\0'; + /* Only keep the first line, excluding the newline character. */ + p = strchr(name, '\n'); + if (p != NULL) + *p = '\0'; +} + +int +cmd_label(int argc, char *argv[]) +{ + char *p; + int ch, fd; + + /* Get the label options */ + while ((ch = getopt(argc, argv, "ab:f:i:l:s:t:")) != -1) { + switch(ch) { + case 'a': + if (all > 0) + usage_label(); + all = 1; + break; + case 'b': + if (block > 0) + usage_label(); + block = strtoll(optarg, &p, 10); + if (*p != 0 || block < 1) + usage_label(); + break; + case 'f': + if (name != NULL) + usage_label(); + name_from_file(optarg); + break; + case 'i': + if (entry > 0) + usage_label(); + entry = strtol(optarg, &p, 10); + if (*p != 0 || entry < 1) + usage_label(); + break; + case 'l': + if (name != NULL) + usage_label(); + name = strdup(optarg); + break; + case 's': + if (size > 0) + usage_label(); + size = strtoll(optarg, &p, 10); + if (*p != 0 || size < 1) + usage_label(); + break; + case 't': + if (!uuid_is_nil(&type, NULL)) + usage_label(); + if (parse_uuid(optarg, &type) != 0) + usage_label(); + break; + default: + usage_label(); + } + } + + if (!all ^ + (block > 0 || entry > 0 || size > 0 || !uuid_is_nil(&type, NULL))) + usage_label(); + + if (name == NULL || argc == optind) + usage_label(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + label(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/map.c b/sbin/gpt/map.c new file mode 100644 index 0000000000..5d82bf9e0d --- /dev/null +++ b/sbin/gpt/map.c @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/map.c,v 1.6 2005/08/31 01:47:19 marcel Exp $ + * $DragonFly: src/sbin/gpt/map.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include +#include +#include +#include + +#include "map.h" + +int lbawidth; + +static map_t *mediamap; + +static map_t * +mkmap(off_t start, off_t size, int type) +{ + map_t *m; + + m = malloc(sizeof(*m)); + if (m == NULL) + return (NULL); + m->map_start = start; + m->map_size = size; + m->map_next = m->map_prev = NULL; + m->map_type = type; + m->map_index = 0; + m->map_data = NULL; + return (m); +} + +map_t * +map_add(off_t start, off_t size, int type, void *data) +{ + map_t *m, *n, *p; + + n = mediamap; + while (n != NULL && n->map_start + n->map_size <= start) + n = n->map_next; + if (n == NULL) + return (NULL); + + if (n->map_start + n->map_size < start + size) { + warnx("error: bogus map"); + return (0); + } + + if (n->map_start == start && n->map_size == size) { + if (n->map_type != MAP_TYPE_UNUSED) { + if (n->map_type != MAP_TYPE_MBR_PART || + type != MAP_TYPE_GPT_PART) { + warnx("warning: partition(%llu,%llu) mirrored", + (long long)start, (long long)size); + } + } + n->map_type = type; + n->map_data = data; + return (n); + } + + if (n->map_type != MAP_TYPE_UNUSED) { + if (n->map_type != MAP_TYPE_MBR_PART || + type != MAP_TYPE_GPT_PART) { + warnx("error: bogus map"); + return (0); + } + n->map_type = MAP_TYPE_UNUSED; + } + + m = mkmap(start, size, type); + if (m == NULL) + return (NULL); + + m->map_data = data; + + if (start == n->map_start) { + m->map_prev = n->map_prev; + m->map_next = n; + if (m->map_prev != NULL) + m->map_prev->map_next = m; + else + mediamap = m; + n->map_prev = m; + n->map_start += size; + n->map_size -= size; + } else if (start + size == n->map_start + n->map_size) { + p = n; + m->map_next = p->map_next; + m->map_prev = p; + if (m->map_next != NULL) + m->map_next->map_prev = m; + p->map_next = m; + p->map_size -= size; + } else { + p = mkmap(n->map_start, start - n->map_start, n->map_type); + n->map_start += p->map_size + m->map_size; + n->map_size -= (p->map_size + m->map_size); + p->map_prev = n->map_prev; + m->map_prev = p; + n->map_prev = m; + m->map_next = n; + p->map_next = m; + if (p->map_prev != NULL) + p->map_prev->map_next = p; + else + mediamap = p; + } + + return (m); +} + +map_t * +map_alloc(off_t start, off_t size) +{ + off_t delta; + map_t *m; + + for (m = mediamap; m != NULL; m = m->map_next) { + if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2) + continue; + if (start != 0 && m->map_start > start) + return (NULL); + delta = (start != 0) ? start - m->map_start : 0; + if (size == 0 || m->map_size - delta >= size) { + if (m->map_size - delta <= 0) + continue; + if (size == 0) + size = m->map_size - delta; + return (map_add(m->map_start + delta, size, + MAP_TYPE_GPT_PART, NULL)); + } + } + + return (NULL); +} + +map_t * +map_find(int type) +{ + map_t *m; + + m = mediamap; + while (m != NULL && m->map_type != type) + m = m->map_next; + return (m); +} + +map_t * +map_first(void) +{ + return mediamap; +} + +map_t * +map_last(void) +{ + map_t *m; + + m = mediamap; + while (m != NULL && m->map_next != NULL) + m = m->map_next; + return (m); +} + +off_t +map_free(off_t start, off_t size) +{ + map_t *m; + + m = mediamap; + + while (m != NULL && m->map_start + m->map_size <= start) + m = m->map_next; + if (m == NULL || m->map_type != MAP_TYPE_UNUSED) + return (0LL); + if (size) + return ((m->map_start + m->map_size >= start + size) ? 1 : 0); + return (m->map_size - (start - m->map_start)); +} + +void +map_init(off_t size) +{ + char buf[32]; + + mediamap = mkmap(0LL, size, MAP_TYPE_UNUSED); + lbawidth = sprintf(buf, "%llu", (long long)size); + if (lbawidth < 5) + lbawidth = 5; +} diff --git a/sbin/gpt/map.h b/sbin/gpt/map.h new file mode 100644 index 0000000000..7e721d629a --- /dev/null +++ b/sbin/gpt/map.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/map.h,v 1.6 2005/08/31 01:47:19 marcel Exp $ + * $DragonFly: src/sbin/gpt/map.h,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#ifndef _MAP_H_ +#define _MAP_H_ + +typedef struct map { + off_t map_start; + off_t map_size; + struct map *map_next; + struct map *map_prev; + int map_type; +#define MAP_TYPE_UNUSED 0 +#define MAP_TYPE_MBR 1 +#define MAP_TYPE_MBR_PART 2 +#define MAP_TYPE_PRI_GPT_HDR 3 +#define MAP_TYPE_SEC_GPT_HDR 4 +#define MAP_TYPE_PRI_GPT_TBL 5 +#define MAP_TYPE_SEC_GPT_TBL 6 +#define MAP_TYPE_GPT_PART 7 +#define MAP_TYPE_PMBR 8 + unsigned int map_index; + void *map_data; +} map_t; + +extern int lbawidth; + +map_t *map_add(off_t, off_t, int, void*); +map_t *map_alloc(off_t, off_t); +map_t *map_find(int); +map_t *map_first(void); +map_t *map_last(void); + +off_t map_free(off_t, off_t); + +void map_init(off_t); + +#endif /* _MAP_H_ */ diff --git a/sbin/gpt/migrate.c b/sbin/gpt/migrate.c new file mode 100644 index 0000000000..456ca329e4 --- /dev/null +++ b/sbin/gpt/migrate.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/migrate.c,v 1.16 2005/09/01 02:42:52 marcel Exp $ + * $DragonFly: src/sbin/gpt/migrate.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +/* + * Allow compilation on platforms that do not have a BSD label. + * The values are valid for amd64, i386 and ia64 disklabels. + */ +#ifndef LABELOFFSET +#define LABELOFFSET 0 +#endif +#ifndef LABELSECTOR +#define LABELSECTOR 1 +#endif + +static int force; +static int slice; + +static void +usage_migrate(void) +{ + + fprintf(stderr, + "usage: %s [-fs] device ...\n", getprogname()); + exit(1); +} + +static struct gpt_ent* +migrate_disklabel(int fd, off_t start, struct gpt_ent *ent) +{ + char *buf; + struct disklabel *dl; + off_t ofs, rawofs; + int i; + + buf = gpt_read(fd, start + LABELSECTOR, 1); + dl = (void*)(buf + LABELOFFSET); + + if (le32toh(dl->d_magic) != DISKMAGIC || + le32toh(dl->d_magic2) != DISKMAGIC) { + warnx("%s: warning: FreeBSD slice without disklabel", + device_name); + return (ent); + } + + rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) * + le32toh(dl->d_secsize); + for (i = 0; i < le16toh(dl->d_npartitions); i++) { + if (dl->d_partitions[i].p_fstype == FS_UNUSED) + continue; + ofs = le32toh(dl->d_partitions[i].p_offset) * + le32toh(dl->d_secsize); + if (ofs < rawofs) + rawofs = 0; + } + rawofs /= secsz; + + for (i = 0; i < le16toh(dl->d_npartitions); i++) { + switch (dl->d_partitions[i].p_fstype) { + case FS_UNUSED: + continue; + case FS_SWAP: { + uuid_t swap = GPT_ENT_TYPE_FREEBSD_SWAP; + le_uuid_enc(&ent->ent_type, &swap); + utf8_to_utf16("FreeBSD swap partition", + ent->ent_name, 36); + break; + } + case FS_BSDFFS: { + uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS; + le_uuid_enc(&ent->ent_type, &ufs); + utf8_to_utf16("FreeBSD UFS partition", + ent->ent_name, 36); + break; + } + case FS_VINUM: { + uuid_t vinum = GPT_ENT_TYPE_FREEBSD_VINUM; + le_uuid_enc(&ent->ent_type, &vinum); + utf8_to_utf16("FreeBSD vinum partition", + ent->ent_name, 36); + break; + } + default: + warnx("%s: warning: unknown FreeBSD partition (%d)", + device_name, dl->d_partitions[i].p_fstype); + continue; + } + + ofs = (le32toh(dl->d_partitions[i].p_offset) * + le32toh(dl->d_secsize)) / secsz; + ofs = (ofs > 0) ? ofs - rawofs : 0; + ent->ent_lba_start = htole64(start + ofs); + ent->ent_lba_end = htole64(start + ofs + + le32toh(dl->d_partitions[i].p_size) - 1LL); + ent++; + } + + return (ent); +} + +static void +migrate(int fd) +{ + uuid_t uuid; + off_t blocks, last; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *map; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + struct mbr *mbr; + uint32_t start, size; + unsigned int i; + + last = mediasz / secsz - 1LL; + + map = map_find(MAP_TYPE_MBR); + if (map == NULL || map->map_start != 0) { + warnx("%s: error: no partitions to convert", device_name); + return; + } + + mbr = map->map_data; + + if (map_find(MAP_TYPE_PRI_GPT_HDR) != NULL || + map_find(MAP_TYPE_SEC_GPT_HDR) != NULL) { + warnx("%s: error: device already contains a GPT", device_name); + return; + } + + /* Get the amount of free space after the MBR */ + blocks = map_free(1LL, 0LL); + if (blocks == 0LL) { + warnx("%s: error: no room for the GPT header", device_name); + return; + } + + /* Don't create more than parts entries. */ + if ((uint64_t)(blocks - 1) * secsz > parts * sizeof(struct gpt_ent)) { + blocks = (parts * sizeof(struct gpt_ent)) / secsz; + if ((parts * sizeof(struct gpt_ent)) % secsz) + blocks++; + blocks++; /* Don't forget the header itself */ + } + + /* Never cross the median of the device. */ + if ((blocks + 1LL) > ((last + 1LL) >> 1)) + blocks = ((last + 1LL) >> 1) - 1LL; + + /* + * Get the amount of free space at the end of the device and + * calculate the size for the GPT structures. + */ + map = map_last(); + if (map->map_type != MAP_TYPE_UNUSED) { + warnx("%s: error: no room for the backup header", device_name); + return; + } + + if (map->map_size < blocks) + blocks = map->map_size; + if (blocks == 1LL) { + warnx("%s: error: no room for the GPT table", device_name); + return; + } + + blocks--; /* Number of blocks in the GPT table. */ + gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, calloc(1, secsz)); + tbl = map_add(2LL, blocks, MAP_TYPE_PRI_GPT_TBL, + calloc(blocks, secsz)); + if (gpt == NULL || tbl == NULL) + return; + + lbt = map_add(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL, + tbl->map_data); + tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, calloc(1, secsz)); + + hdr = gpt->map_data; + memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); + hdr->hdr_revision = htole32(GPT_HDR_REVISION); + /* + * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus + * contains padding we must not include in the size. + */ + hdr->hdr_size = htole32(offsetof(struct gpt_hdr, padding)); + hdr->hdr_lba_self = htole64(gpt->map_start); + hdr->hdr_lba_alt = htole64(tpg->map_start); + hdr->hdr_lba_start = htole64(tbl->map_start + blocks); + hdr->hdr_lba_end = htole64(lbt->map_start - 1LL); + uuid_create(&uuid, NULL); + le_uuid_enc(&hdr->hdr_uuid, &uuid); + hdr->hdr_lba_table = htole64(tbl->map_start); + hdr->hdr_entries = htole32((blocks * secsz) / sizeof(struct gpt_ent)); + if (le32toh(hdr->hdr_entries) > parts) + hdr->hdr_entries = htole32(parts); + hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); + + ent = tbl->map_data; + for (i = 0; i < le32toh(hdr->hdr_entries); i++) { + uuid_create(&uuid, NULL); + le_uuid_enc(&ent[i].ent_uuid, &uuid); + } + + /* Mirror partitions. */ + for (i = 0; i < 4; i++) { + start = le16toh(mbr->mbr_part[i].part_start_hi); + start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo); + size = le16toh(mbr->mbr_part[i].part_size_hi); + size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); + + switch (mbr->mbr_part[i].part_typ) { + case 0: + continue; + case 165: { /* FreeBSD */ + if (slice) { + uuid_t freebsd = GPT_ENT_TYPE_FREEBSD; + le_uuid_enc(&ent->ent_type, &freebsd); + ent->ent_lba_start = htole64((uint64_t)start); + ent->ent_lba_end = htole64(start + size - 1LL); + utf8_to_utf16("FreeBSD disklabel partition", + ent->ent_name, 36); + ent++; + } else + ent = migrate_disklabel(fd, start, ent); + break; + } + case 239: { /* EFI */ + uuid_t efi_slice = GPT_ENT_TYPE_EFI; + le_uuid_enc(&ent->ent_type, &efi_slice); + ent->ent_lba_start = htole64((uint64_t)start); + ent->ent_lba_end = htole64(start + size - 1LL); + utf8_to_utf16("EFI system partition", + ent->ent_name, 36); + ent++; + break; + } + default: + if (!force) { + warnx("%s: error: unknown partition type (%d)", + device_name, mbr->mbr_part[i].part_typ); + return; + } + } + } + ent = tbl->map_data; + + hdr->hdr_crc_table = htole32(crc32(ent, le32toh(hdr->hdr_entries) * + le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, gpt); + gpt_write(fd, tbl); + + /* + * Create backup GPT. + */ + memcpy(tpg->map_data, gpt->map_data, secsz); + hdr = tpg->map_data; + hdr->hdr_lba_self = htole64(tpg->map_start); + hdr->hdr_lba_alt = htole64(gpt->map_start); + hdr->hdr_lba_table = htole64(lbt->map_start); + hdr->hdr_crc_self = 0; /* Don't ever forget this! */ + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, lbt); + gpt_write(fd, tpg); + + map = map_find(MAP_TYPE_MBR); + mbr = map->map_data; + /* + * Turn the MBR into a Protective MBR. + */ + bzero(mbr->mbr_part, sizeof(mbr->mbr_part)); + mbr->mbr_part[0].part_shd = 0xff; + mbr->mbr_part[0].part_ssect = 0xff; + mbr->mbr_part[0].part_scyl = 0xff; + mbr->mbr_part[0].part_typ = 0xee; + mbr->mbr_part[0].part_ehd = 0xff; + mbr->mbr_part[0].part_esect = 0xff; + mbr->mbr_part[0].part_ecyl = 0xff; + mbr->mbr_part[0].part_start_lo = htole16(1); + if (last > 0xffffffff) { + mbr->mbr_part[0].part_size_lo = htole16(0xffff); + mbr->mbr_part[0].part_size_hi = htole16(0xffff); + } else { + mbr->mbr_part[0].part_size_lo = htole16(last); + mbr->mbr_part[0].part_size_hi = htole16(last >> 16); + } + gpt_write(fd, map); +} + +int +cmd_migrate(int argc, char *argv[]) +{ + int ch, fd; + + /* Get the migrate options */ + while ((ch = getopt(argc, argv, "fs")) != -1) { + switch(ch) { + case 'f': + force = 1; + break; + case 's': + slice = 1; + break; + default: + usage_migrate(); + } + } + + if (argc == optind) + usage_migrate(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + migrate(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/recover.c b/sbin/gpt/recover.c new file mode 100644 index 0000000000..9f1461b2be --- /dev/null +++ b/sbin/gpt/recover.c @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/recover.c,v 1.8 2005/08/31 01:47:19 marcel Exp $ + * $DragonFly: src/sbin/gpt/recover.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int recoverable; + +static void +usage_recover(void) +{ + + fprintf(stderr, + "usage: %s device ...\n", getprogname()); + exit(1); +} + +static void +recover(int fd) +{ + off_t last; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + struct gpt_hdr *hdr; + + if (map_find(MAP_TYPE_MBR) != NULL) { + warnx("%s: error: device contains a MBR", device_name); + return; + } + + gpt = map_find(MAP_TYPE_PRI_GPT_HDR); + tpg = map_find(MAP_TYPE_SEC_GPT_HDR); + tbl = map_find(MAP_TYPE_PRI_GPT_TBL); + lbt = map_find(MAP_TYPE_SEC_GPT_TBL); + + if (gpt == NULL && tpg == NULL) { + warnx("%s: no primary or secondary GPT headers, can't recover", + device_name); + return; + } + if (tbl == NULL && lbt == NULL) { + warnx("%s: no primary or secondary GPT tables, can't recover", + device_name); + return; + } + + last = mediasz / secsz - 1LL; + + if (tbl != NULL && lbt == NULL) { + lbt = map_add(last - tbl->map_size, tbl->map_size, + MAP_TYPE_SEC_GPT_TBL, tbl->map_data); + if (lbt == NULL) { + warnx("%s: adding secondary GPT table failed", + device_name); + return; + } + gpt_write(fd, lbt); + warnx("%s: recovered secondary GPT table from primary", + device_name); + } else if (tbl == NULL && lbt != NULL) { + tbl = map_add(2LL, lbt->map_size, MAP_TYPE_PRI_GPT_TBL, + lbt->map_data); + if (tbl == NULL) { + warnx("%s: adding primary GPT table failed", + device_name); + return; + } + gpt_write(fd, tbl); + warnx("%s: recovered primary GPT table from secondary", + device_name); + } + + if (gpt != NULL && tpg == NULL) { + tpg = map_add(last, 1LL, MAP_TYPE_SEC_GPT_HDR, + calloc(1, secsz)); + if (tpg == NULL) { + warnx("%s: adding secondary GPT header failed", + device_name); + return; + } + memcpy(tpg->map_data, gpt->map_data, secsz); + hdr = tpg->map_data; + hdr->hdr_lba_self = htole64(tpg->map_start); + hdr->hdr_lba_alt = htole64(gpt->map_start); + hdr->hdr_lba_table = htole64(lbt->map_start); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + gpt_write(fd, tpg); + warnx("%s: recovered secondary GPT header from primary", + device_name); + } else if (gpt == NULL && tpg != NULL) { + gpt = map_add(1LL, 1LL, MAP_TYPE_PRI_GPT_HDR, + calloc(1, secsz)); + if (gpt == NULL) { + warnx("%s: adding primary GPT header failed", + device_name); + return; + } + memcpy(gpt->map_data, tpg->map_data, secsz); + hdr = gpt->map_data; + hdr->hdr_lba_self = htole64(gpt->map_start); + hdr->hdr_lba_alt = htole64(tpg->map_start); + hdr->hdr_lba_table = htole64(tbl->map_start); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + gpt_write(fd, gpt); + warnx("%s: recovered primary GPT header from secondary", + device_name); + } +} + +int +cmd_recover(int argc, char *argv[]) +{ + int ch, fd; + + while ((ch = getopt(argc, argv, "r")) != -1) { + switch(ch) { + case 'r': + recoverable = 1; + break; + default: + usage_recover(); + } + } + + if (argc == optind) + usage_recover(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + recover(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/remove.c b/sbin/gpt/remove.c new file mode 100644 index 0000000000..ae1e01c07e --- /dev/null +++ b/sbin/gpt/remove.c @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2004 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/remove.c,v 1.10 2006/10/04 18:20:25 marcel Exp $ + * $DragonFly: src/sbin/gpt/remove.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int all; +static uuid_t type; +static off_t block, size; +static unsigned int entry; + +static void +usage_remove(void) +{ + + fprintf(stderr, + "usage: %s -a device ...\n" + " %s [-b lba] [-i index] [-s lba] [-t uuid] device ...\n", + getprogname(), getprogname()); + exit(1); +} + +static void +rem(int fd) +{ + uuid_t uuid; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *m; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + unsigned int i; + + gpt = map_find(MAP_TYPE_PRI_GPT_HDR); + if (gpt == NULL) { + warnx("%s: error: no primary GPT header; run create or recover", + device_name); + return; + } + + tpg = map_find(MAP_TYPE_SEC_GPT_HDR); + if (tpg == NULL) { + warnx("%s: error: no secondary GPT header; run recover", + device_name); + return; + } + + tbl = map_find(MAP_TYPE_PRI_GPT_TBL); + lbt = map_find(MAP_TYPE_SEC_GPT_TBL); + if (tbl == NULL || lbt == NULL) { + warnx("%s: error: run recover -- trust me", device_name); + return; + } + + /* Remove all matching entries in the map. */ + for (m = map_first(); m != NULL; m = m->map_next) { + if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) + continue; + if (entry > 0 && entry != m->map_index) + continue; + if (block > 0 && block != m->map_start) + continue; + if (size > 0 && size != m->map_size) + continue; + + i = m->map_index - 1; + + hdr = gpt->map_data; + ent = (void*)((char*)tbl->map_data + i * + le32toh(hdr->hdr_entsz)); + le_uuid_dec(&ent->ent_type, &uuid); + if (!uuid_is_nil(&type, NULL) && + !uuid_equal(&type, &uuid, NULL)) + continue; + + /* Remove the primary entry by clearing the partition type. */ + uuid_create_nil(&ent->ent_type, NULL); + + hdr->hdr_crc_table = htole32(crc32(tbl->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, gpt); + gpt_write(fd, tbl); + + hdr = tpg->map_data; + ent = (void*)((char*)lbt->map_data + i * + le32toh(hdr->hdr_entsz)); + + /* Remove the secundary entry. */ + uuid_create_nil(&ent->ent_type, NULL); + + hdr->hdr_crc_table = htole32(crc32(lbt->map_data, + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); + + gpt_write(fd, lbt); + gpt_write(fd, tpg); + + printf("%sp%u removed\n", device_name, m->map_index); + } +} + +int +cmd_remove(int argc, char *argv[]) +{ + char *p; + int ch, fd; + + /* Get the remove options */ + while ((ch = getopt(argc, argv, "ab:i:s:t:")) != -1) { + switch(ch) { + case 'a': + if (all > 0) + usage_remove(); + all = 1; + break; + case 'b': + if (block > 0) + usage_remove(); + block = strtoll(optarg, &p, 10); + if (*p != 0 || block < 1) + usage_remove(); + break; + case 'i': + if (entry > 0) + usage_remove(); + entry = strtol(optarg, &p, 10); + if (*p != 0 || entry < 1) + usage_remove(); + break; + case 's': + if (size > 0) + usage_remove(); + size = strtoll(optarg, &p, 10); + if (*p != 0 || size < 1) + usage_remove(); + break; + case 't': + if (!uuid_is_nil(&type, NULL)) + usage_remove(); + if (parse_uuid(optarg, &type) != 0) + usage_remove(); + break; + default: + usage_remove(); + } + } + + if (!all ^ + (block > 0 || entry > 0 || size > 0 || !uuid_is_nil(&type, NULL))) + usage_remove(); + + if (argc == optind) + usage_remove(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + rem(fd); + + gpt_close(fd); + } + + return (0); +} diff --git a/sbin/gpt/show.c b/sbin/gpt/show.c new file mode 100644 index 0000000000..54751f9554 --- /dev/null +++ b/sbin/gpt/show.c @@ -0,0 +1,212 @@ +/*- + * Copyright (c) 2002 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sbin/gpt/show.c,v 1.14 2006/06/22 22:22:32 marcel Exp $ + * $DragonFly: src/sbin/gpt/show.c,v 1.1 2007/06/16 22:29:27 dillon Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "map.h" +#include "gpt.h" + +static int show_label = 0; +static int show_uuid = 0; + +static void +usage_show(void) +{ + + fprintf(stderr, + "usage: %s [-lu] device ...\n", getprogname()); + exit(1); +} + +static const char * +friendly(uuid_t *t) +{ + static uuid_t efi_slice = GPT_ENT_TYPE_EFI; + static uuid_t mslinux = GPT_ENT_TYPE_MS_BASIC_DATA; + static uuid_t freebsd = GPT_ENT_TYPE_FREEBSD; + static uuid_t hfs = GPT_ENT_TYPE_APPLE_HFS; + static uuid_t linuxswap = GPT_ENT_TYPE_LINUX_SWAP; + static uuid_t msr = GPT_ENT_TYPE_MS_RESERVED; + static uuid_t swap = GPT_ENT_TYPE_FREEBSD_SWAP; + static uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS; + static uuid_t vinum = GPT_ENT_TYPE_FREEBSD_VINUM; + static char buf[80]; + char *s; + + if (show_uuid) + goto unfriendly; + + if (uuid_equal(t, &efi_slice, NULL)) + return ("EFI System"); + if (uuid_equal(t, &swap, NULL)) + return ("FreeBSD swap"); + if (uuid_equal(t, &ufs, NULL)) + return ("FreeBSD UFS/UFS2"); + if (uuid_equal(t, &vinum, NULL)) + return ("FreeBSD vinum"); + + if (uuid_equal(t, &freebsd, NULL)) + return ("FreeBSD legacy"); + if (uuid_equal(t, &mslinux, NULL)) + return ("Linux/Windows"); + if (uuid_equal(t, &linuxswap, NULL)) + return ("Linux swap"); + if (uuid_equal(t, &msr, NULL)) + return ("Windows reserved"); + if (uuid_equal(t, &hfs, NULL)) + return ("Apple HFS"); + +unfriendly: + uuid_to_string(t, &s, NULL); + strlcpy(buf, s, sizeof buf); + free(s); + return (buf); +} + +static void +show(int fd __unused) +{ + uuid_t type; + off_t start; + map_t *m, *p; + struct mbr *mbr; + struct gpt_ent *ent; + unsigned int i; + + printf(" %*s", lbawidth, "start"); + printf(" %*s", lbawidth, "size"); + printf(" index contents\n"); + + m = map_first(); + while (m != NULL) { + printf(" %*llu", lbawidth, (long long)m->map_start); + printf(" %*llu", lbawidth, (long long)m->map_size); + putchar(' '); + putchar(' '); + if (m->map_index > 0) + printf("%5d", m->map_index); + else + printf(" "); + putchar(' '); + putchar(' '); + switch (m->map_type) { + case MAP_TYPE_MBR: + if (m->map_start != 0) + printf("Extended "); + printf("MBR"); + break; + case MAP_TYPE_PRI_GPT_HDR: + printf("Pri GPT header"); + break; + case MAP_TYPE_SEC_GPT_HDR: + printf("Sec GPT header"); + break; + case MAP_TYPE_PRI_GPT_TBL: + printf("Pri GPT table"); + break; + case MAP_TYPE_SEC_GPT_TBL: + printf("Sec GPT table"); + break; + case MAP_TYPE_MBR_PART: + p = m->map_data; + if (p->map_start != 0) + printf("Extended "); + printf("MBR part "); + mbr = p->map_data; + for (i = 0; i < 4; i++) { + start = le16toh(mbr->mbr_part[i].part_start_hi); + start = (start << 16) + + le16toh(mbr->mbr_part[i].part_start_lo); + if (m->map_start == p->map_start + start) + break; + } + printf("%d", mbr->mbr_part[i].part_typ); + break; + case MAP_TYPE_GPT_PART: + printf("GPT part "); + ent = m->map_data; + if (show_label) { + printf("- \"%s\"", + utf16_to_utf8(ent->ent_name)); + } else { + le_uuid_dec(&ent->ent_type, &type); + printf("- %s", friendly(&type)); + } + break; + case MAP_TYPE_PMBR: + printf("PMBR"); + break; + } + putchar('\n'); + m = m->map_next; + } +} + +int +cmd_show(int argc, char *argv[]) +{ + int ch, fd; + + while ((ch = getopt(argc, argv, "lu")) != -1) { + switch(ch) { + case 'l': + show_label = 1; + break; + case 'u': + show_uuid = 1; + break; + default: + usage_show(); + } + } + + if (argc == optind) + usage_show(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + show(fd); + + gpt_close(fd); + } + + return (0); +} -- 2.41.0