sys/vfs/hammer2: Add initial multi-volumes support for HAMMER2
[dragonfly.git] / sbin / gpt / expand.c
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 }