Module Name: src Committed By: jnemeth Date: Tue Sep 23 07:47:54 UTC 2014
Modified Files: src/sbin/gpt: Makefile gpt.8 gpt.c gpt.h Added Files: src/sbin/gpt: resizedisk.c Log Message: - make gpt_gpt() available for use directly by subcommands - create new resizedisk disk subcommand for relocating backup GPT To generate a diff of this commit: cvs rdiff -u -r1.8 -r1.9 src/sbin/gpt/Makefile cvs rdiff -u -r1.29 -r1.30 src/sbin/gpt/gpt.8 cvs rdiff -u -r1.28 -r1.29 src/sbin/gpt/gpt.c cvs rdiff -u -r1.11 -r1.12 src/sbin/gpt/gpt.h cvs rdiff -u -r0 -r1.1 src/sbin/gpt/resizedisk.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sbin/gpt/Makefile diff -u src/sbin/gpt/Makefile:1.8 src/sbin/gpt/Makefile:1.9 --- src/sbin/gpt/Makefile:1.8 Sun Aug 10 18:27:15 2014 +++ src/sbin/gpt/Makefile Tue Sep 23 07:47:54 2014 @@ -1,9 +1,10 @@ -# $NetBSD: Makefile,v 1.8 2014/08/10 18:27:15 jnemeth Exp $ +# $NetBSD: Makefile,v 1.9 2014/09/23 07:47:54 jnemeth Exp $ # $FreeBSD: src/sbin/gpt/Makefile,v 1.7 2005/09/01 02:49:20 marcel Exp $ PROG= gpt SRCS= add.c backup.c biosboot.c create.c destroy.c gpt.c label.c map.c \ - migrate.c recover.c remove.c resize.c restore.c set.c show.c unset.c + migrate.c recover.c remove.c resize.c resizedisk.c restore.c \ + set.c show.c unset.c MAN= gpt.8 LDADD+= -lprop -lutil Index: src/sbin/gpt/gpt.8 diff -u src/sbin/gpt/gpt.8:1.29 src/sbin/gpt/gpt.8:1.30 --- src/sbin/gpt/gpt.8:1.29 Sat Sep 20 22:36:09 2014 +++ src/sbin/gpt/gpt.8 Tue Sep 23 07:47:54 2014 @@ -1,4 +1,4 @@ -.\" $NetBSD: gpt.8,v 1.29 2014/09/20 22:36:09 jnemeth Exp $ +.\" $NetBSD: gpt.8,v 1.30 2014/09/23 07:47:54 jnemeth Exp $ .\" .\" Copyright (c) 2002 Marcel Moolenaar .\" All rights reserved. @@ -26,7 +26,7 @@ .\" .\" $FreeBSD: src/sbin/gpt/gpt.8,v 1.17 2006/06/22 22:22:32 marcel Exp $ .\" -.Dd September 20, 2014 +.Dd September 23, 2014 .Dt GPT 8 .Os .Sh NAME @@ -354,6 +354,34 @@ If the .Fl a option is specified then the size will be adjusted to be a multiple of alignment if possible. +.\" ==== resizedisk ==== +.It Nm Ic resizedisk Oo Fl s Ar size Oc Ar device ... +The +.Ic resizedisk +command allows the user to resize a disk. +With GPTs, a backup copy is stored at the end of the disk. +If the underlying medium changes size +.Pq or is going to change size , +then the backup copy needs to be moved to the new end of the disk, +and the last sector available for data storage needs to be adjusted. +This command does that. +If the backup copy no longer exists due to the medium shrinking, then +a new backup copy will be created using the primary copy. +.Pp +The +.Fl s +option allows the new size to be specified, otherwise the backup copy +will automatically be placed at the current end of the disk. +If there is no suffix, or the suffix is +.Sq s +or +.Sq S +then size is in sectors, otherwise size is in bytes which must be +a multiple of the device's sector size. +Using the +.Fl s +option allows you to move the backup copy prior to resizing the medium. +This is primarily useful when shrinking the medium. .\" ==== restore ==== .It Nm Ic restore Oo Fl F Oc Ar device ... The Index: src/sbin/gpt/gpt.c diff -u src/sbin/gpt/gpt.c:1.28 src/sbin/gpt/gpt.c:1.29 --- src/sbin/gpt/gpt.c:1.28 Sun Aug 10 18:27:15 2014 +++ src/sbin/gpt/gpt.c Tue Sep 23 07:47:54 2014 @@ -31,7 +31,7 @@ __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $"); #endif #ifdef __RCSID -__RCSID("$NetBSD: gpt.c,v 1.28 2014/08/10 18:27:15 jnemeth Exp $"); +__RCSID("$NetBSD: gpt.c,v 1.29 2014/09/23 07:47:54 jnemeth Exp $"); #endif #include <sys/param.h> @@ -534,7 +534,7 @@ out: return -1; } -static int +int gpt_gpt(int fd, off_t lba, int found) { uuid_t type; @@ -728,6 +728,7 @@ static struct { { cmd_remove, "remove" }, { NULL, "rename" }, { cmd_resize, "resize" }, + { cmd_resizedisk, "resizedisk" }, { cmd_restore, "restore" }, { cmd_set, "set" }, { cmd_show, "show" }, @@ -742,8 +743,8 @@ usage(void) extern const char addmsg1[], addmsg2[], backupmsg[], biosbootmsg[]; extern const char createmsg[], destroymsg[], labelmsg1[], labelmsg2[]; extern const char labelmsg3[], migratemsg[], recovermsg[], removemsg1[]; - extern const char removemsg2[], resizemsg[], restoremsg[], setmsg[]; - extern const char showmsg[], unsetmsg[]; + extern const char removemsg2[], resizemsg[], resizediskmsg[]; + extern const char restoremsg[], setmsg[], showmsg[], unsetmsg[]; fprintf(stderr, "usage: %s %s\n" @@ -763,6 +764,7 @@ usage(void) " %s %s\n" " %s %s\n" " %s %s\n" + " %s %s\n" " %s %s\n", getprogname(), addmsg1, getprogname(), addmsg2, @@ -778,6 +780,7 @@ usage(void) getprogname(), removemsg1, getprogname(), removemsg2, getprogname(), resizemsg, + getprogname(), resizediskmsg, getprogname(), restoremsg, getprogname(), setmsg, getprogname(), showmsg, Index: src/sbin/gpt/gpt.h diff -u src/sbin/gpt/gpt.h:1.11 src/sbin/gpt/gpt.h:1.12 --- src/sbin/gpt/gpt.h:1.11 Sun Aug 10 18:27:15 2014 +++ src/sbin/gpt/gpt.h Tue Sep 23 07:47:54 2014 @@ -72,6 +72,7 @@ extern int readonly, verbose; uint32_t crc32(const void *, size_t); void gpt_close(int); +int gpt_gpt(int, off_t, int); int gpt_open(const char *); void* gpt_read(int, off_t, size_t); int gpt_write(int, map_t *); @@ -89,6 +90,7 @@ int cmd_migrate(int, char *[]); int cmd_recover(int, char *[]); int cmd_remove(int, char *[]); int cmd_resize(int, char *[]); +int cmd_resizedisk(int, char *[]); int cmd_restore(int, char *[]); int cmd_set(int, char *[]); int cmd_show(int, char *[]); Added files: Index: src/sbin/gpt/resizedisk.c diff -u /dev/null src/sbin/gpt/resizedisk.c:1.1 --- /dev/null Tue Sep 23 07:47:54 2014 +++ src/sbin/gpt/resizedisk.c Tue Sep 23 07:47:54 2014 @@ -0,0 +1,289 @@ +/*- + * 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. + */ + +#include <sys/cdefs.h> +#ifdef __FBSDID +__FBSDID("$FreeBSD: src/sbin/gpt/add.c,v 1.14 2006/06/22 22:05:28 marcel Exp $"); +#endif +#ifdef __RCSID +__RCSID("$NetBSD: resizedisk.c,v 1.1 2014/09/23 07:47:54 jnemeth Exp $"); +#endif + +#include <sys/bootblock.h> +#include <sys/types.h> + +#include <err.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> + +#include "map.h" +#include "gpt.h" + +static uint64_t sector, size; + +const char resizediskmsg[] = "resizedisk [-s size] device ..."; + +__dead static void +usage_resizedisk(void) +{ + + fprintf(stderr, + "usage: %s %s\n", getprogname(), resizediskmsg); + exit(1); +} + +/* + * relocate the secondary GPT based on the following criteria: + * - size not specified + * - disk has not changed size, do nothing + * - disk has grown, relocate secondary + * - disk has shrunk, create new secondary + * - size specified + * - size is larger then disk or same as current location, do nothing + * - relocate or create new secondary + * - when shrinking, verify that table fits + */ +static void +resizedisk(int fd) +{ + uuid_t uuid; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *mbrmap; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + struct mbr *mbr; + uint64_t last, oldloc, newloc, lastdata, gpt_size; + int i; + + last = mediasz / secsz - 1; + lastdata = 0; + newloc = 0; + + if (sector > last) { + warnx("%s: specified size is larger then the disk", + device_name); + return; + } + + mbrmap = map_find(MAP_TYPE_PMBR); + if (mbrmap == NULL || mbrmap->map_start != 0) { + warnx("%s: error: no valid Protective MBR found", device_name); + return; + } + mbr = mbrmap->map_data; + + gpt = map_find(MAP_TYPE_PRI_GPT_HDR); + ent = NULL; + if (gpt == NULL) { + warnx("%s: error: no primary GPT header; run create or recover", + device_name); + return; + } + hdr = gpt->map_data; + oldloc = le64toh(hdr->hdr_lba_alt); + + tpg = map_find(MAP_TYPE_SEC_GPT_HDR); + if (tpg == NULL) + if (gpt_gpt(fd, oldloc, 1)) + 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 (tbl == NULL) { + warnx("%s: error: run recover -- trust me", device_name); + return; + } + + gpt_size = tbl->map_size; + if (sector == oldloc) { + warnx("%s: device is already the specified size", device_name); + return; + } + if (sector == 0 && last == oldloc) { + warnx("%s: device hasn't changed size", device_name); + return; + } + + for (ent = tbl->map_data; ent < + (struct gpt_ent *)((char *)tbl->map_data + + le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)); ent++) { + le_uuid_dec(ent->ent_type, &uuid); + if (!uuid_is_nil(&uuid, NULL) && + (le64toh(ent->ent_lba_end) > lastdata)) { + lastdata = le64toh(ent->ent_lba_end); + } + } + if (sector - gpt_size <= lastdata) { + warnx("%s: not enough space at %lu for secondary GPT table", + device_name, sector); + return; + } + if (last - gpt_size <= lastdata) { + warnx("%s: not enough space for new secondary GPT table", + device_name); + return; + } + + if (sector > oldloc) + newloc = sector; + if (sector > 0 && sector < oldloc && last >= oldloc) + newloc = sector; + if (sector == 0 && last > oldloc) + newloc = last; + if (newloc > 0) { + if (tpg == NULL) { + warnx("%s: error: no secondary GPT header; run recover", + device_name); + return; + } + if (lbt == NULL) { + warnx("%s: error: run recover -- trust me", + device_name); + return; + } + tpg->map_start = newloc; + lbt->map_start = newloc - gpt_size; + } else { + if (sector > 0) + newloc = sector; + else + newloc = last; + tpg = map_add(newloc, 1LL, MAP_TYPE_SEC_GPT_HDR, + calloc(1, secsz)); + lbt = map_add(newloc - gpt_size, gpt_size, MAP_TYPE_SEC_GPT_TBL, + tbl->map_data); + memcpy(tpg->map_data, gpt->map_data, secsz); + } + + hdr = gpt->map_data; + hdr->hdr_lba_alt = tpg->map_start; + hdr->hdr_crc_self = 0; + hdr->hdr_crc_self = + htole32(crc32(gpt->map_data, GPT_HDR_SIZE)); + gpt_write(fd, gpt); + + 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(tpg->map_data, GPT_HDR_SIZE)); + gpt_write(fd, lbt); + gpt_write(fd, tpg); + + for (i = 0; i < 4; i++) + if (mbr->mbr_part[0].part_typ == MBR_PTYPE_PMBR) + break; + if (i == 4) { + warnx("%s: no valid PMBR partition found", device_name); + return; + } + 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, mbrmap); + + return; +} + +int +cmd_resizedisk(int argc, char *argv[]) +{ + char *p; + int ch, fd; + int64_t human_num; + + while ((ch = getopt(argc, argv, "s:")) != -1) { + switch(ch) { + case 's': + if (sector > 0 || size > 0) + usage_resizedisk(); + sector = strtoll(optarg, &p, 10); + if (sector < 1) + usage_resizedisk(); + if (*p == '\0') + break; + if (*p == 's' || *p == 'S') { + if (*(p + 1) == '\0') + break; + else + usage_resizedisk(); + } + if (*p == 'b' || *p == 'B') { + if (*(p + 1) == '\0') { + size = sector; + sector = 0; + break; + } else + usage_resizedisk(); + } + if (dehumanize_number(optarg, &human_num) < 0) + usage_resizedisk(); + size = human_num; + sector = 0; + break; + default: + usage_resizedisk(); + } + } + + if (argc == optind) + usage_resizedisk(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + if (size % secsz != 0) { + warnx("Size in bytes must be a multiple of sector " + "size;"); + warnx("the sector size for %s is %d bytes.", + device_name, secsz); + continue; + } + if (size > 0) + sector = size / secsz - 1; + + resizedisk(fd); + + gpt_close(fd); + } + + return 0; +}