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