1 /*- 2 * Copyright (c) 2002 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #if HAVE_NBTOOL_CONFIG_H 28 #include "nbtool_config.h" 29 #endif 30 31 #include <sys/cdefs.h> 32 #ifdef __FBSDID 33 __FBSDID("$FreeBSD: src/sbin/gpt/add.c,v 1.14 2006/06/22 22:05:28 marcel Exp $"); 34 #endif 35 #ifdef __RCSID 36 __RCSID("$NetBSD: resizedisk.c,v 1.6 2014/10/01 03:52:42 jnemeth Exp $"); 37 #endif 38 39 #include <sys/bootblock.h> 40 #include <sys/types.h> 41 42 #include <err.h> 43 #include <stddef.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 #include "map.h" 50 #include "gpt.h" 51 52 static uint64_t sector, size; 53 54 const char resizediskmsg[] = "resizedisk [-s size] device ..."; 55 56 __dead static void 57 usage_resizedisk(void) 58 { 59 60 fprintf(stderr, 61 "usage: %s %s\n", getprogname(), resizediskmsg); 62 exit(1); 63 } 64 65 /* 66 * relocate the secondary GPT based on the following criteria: 67 * - size not specified 68 * - disk has not changed size, do nothing 69 * - disk has grown, relocate secondary 70 * - disk has shrunk, create new secondary 71 * - size specified 72 * - size is larger then disk or same as current location, do nothing 73 * - relocate or create new secondary 74 * - when shrinking, verify that table fits 75 */ 76 static void 77 resizedisk(int fd) 78 { 79 map_t *gpt, *tpg; 80 map_t *tbl, *lbt; 81 map_t *mbrmap; 82 struct gpt_hdr *hdr; 83 struct gpt_ent *ent; 84 struct mbr *mbr; 85 uint64_t last, oldloc, newloc, lastdata, gpt_size; 86 int i; 87 88 last = mediasz / secsz - 1; 89 lastdata = 0; 90 newloc = 0; 91 92 if (sector > last) { 93 warnx("%s: specified size is larger then the disk", 94 device_name); 95 return; 96 } 97 98 mbrmap = map_find(MAP_TYPE_PMBR); 99 if (mbrmap == NULL || mbrmap->map_start != 0) { 100 warnx("%s: error: no valid Protective MBR found", device_name); 101 return; 102 } 103 mbr = mbrmap->map_data; 104 105 gpt = map_find(MAP_TYPE_PRI_GPT_HDR); 106 ent = NULL; 107 if (gpt == NULL) { 108 warnx("%s: error: no primary GPT header; run create or recover", 109 device_name); 110 return; 111 } 112 hdr = gpt->map_data; 113 oldloc = le64toh(hdr->hdr_lba_alt); 114 115 tpg = map_find(MAP_TYPE_SEC_GPT_HDR); 116 if (tpg == NULL) 117 if (gpt_gpt(fd, oldloc, 1)) 118 tpg = map_find(MAP_TYPE_SEC_GPT_HDR); 119 120 tbl = map_find(MAP_TYPE_PRI_GPT_TBL); 121 lbt = map_find(MAP_TYPE_SEC_GPT_TBL); 122 if (tbl == NULL) { 123 warnx("%s: error: run recover -- trust me", device_name); 124 return; 125 } 126 127 gpt_size = tbl->map_size; 128 if (sector == oldloc) { 129 warnx("%s: device is already the specified size", device_name); 130 return; 131 } 132 if (sector == 0 && last == oldloc) { 133 warnx("%s: device hasn't changed size", device_name); 134 return; 135 } 136 137 for (ent = tbl->map_data; ent < 138 (struct gpt_ent *)((char *)tbl->map_data + 139 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)); ent++) { 140 if (!gpt_uuid_is_nil(ent->ent_type) && 141 (le64toh(ent->ent_lba_end) > lastdata)) { 142 lastdata = le64toh(ent->ent_lba_end); 143 } 144 } 145 if (sector - gpt_size <= lastdata) { 146 warnx("%s: not enough space at %" PRIu64 147 " for secondary GPT table", device_name, sector); 148 return; 149 } 150 if (last - gpt_size <= lastdata) { 151 warnx("%s: not enough space for new secondary GPT table", 152 device_name); 153 return; 154 } 155 156 if (sector > oldloc) 157 newloc = sector; 158 if (sector > 0 && sector < oldloc && last >= oldloc) 159 newloc = sector; 160 if (sector == 0 && last > oldloc) 161 newloc = last; 162 if (newloc > 0) { 163 if (tpg == NULL) { 164 warnx("%s: error: no secondary GPT header; run recover", 165 device_name); 166 return; 167 } 168 if (lbt == NULL) { 169 warnx("%s: error: run recover -- trust me", 170 device_name); 171 return; 172 } 173 tpg->map_start = newloc; 174 lbt->map_start = newloc - gpt_size; 175 } else { 176 if (sector > 0) 177 newloc = sector; 178 else 179 newloc = last; 180 tpg = map_add(newloc, 1LL, MAP_TYPE_SEC_GPT_HDR, 181 calloc(1, secsz)); 182 lbt = map_add(newloc - gpt_size, gpt_size, MAP_TYPE_SEC_GPT_TBL, 183 tbl->map_data); 184 memcpy(tpg->map_data, gpt->map_data, secsz); 185 } 186 187 hdr = gpt->map_data; 188 hdr->hdr_lba_alt = tpg->map_start; 189 hdr->hdr_crc_self = 0; 190 hdr->hdr_lba_end = htole64(lbt->map_start - 1); 191 hdr->hdr_crc_self = 192 htole32(crc32(gpt->map_data, GPT_HDR_SIZE)); 193 gpt_write(fd, gpt); 194 195 hdr = tpg->map_data; 196 hdr->hdr_lba_self = htole64(tpg->map_start); 197 hdr->hdr_lba_alt = htole64(gpt->map_start); 198 hdr->hdr_lba_end = htole64(lbt->map_start - 1); 199 hdr->hdr_lba_table = htole64(lbt->map_start); 200 hdr->hdr_crc_self = 0; 201 hdr->hdr_crc_self = 202 htole32(crc32(tpg->map_data, GPT_HDR_SIZE)); 203 gpt_write(fd, lbt); 204 gpt_write(fd, tpg); 205 206 for (i = 0; i < 4; i++) 207 if (mbr->mbr_part[0].part_typ == MBR_PTYPE_PMBR) 208 break; 209 if (i == 4) { 210 warnx("%s: no valid PMBR partition found", device_name); 211 return; 212 } 213 if (last > 0xffffffff) { 214 mbr->mbr_part[0].part_size_lo = htole16(0xffff); 215 mbr->mbr_part[0].part_size_hi = htole16(0xffff); 216 } else { 217 mbr->mbr_part[0].part_size_lo = htole16(last); 218 mbr->mbr_part[0].part_size_hi = htole16(last >> 16); 219 } 220 gpt_write(fd, mbrmap); 221 222 return; 223 } 224 225 int 226 cmd_resizedisk(int argc, char *argv[]) 227 { 228 char *p; 229 int ch, fd; 230 int64_t human_num; 231 232 while ((ch = getopt(argc, argv, "s:")) != -1) { 233 switch(ch) { 234 case 's': 235 if (sector > 0 || size > 0) 236 usage_resizedisk(); 237 sector = strtoll(optarg, &p, 10); 238 if (sector < 1) 239 usage_resizedisk(); 240 if (*p == '\0') 241 break; 242 if (*p == 's' || *p == 'S') { 243 if (*(p + 1) == '\0') 244 break; 245 else 246 usage_resizedisk(); 247 } 248 if (*p == 'b' || *p == 'B') { 249 if (*(p + 1) == '\0') { 250 size = sector; 251 sector = 0; 252 break; 253 } else 254 usage_resizedisk(); 255 } 256 if (dehumanize_number(optarg, &human_num) < 0) 257 usage_resizedisk(); 258 size = human_num; 259 sector = 0; 260 break; 261 default: 262 usage_resizedisk(); 263 } 264 } 265 266 if (argc == optind) 267 usage_resizedisk(); 268 269 while (optind < argc) { 270 fd = gpt_open(argv[optind++]); 271 if (fd == -1) { 272 warn("unable to open device '%s'", device_name); 273 continue; 274 } 275 276 if (size % secsz != 0) { 277 warnx("Size in bytes must be a multiple of sector " 278 "size;"); 279 warnx("the sector size for %s is %d bytes.", 280 device_name, secsz); 281 continue; 282 } 283 if (size > 0) 284 sector = size / secsz - 1; 285 286 resizedisk(fd); 287 288 gpt_close(fd); 289 } 290 291 return 0; 292 } 293