xref: /dragonfly/sbin/gpt/expand.c (revision e4adeac1)
1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <sys/types.h>
28 
29 #include <err.h>
30 #include <stddef.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "map.h"
37 #include "gpt.h"
38 
39 static void expand(int fd);
40 
41 int
42 cmd_expand(int argc, char *argv[])
43 {
44 	int fd;
45 
46 	if (argc == optind) {
47 		fprintf(stderr, "usage: gpt expand <device>...\n");
48 		exit(1);
49 	}
50 	while (optind < argc) {
51 		fd = gpt_open(argv[optind++]);
52 		if (fd == -1) {
53 			warn("unable to open device '%s'", device_name);
54 			continue;
55 		}
56 
57 		expand(fd);
58 
59 		gpt_close(fd);
60 	}
61 	return 0;
62 }
63 
64 static void
65 expand(int fd __unused)
66 {
67 	map_t *pmbr;
68 	map_t *gpt, *gpt2;
69 	map_t *tbl, *tbl2;
70 	map_t *map __unused;
71 	struct mbr *mbr;
72 	off_t last;
73 	off_t blocks;
74 	off_t delta;
75 	off_t nblocks;
76 	struct gpt_hdr *hdr;
77 	struct gpt_ent *ent;
78 	struct gpt_ent *lent;
79 	char *name = NULL;
80 	u_int i;
81 	u_int li;
82 
83 	pmbr = map_find(MAP_TYPE_PMBR);
84 	if (pmbr == NULL) {
85 		warnx("%s: error: no PMBR to expand", device_name);
86 		return;
87 	}
88 
89 	mbr = pmbr->map_data;
90 
91 	last = mediasz / secsz - 1LL;
92 
93 	gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
94 	if (gpt == NULL) {
95 		warnx("%s: error: no primary GPT header; run create or recover",
96 		    device_name);
97 		return;
98 	}
99 	tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
100 	if (tbl == NULL) {
101 		warnx("%s: error: no primary partition table; "
102 		      "run create or recover",
103 		      device_name);
104 		return;
105 	}
106 	blocks = tbl->map_size;
107 
108 	/*
109 	 * Since the device may have changed size, gpt might not be able to
110 	 * find the backup table.  Just ignore anytying we scanned and
111 	 * create new maps for the secondary gpt and table.
112 	 */
113 	gpt2 = mkmap(last, 1LL, MAP_TYPE_SEC_GPT_HDR);
114 	gpt2->map_data = calloc(1, secsz);
115 
116 	tbl2 = mkmap(last - blocks, blocks, MAP_TYPE_SEC_GPT_TBL);
117 	tbl2->map_data = tbl->map_data;
118 
119 	/*
120 	 * Update the PMBR
121 	 */
122 	if (last > 0xffffffff) {
123 		mbr->mbr_part[0].part_size_lo = htole16(0xffff);
124 		mbr->mbr_part[0].part_size_hi = htole16(0xffff);
125 	} else {
126 		mbr->mbr_part[0].part_size_lo = htole16(last);
127 		mbr->mbr_part[0].part_size_hi = htole16(last >> 16);
128 	}
129 
130 	/*
131 	 * Calculate expansion size
132 	 *
133 	 * Update the primary gpt header, adjusting the pointer to the
134 	 * alternative table.
135 	 */
136 	hdr = gpt->map_data;
137 	delta = last - hdr->hdr_lba_alt;
138 	hdr->hdr_lba_alt = htole64(last);
139 
140 	/*
141 	 * Update the secondary gpt header.
142 	 */
143 	if (delta == 0) {
144 		printf("gpt already expanded to full device size\n");
145 	} else {
146 		printf("Expand GPT by %jd blocks\n", (intmax_t)delta);
147 	}
148 
149 	/*
150 	 * Create the secondary gpt header
151 	 */
152 	hdr = gpt2->map_data;
153 	*hdr = *(struct gpt_hdr *)gpt->map_data;
154 
155 	hdr->hdr_lba_self = htole64(gpt2->map_start);
156 	hdr->hdr_lba_table = htole64(tbl2->map_start);
157 	hdr->hdr_lba_alt = htole64(1);
158 
159 	lent = NULL;
160 	li = 0;
161 	for (i = 0; i < le32toh(hdr->hdr_entries); ++i) {
162 		ent = (void *)((char *)tbl->map_data + i *
163 			       le32toh(hdr->hdr_entsz));
164 		if (uuid_is_nil(&ent->ent_type, NULL))
165 			continue;
166 		lent = ent;
167 		li = i;
168 	}
169 
170 	hdr = gpt2->map_data;
171 
172 	if (lent) {
173 		uuid_to_string(&ent->ent_type, &name, NULL);
174 		nblocks = last - blocks - le64toh(lent->ent_lba_start);
175 		nblocks = (nblocks * secsz / (1024 * 1024)) * 1024 * 1024 /
176 			  secsz;
177 
178 		if (le64toh(lent->ent_lba_end) ==
179 		    le64toh(lent->ent_lba_start) + nblocks - 1) {
180 			printf("entry %d type=%s %ld,%ld unchanged\n",
181 				li, name,
182 				le64toh(lent->ent_lba_start),
183 				le64toh(lent->ent_lba_end));
184 		} else {
185 			printf("expand entry %d type=%s %ld,%ld to %ld\n",
186 				li, name,
187 				le64toh(lent->ent_lba_start),
188 				le64toh(lent->ent_lba_end),
189 				le64toh(lent->ent_lba_start) + nblocks - 1);
190 			lent->ent_lba_end = htole64(
191 						le64toh(lent->ent_lba_start) +
192 						nblocks - 1);
193 		}
194 	}
195 
196 	/*
197 	 * Write out
198 	 */
199 	hdr = gpt->map_data;
200 	hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
201 		le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
202 	hdr->hdr_crc_self = 0;
203 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
204 
205 	hdr = gpt2->map_data;
206 	hdr->hdr_crc_table = htole32(crc32(tbl2->map_data,
207 		le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
208 	hdr->hdr_crc_self = 0;
209 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
210 
211 	/*
212 	 * NOTE! We don't even try to manage the in-memory media links
213 	 *	 and tbl2's data is the same pointer as tbl's data.  Don't
214 	 *	 try to clean up here or be fancy.
215 	 */
216 	gpt_write(fd, tbl2);	/* secondary partition table */
217 	gpt_write(fd, gpt2);	/* secondary header */
218 	gpt_write(fd, gpt);	/* primary partition table */
219 	gpt_write(fd, tbl);	/* primary header */
220 	gpt_write(fd, pmbr);	/* primary header */
221 
222 	if (name)
223 		free(name);
224 }
225