xref: /dflybsd-src/sbin/gpt/expand.c (revision e04444f90cdc669c716a17ac94d82b7be1bcc8e7)
135a9ab8aSMatthew Dillon /*-
235a9ab8aSMatthew Dillon  * Copyright (c) 2002 Marcel Moolenaar
335a9ab8aSMatthew Dillon  * All rights reserved.
435a9ab8aSMatthew Dillon  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
535a9ab8aSMatthew Dillon  *
635a9ab8aSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
735a9ab8aSMatthew Dillon  * modification, are permitted provided that the following conditions
835a9ab8aSMatthew Dillon  * are met:
935a9ab8aSMatthew Dillon  *
1035a9ab8aSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1135a9ab8aSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1235a9ab8aSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1335a9ab8aSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
1435a9ab8aSMatthew Dillon  *    documentation and/or other materials provided with the distribution.
1535a9ab8aSMatthew Dillon  *
1635a9ab8aSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1735a9ab8aSMatthew Dillon  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1835a9ab8aSMatthew Dillon  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1935a9ab8aSMatthew Dillon  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2035a9ab8aSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2135a9ab8aSMatthew Dillon  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2235a9ab8aSMatthew Dillon  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2335a9ab8aSMatthew Dillon  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2435a9ab8aSMatthew Dillon  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2535a9ab8aSMatthew Dillon  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2635a9ab8aSMatthew Dillon  */
2735a9ab8aSMatthew Dillon #include <sys/types.h>
2835a9ab8aSMatthew Dillon 
2935a9ab8aSMatthew Dillon #include <err.h>
3035a9ab8aSMatthew Dillon #include <stddef.h>
3135a9ab8aSMatthew Dillon #include <stdio.h>
3235a9ab8aSMatthew Dillon #include <stdlib.h>
3335a9ab8aSMatthew Dillon #include <string.h>
3435a9ab8aSMatthew Dillon #include <unistd.h>
3535a9ab8aSMatthew Dillon 
3635a9ab8aSMatthew Dillon #include "map.h"
3735a9ab8aSMatthew Dillon #include "gpt.h"
3835a9ab8aSMatthew Dillon 
3935a9ab8aSMatthew Dillon static void expand(int fd);
4035a9ab8aSMatthew Dillon 
4135a9ab8aSMatthew Dillon int
cmd_expand(int argc,char * argv[])4235a9ab8aSMatthew Dillon cmd_expand(int argc, char *argv[])
4335a9ab8aSMatthew Dillon {
4435a9ab8aSMatthew Dillon 	int fd;
4535a9ab8aSMatthew Dillon 
4635a9ab8aSMatthew Dillon 	if (argc == optind) {
4735a9ab8aSMatthew Dillon 		fprintf(stderr, "usage: gpt expand <device>...\n");
4835a9ab8aSMatthew Dillon 		exit(1);
4935a9ab8aSMatthew Dillon 	}
5035a9ab8aSMatthew Dillon 	while (optind < argc) {
5135a9ab8aSMatthew Dillon 		fd = gpt_open(argv[optind++]);
5235a9ab8aSMatthew Dillon 		if (fd == -1) {
5335a9ab8aSMatthew Dillon 			warn("unable to open device '%s'", device_name);
5435a9ab8aSMatthew Dillon 			continue;
5535a9ab8aSMatthew Dillon 		}
5635a9ab8aSMatthew Dillon 
5735a9ab8aSMatthew Dillon 		expand(fd);
5835a9ab8aSMatthew Dillon 
5935a9ab8aSMatthew Dillon 		gpt_close(fd);
6035a9ab8aSMatthew Dillon 	}
6135a9ab8aSMatthew Dillon 	return 0;
6235a9ab8aSMatthew Dillon }
6335a9ab8aSMatthew Dillon 
6435a9ab8aSMatthew Dillon static void
expand(int fd __unused)6535a9ab8aSMatthew Dillon expand(int fd __unused)
6635a9ab8aSMatthew Dillon {
6735a9ab8aSMatthew Dillon 	map_t *pmbr;
6835a9ab8aSMatthew Dillon 	map_t *gpt, *gpt2;
6935a9ab8aSMatthew Dillon 	map_t *tbl, *tbl2;
7035a9ab8aSMatthew Dillon 	map_t *map __unused;
7135a9ab8aSMatthew Dillon 	struct mbr *mbr;
7235a9ab8aSMatthew Dillon 	off_t last;
7335a9ab8aSMatthew Dillon 	off_t blocks;
7435a9ab8aSMatthew Dillon 	off_t delta;
7535a9ab8aSMatthew Dillon 	off_t nblocks;
7635a9ab8aSMatthew Dillon 	struct gpt_hdr *hdr;
7735a9ab8aSMatthew Dillon 	struct gpt_ent *ent;
7835a9ab8aSMatthew Dillon 	struct gpt_ent *lent;
7935a9ab8aSMatthew Dillon 	char *name = NULL;
8035a9ab8aSMatthew Dillon 	u_int i;
81*e04444f9SMatthew Dillon 	u_int li;
8235a9ab8aSMatthew Dillon 
8335a9ab8aSMatthew Dillon 	pmbr = map_find(MAP_TYPE_PMBR);
8435a9ab8aSMatthew Dillon 	if (pmbr == NULL) {
8535a9ab8aSMatthew Dillon 		warnx("%s: error: no PMBR to expand", device_name);
8635a9ab8aSMatthew Dillon 		return;
8735a9ab8aSMatthew Dillon 	}
8835a9ab8aSMatthew Dillon 
8935a9ab8aSMatthew Dillon 	mbr = pmbr->map_data;
9035a9ab8aSMatthew Dillon 
9135a9ab8aSMatthew Dillon 	last = mediasz / secsz - 1LL;
9235a9ab8aSMatthew Dillon 
9335a9ab8aSMatthew Dillon 	gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
9435a9ab8aSMatthew Dillon 	if (gpt == NULL) {
9535a9ab8aSMatthew Dillon 		warnx("%s: error: no primary GPT header; run create or recover",
9635a9ab8aSMatthew Dillon 		    device_name);
9735a9ab8aSMatthew Dillon 		return;
9835a9ab8aSMatthew Dillon 	}
9935a9ab8aSMatthew Dillon 	tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
10035a9ab8aSMatthew Dillon 	if (tbl == NULL) {
10135a9ab8aSMatthew Dillon 		warnx("%s: error: no primary partition table; "
10235a9ab8aSMatthew Dillon 		      "run create or recover",
10335a9ab8aSMatthew Dillon 		      device_name);
10435a9ab8aSMatthew Dillon 		return;
10535a9ab8aSMatthew Dillon 	}
10635a9ab8aSMatthew Dillon 	blocks = tbl->map_size;
10735a9ab8aSMatthew Dillon 
10835a9ab8aSMatthew Dillon 	/*
10935a9ab8aSMatthew Dillon 	 * Since the device may have changed size, gpt might not be able to
11035a9ab8aSMatthew Dillon 	 * find the backup table.  Just ignore anytying we scanned and
11135a9ab8aSMatthew Dillon 	 * create new maps for the secondary gpt and table.
11235a9ab8aSMatthew Dillon 	 */
11335a9ab8aSMatthew Dillon 	gpt2 = mkmap(last, 1LL, MAP_TYPE_SEC_GPT_HDR);
11435a9ab8aSMatthew Dillon 	gpt2->map_data = calloc(1, secsz);
11535a9ab8aSMatthew Dillon 
11635a9ab8aSMatthew Dillon 	tbl2 = mkmap(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL);
11735a9ab8aSMatthew Dillon 	tbl2->map_data = tbl->map_data;
11835a9ab8aSMatthew Dillon 
11935a9ab8aSMatthew Dillon 	/*
12035a9ab8aSMatthew Dillon 	 * Update the PMBR
12135a9ab8aSMatthew Dillon 	 */
12235a9ab8aSMatthew Dillon 	if (last > 0xffffffff) {
12335a9ab8aSMatthew Dillon 		mbr->mbr_part[0].part_size_lo = htole16(0xffff);
12435a9ab8aSMatthew Dillon 		mbr->mbr_part[0].part_size_hi = htole16(0xffff);
12535a9ab8aSMatthew Dillon 	} else {
12635a9ab8aSMatthew Dillon 		mbr->mbr_part[0].part_size_lo = htole16(last);
12735a9ab8aSMatthew Dillon 		mbr->mbr_part[0].part_size_hi = htole16(last >> 16);
12835a9ab8aSMatthew Dillon 	}
12935a9ab8aSMatthew Dillon 
13035a9ab8aSMatthew Dillon 	/*
131*e04444f9SMatthew Dillon 	 * Calculate expansion size
132*e04444f9SMatthew Dillon 	 *
13335a9ab8aSMatthew Dillon 	 * Update the primary gpt header, adjusting the pointer to the
13435a9ab8aSMatthew Dillon 	 * alternative table.
13535a9ab8aSMatthew Dillon 	 */
13635a9ab8aSMatthew Dillon 	hdr = gpt->map_data;
137*e04444f9SMatthew Dillon 	delta = last - hdr->hdr_lba_alt;
13835a9ab8aSMatthew Dillon 	hdr->hdr_lba_alt = htole64(last);
13935a9ab8aSMatthew Dillon 
14035a9ab8aSMatthew Dillon 	/*
141*e04444f9SMatthew Dillon 	 * Update the secondary gpt header.
142*e04444f9SMatthew Dillon 	 */
143*e04444f9SMatthew Dillon 	if (delta == 0) {
144*e04444f9SMatthew Dillon 		printf("gpt already expanded to full device size\n");
145*e04444f9SMatthew Dillon 	} else {
146*e04444f9SMatthew Dillon 		printf("Expand GPT by %jd blocks\n", (intmax_t)delta);
147*e04444f9SMatthew Dillon 	}
148*e04444f9SMatthew Dillon 
149*e04444f9SMatthew Dillon 	/*
15035a9ab8aSMatthew Dillon 	 * Create the secondary gpt header
15135a9ab8aSMatthew Dillon 	 */
15235a9ab8aSMatthew Dillon 	hdr = gpt2->map_data;
15335a9ab8aSMatthew Dillon 	*hdr = *(struct gpt_hdr *)gpt->map_data;
15435a9ab8aSMatthew Dillon 
15535a9ab8aSMatthew Dillon 	hdr->hdr_lba_self = htole64(gpt2->map_start);
15635a9ab8aSMatthew Dillon 	hdr->hdr_lba_table = htole64(tbl2->map_start);
15735a9ab8aSMatthew Dillon 	hdr->hdr_lba_alt = htole64(1);
15835a9ab8aSMatthew Dillon 
15935a9ab8aSMatthew Dillon 	lent = NULL;
160*e04444f9SMatthew Dillon 	li = 0;
16135a9ab8aSMatthew Dillon 	for (i = 0; i < le32toh(hdr->hdr_entries); ++i) {
16235a9ab8aSMatthew Dillon 		ent = (void *)((char *)tbl->map_data + i *
16335a9ab8aSMatthew Dillon 			       le32toh(hdr->hdr_entsz));
16435a9ab8aSMatthew Dillon 		if (uuid_is_nil(&ent->ent_type, NULL))
16535a9ab8aSMatthew Dillon 			continue;
16635a9ab8aSMatthew Dillon 		lent = ent;
167*e04444f9SMatthew Dillon 		li = i;
16835a9ab8aSMatthew Dillon 	}
16935a9ab8aSMatthew Dillon 
17035a9ab8aSMatthew Dillon 	hdr = gpt2->map_data;
17135a9ab8aSMatthew Dillon 
17235a9ab8aSMatthew Dillon 	if (lent) {
173*e04444f9SMatthew Dillon 		uuid_to_string(&ent->ent_type, &name, NULL);
17435a9ab8aSMatthew Dillon 		nblocks = last - blocks - le64toh(lent->ent_lba_start);
17535a9ab8aSMatthew Dillon 		nblocks = (nblocks * secsz / (1024 * 1024)) * 1024 * 1024 /
17635a9ab8aSMatthew Dillon 			  secsz;
177*e04444f9SMatthew Dillon 
178*e04444f9SMatthew Dillon 		if (le64toh(lent->ent_lba_end) ==
179*e04444f9SMatthew Dillon 		    le64toh(lent->ent_lba_start) + nblocks - 1) {
180*e04444f9SMatthew Dillon 			printf("entry %d type=%s %ld,%ld unchanged\n",
181*e04444f9SMatthew Dillon 				li, name,
182*e04444f9SMatthew Dillon 				le64toh(lent->ent_lba_start),
183*e04444f9SMatthew Dillon 				le64toh(lent->ent_lba_end));
184*e04444f9SMatthew Dillon 		} else {
185*e04444f9SMatthew Dillon 			printf("expand entry %d type=%s %ld,%ld to %ld\n",
186*e04444f9SMatthew Dillon 				li, name,
187*e04444f9SMatthew Dillon 				le64toh(lent->ent_lba_start),
188*e04444f9SMatthew Dillon 				le64toh(lent->ent_lba_end),
189*e04444f9SMatthew Dillon 				le64toh(lent->ent_lba_start) + nblocks - 1);
190*e04444f9SMatthew Dillon 			lent->ent_lba_end = htole64(
191*e04444f9SMatthew Dillon 						le64toh(lent->ent_lba_start) +
19235a9ab8aSMatthew Dillon 						nblocks - 1);
19335a9ab8aSMatthew Dillon 		}
194*e04444f9SMatthew Dillon 	}
19535a9ab8aSMatthew Dillon 
19635a9ab8aSMatthew Dillon 	/*
19735a9ab8aSMatthew Dillon 	 * Write out
19835a9ab8aSMatthew Dillon 	 */
19935a9ab8aSMatthew Dillon 	hdr = gpt->map_data;
20035a9ab8aSMatthew Dillon 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
20135a9ab8aSMatthew Dillon 		le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
20235a9ab8aSMatthew Dillon 	hdr->hdr_crc_self = 0;
20335a9ab8aSMatthew Dillon 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
20435a9ab8aSMatthew Dillon 
20535a9ab8aSMatthew Dillon 	hdr = gpt2->map_data;
20635a9ab8aSMatthew Dillon 	hdr->hdr_crc_table = htole32(crc32(tbl2->map_data,
20735a9ab8aSMatthew Dillon 		le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
20835a9ab8aSMatthew Dillon 	hdr->hdr_crc_self = 0;
20935a9ab8aSMatthew Dillon 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
21035a9ab8aSMatthew Dillon 
21135a9ab8aSMatthew Dillon 	/*
21235a9ab8aSMatthew Dillon 	 * NOTE! We don't even try to manage the in-memory media links
21335a9ab8aSMatthew Dillon 	 *	 and tbl2's data is the same pointer as tbl's data.  Don't
21435a9ab8aSMatthew Dillon 	 *	 try to clean up here or be fancy.
21535a9ab8aSMatthew Dillon 	 */
21635a9ab8aSMatthew Dillon 	gpt_write(fd, tbl2);	/* secondary partition table */
21735a9ab8aSMatthew Dillon 	gpt_write(fd, gpt2);	/* secondary header */
21835a9ab8aSMatthew Dillon 	gpt_write(fd, gpt);	/* primary partition table */
21935a9ab8aSMatthew Dillon 	gpt_write(fd, tbl);	/* primary header */
220*e04444f9SMatthew Dillon 	gpt_write(fd, pmbr);	/* primary header */
221*e04444f9SMatthew Dillon 
22235a9ab8aSMatthew Dillon 	if (name)
22335a9ab8aSMatthew Dillon 		free(name);
22435a9ab8aSMatthew Dillon }
225