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