Module Name: src Committed By: jnemeth Date: Wed Nov 20 08:08:47 UTC 2013
Modified Files: src/sbin/gpt: Makefile gpt.8 gpt.c gpt.h map.c map.h Added Files: src/sbin/gpt: resize.c Log Message: Add a resize command. This command was inspired by FreeBSD's gpart(8), but the code was written by myself. To generate a diff of this commit: cvs rdiff -u -r1.4 -r1.5 src/sbin/gpt/Makefile src/sbin/gpt/map.c cvs rdiff -u -r1.16 -r1.17 src/sbin/gpt/gpt.8 cvs rdiff -u -r1.21 -r1.22 src/sbin/gpt/gpt.c cvs rdiff -u -r1.6 -r1.7 src/sbin/gpt/gpt.h cvs rdiff -u -r1.2 -r1.3 src/sbin/gpt/map.h cvs rdiff -u -r0 -r1.1 src/sbin/gpt/resize.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.4 src/sbin/gpt/Makefile:1.5 --- src/sbin/gpt/Makefile:1.4 Thu Jan 6 01:08:48 2011 +++ src/sbin/gpt/Makefile Wed Nov 20 08:08:47 2013 @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.4 2011/01/06 01:08:48 jakllsch Exp $ +# $NetBSD: Makefile,v 1.5 2013/11/20 08:08:47 jnemeth Exp $ # $FreeBSD: src/sbin/gpt/Makefile,v 1.7 2005/09/01 02:49:20 marcel Exp $ PROG= gpt SRCS= add.c biosboot.c create.c destroy.c gpt.c label.c map.c migrate.c \ - recover.c remove.c show.c + recover.c remove.c resize.c show.c MAN= gpt.8 LDADD+= -lprop -lutil Index: src/sbin/gpt/map.c diff -u src/sbin/gpt/map.c:1.4 src/sbin/gpt/map.c:1.5 --- src/sbin/gpt/map.c:1.4 Tue Nov 19 05:03:41 2013 +++ src/sbin/gpt/map.c Wed Nov 20 08:08:47 2013 @@ -29,7 +29,7 @@ __FBSDID("$FreeBSD: src/sbin/gpt/map.c,v 1.6 2005/08/31 01:47:19 marcel Exp $"); #endif #ifdef __RCSID -__RCSID("$NetBSD: map.c,v 1.4 2013/11/19 05:03:41 jnemeth Exp $"); +__RCSID("$NetBSD: map.c,v 1.5 2013/11/20 08:08:47 jnemeth Exp $"); #endif #include <sys/types.h> @@ -186,6 +186,96 @@ map_alloc(off_t start, off_t size, off_t return NULL; } +off_t +map_resize(map_t *m, off_t size, off_t alignment) +{ + map_t *n, *o; + off_t alignsize, prevsize; + + n = m->map_next; + + if (size == 0 && alignment == 0) { + if (n == NULL || n->map_type != MAP_TYPE_UNUSED) + return 0; + else { + size = m->map_size + n->map_size; + m->map_size = size; + m->map_next = n->map_next; + if (n->map_next != NULL) + n->map_next->map_prev = m; + if (n->map_data != NULL) + free(n->map_data); + free(n); + return size; + } + } + + if (size == 0 && alignment > 0) { + if (n == NULL || n->map_type != MAP_TYPE_UNUSED) + return 0; + else { + prevsize = m->map_size; + size = (m->map_size + n->map_size) / + alignment * alignment; + if (size <= prevsize) + return 0; + m->map_size = size; + n->map_start += size - prevsize; + n->map_size -= size - prevsize; + if (n->map_size == 0) { + m->map_next = n->map_next; + if (n->map_next != NULL) + n->map_next->map_prev = m; + if (n->map_data != NULL) + free(n->map_data); + free(n); + } + return size; + } + } + + alignsize = size; + if (alignment % size != 0) + alignsize = (size + alignment) / alignment * alignment; + + if (alignsize < m->map_size) { /* shrinking */ + prevsize = m->map_size; + m->map_size = alignsize; + if (n == NULL || n->map_type != MAP_TYPE_UNUSED) { + o = mkmap(m->map_start + alignsize, + prevsize - alignsize, MAP_TYPE_UNUSED); + m->map_next = o; + o->map_prev = m; + o->map_next = n; + if (n != NULL) + n->map_prev = o; + return alignsize; + } else { + n->map_start -= alignsize; + n->map_size += alignsize; + return alignsize; + } + } else if (alignsize > m->map_size) { /* expanding */ + if (n == NULL || n->map_type != MAP_TYPE_UNUSED || + n->map_size < alignsize - m->map_size) { + return 0; + } + n->map_size -= alignsize - m->map_size; + n->map_start += alignsize - m->map_size; + if (n->map_size == 0) { + m->map_next = n->map_next; + if (n->map_next != NULL) + n->map_next->map_prev = m; + if (n->map_data != NULL) + free(n->map_data); + free(n); + } + m->map_size = alignsize; + return alignsize; + } else /* correct size */ + return alignsize; +} + map_t * map_find(int type) { Index: src/sbin/gpt/gpt.8 diff -u src/sbin/gpt/gpt.8:1.16 src/sbin/gpt/gpt.8:1.17 --- src/sbin/gpt/gpt.8:1.16 Tue Nov 19 05:07:40 2013 +++ src/sbin/gpt/gpt.8 Wed Nov 20 08:08:47 2013 @@ -1,4 +1,4 @@ -.\" $NetBSD: gpt.8,v 1.16 2013/11/19 05:07:40 jnemeth Exp $ +.\" $NetBSD: gpt.8,v 1.17 2013/11/20 08:08:47 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 November 18, 2013 +.Dd November 20, 2013 .Dt GPT 8 .Os .Sh NAME @@ -316,6 +316,22 @@ command. See above for a description of these options. Partitions are removed by clearing the partition type. No other information is changed. +.\" ==== resize ==== +.It Nm Ic resize Fl i Ar index Oo Fl a Ar alignment Oc \ +Oo Fl s Ar count Oc Ar device ... +The +.Ic resize +command allows the user to resize a partition. +The partition may be shrunked and if there is sufficient free space +immediately after it then it may be expanded. +The +.Fl s +option allows the new size to be specified, otherwise the partition will +be increased to the maximum available size. +If the +.Fl a +option is specified then the size will be adjusted to be a multiple of +alignment if possible. .\" ==== show ==== .It Nm Ic show Oo Fl lu Oc Ar device ... The Index: src/sbin/gpt/gpt.c diff -u src/sbin/gpt/gpt.c:1.21 src/sbin/gpt/gpt.c:1.22 --- src/sbin/gpt/gpt.c:1.21 Tue Nov 19 05:03:41 2013 +++ src/sbin/gpt/gpt.c Wed Nov 20 08:08:47 2013 @@ -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.21 2013/11/19 05:03:41 jnemeth Exp $"); +__RCSID("$NetBSD: gpt.c,v 1.22 2013/11/20 08:08:47 jnemeth Exp $"); #endif #include <sys/param.h> @@ -776,6 +776,7 @@ static struct { { cmd_recover, "recover" }, { cmd_remove, "remove" }, { NULL, "rename" }, + { cmd_resize, "resize" }, { cmd_show, "show" }, { NULL, "verify" }, { NULL, NULL } @@ -787,7 +788,7 @@ usage(void) extern const char addmsg1[], addmsg2[], biosbootmsg[], createmsg[]; extern const char destroymsg[], labelmsg1[], labelmsg2[], labelmsg3[]; extern const char migratemsg[], recovermsg[], removemsg1[]; - extern const char removemsg2[], showmsg[]; + extern const char removemsg2[], resizemsg[], showmsg[]; fprintf(stderr, "usage: %s %s\n" @@ -802,6 +803,7 @@ usage(void) " %s %s\n" " %s %s\n" " %s %s\n" + " %s %s\n" " %s %s\n", getprogname(), addmsg1, getprogname(), addmsg2, @@ -815,6 +817,7 @@ usage(void) getprogname(), recovermsg, getprogname(), removemsg1, getprogname(), removemsg2, + getprogname(), resizemsg, getprogname(), showmsg); exit(1); } Index: src/sbin/gpt/gpt.h diff -u src/sbin/gpt/gpt.h:1.6 src/sbin/gpt/gpt.h:1.7 --- src/sbin/gpt/gpt.h:1.6 Sat Apr 13 18:04:33 2013 +++ src/sbin/gpt/gpt.h Wed Nov 20 08:08:47 2013 @@ -102,6 +102,7 @@ int cmd_label(int, char *[]); int cmd_migrate(int, char *[]); int cmd_recover(int, char *[]); int cmd_remove(int, char *[]); +int cmd_resize(int, char *[]); int cmd_show(int, char *[]); #endif /* _GPT_H_ */ Index: src/sbin/gpt/map.h diff -u src/sbin/gpt/map.h:1.2 src/sbin/gpt/map.h:1.3 --- src/sbin/gpt/map.h:1.2 Tue Nov 19 05:03:41 2013 +++ src/sbin/gpt/map.h Wed Nov 20 08:08:47 2013 @@ -55,6 +55,7 @@ map_t *map_alloc(off_t, off_t, off_t); map_t *map_find(int); map_t *map_first(void); map_t *map_last(void); +off_t map_resize(map_t *, off_t, off_t); off_t map_free(off_t, off_t); Added files: Index: src/sbin/gpt/resize.c diff -u /dev/null src/sbin/gpt/resize.c:1.1 --- /dev/null Wed Nov 20 08:08:47 2013 +++ src/sbin/gpt/resize.c Wed Nov 20 08:08:47 2013 @@ -0,0 +1,241 @@ +/*- + * 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: resize.c,v 1.1 2013/11/20 08:08:47 jnemeth Exp $"); +#endif + +#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 off_t alignment, size; +static unsigned int entry; + +const char resizemsg[] = "resize -i index [-a alignment] [-s lba] device ..."; + +__dead static void +usage_resize(void) +{ + + fprintf(stderr, + "usage: %s %s\n", getprogname(), resizemsg); + exit(1); +} + +static void +resize(int fd) +{ + uuid_t uuid; + map_t *gpt, *tpg; + map_t *tbl, *lbt; + map_t *map; + struct gpt_hdr *hdr; + struct gpt_ent *ent; + unsigned int i; + off_t alignsecs, newsize; + + + 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; + } + + 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; + } + + i = entry - 1; + ent = (void*)((char*)tbl->map_data + i * + le32toh(hdr->hdr_entsz)); + le_uuid_dec(ent->ent_type, &uuid); + if (uuid_is_nil(&uuid, NULL)) { + warnx("%s: error: entry at index %u is unused", + device_name, entry); + return; + } + + alignsecs = alignment / secsz; + + for (map = map_first(); map != NULL; map = map->map_next) { + if (entry == map->map_index) + break; + } + if (entry != map->map_index) { + warnx("%s: error: could not find map entry corresponding " + "to index", device_name); + return; + } + + if (size > 0 && size == map->map_size) + if (alignment == 0 || + (alignment > 0 && size % alignsecs == 0)) { + /* nothing to do */ + warnx("%s: partition does not need resizing", + device_name); + return; + } + + newsize = map_resize(map, size, alignsecs); + if (newsize == 0 && alignment > 0 && size > 0) { + warnx("%s: could not resize partition with alignment " + "constraint", device_name); + newsize = map_resize(map, size, 0); + } + if (newsize == 0) { + warnx("%s: could not resize partition", device_name); + return; + } + + ent->ent_lba_end = htole64(map->map_start + newsize - 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)); + + ent->ent_lba_end = htole64(map->map_start + newsize - 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); + +#ifdef __FreeBSD__ + printf("%sp%u resized\n", device_name, i + 1); +#endif +#ifdef __NetBSD__ + printf("Partition resized, use:\n"); + printf("\tdkctl %s addwedge <wedgename> %" PRIu64 " %" PRIu64 + " <type>\n", device_arg, map->map_start, newsize); + printf("to create a wedge for it\n"); +#endif +} + +int +cmd_resize(int argc, char *argv[]) +{ + char *p; + int ch, fd; + int64_t human_num; + + while ((ch = getopt(argc, argv, "a:i:s:")) != -1) { + switch(ch) { + case 'a': + if (alignment > 0) + usage_resize(); + if (dehumanize_number(optarg, &human_num) < 0) + usage_resize(); + alignment = human_num; + break; + case 'i': + if (entry > 0) + usage_resize(); + entry = strtoul(optarg, &p, 10); + if (*p != 0 || entry < 1) + usage_resize(); + break; + case 's': + if (size > 0) + usage_resize(); + size = strtoll(optarg, &p, 10); + if (*p != 0 || size < 1) + usage_resize(); + break; + default: + usage_resize(); + } + } + + if (argc == optind) + usage_resize(); + + if (entry == 0) + usage_resize(); + + while (optind < argc) { + fd = gpt_open(argv[optind++]); + if (fd == -1) { + warn("unable to open device '%s'", device_name); + continue; + } + + if (alignment % secsz != 0) { + warnx("Alignment must be a multiple of sector size; "); + warnx("the sector size for %s is %d bytes.", + device_name, secsz); + continue; + } + + resize(fd); + + gpt_close(fd); + } + + return 0; +}