Home | History | Annotate | Line # | Download | only in gpt
resizedisk.c revision 1.16
      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.16 2015/12/04 16:46:24 christos 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 #include "gpt_private.h"
     52 
     53 
     54 static int cmd_resizedisk(gpt_t, int, char *[]);
     55 
     56 static const char *resizediskhelp[] = {
     57 	"[-s size]",
     58 };
     59 
     60 struct gpt_cmd c_resizedisk = {
     61 	"resizedisk",
     62 	cmd_resizedisk,
     63 	resizediskhelp, __arraycount(resizediskhelp),
     64 	0,
     65 };
     66 
     67 #define usage() gpt_usage(NULL, &c_resizedisk)
     68 
     69 /*
     70  * relocate the secondary GPT based on the following criteria:
     71  * - size not specified
     72  *   - disk has not changed size, do nothing
     73  *   - disk has grown, relocate secondary
     74  *   - disk has shrunk, create new secondary
     75  * - size specified
     76  *   - size is larger then disk or same as current location, do nothing
     77  *   - relocate or create new secondary
     78  * - when shrinking, verify that table fits
     79  */
     80 static int
     81 resizedisk(gpt_t gpt, off_t sector, off_t size)
     82 {
     83 	map_t mbrmap;
     84 	struct gpt_hdr *hdr;
     85 	struct gpt_ent *ent;
     86 	struct mbr *mbr;
     87 	off_t last, oldloc, newloc, lastdata, gpt_size;
     88 	int i;
     89 
     90 	last = gpt->mediasz / gpt->secsz - 1;
     91 	lastdata = 0;
     92 	newloc = 0;
     93 
     94 	if (sector > last) {
     95 		gpt_warnx(gpt, "specified number of sectors %jd"
     96 		    " is larger then the disk %jd", (uintmax_t)sector,
     97 		    (uintmax_t)last);
     98 		return -1;
     99 	}
    100 
    101         mbrmap = map_find(gpt, MAP_TYPE_PMBR);
    102         if (mbrmap == NULL || mbrmap->map_start != 0) {
    103                 gpt_warnx(gpt, "No valid PMBR found");
    104                 return -1;
    105         }
    106         mbr = mbrmap->map_data;
    107 
    108 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    109 	if (gpt == NULL) {
    110 		gpt_warnx(gpt, "No primary GPT header; run create or recover");
    111 		return -1;
    112 	}
    113 
    114 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    115 	if (gpt->tbl == NULL) {
    116 		gpt_warnx(gpt, "No primary GPT table; Run recover");
    117 		return -1;
    118 	}
    119 
    120 	hdr = gpt->gpt->map_data;
    121 	oldloc = (off_t)le64toh((uint64_t)hdr->hdr_lba_alt);
    122 
    123 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    124 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    125 	if (gpt->tpg == NULL || gpt->lbt == NULL) {
    126 		if (gpt_gpt(gpt, oldloc, 1) == -1) {
    127 			gpt_warnx(gpt,
    128 			    "Error reading backup GPT information at %#jx",
    129 			    oldloc);
    130 			return -1;
    131 		}
    132 	}
    133 
    134 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    135 	if (gpt->tpg == NULL) {
    136 		gpt_warnx(gpt, "No secondary GPT header; Run recover");
    137 		return -1;
    138 	}
    139 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    140 	if (gpt->lbt == NULL) {
    141 		gpt_warnx(gpt, "No secondary GPT table; Run recover");
    142 		return -1;
    143 	}
    144 
    145 	gpt_size = gpt->tbl->map_size;
    146 	if (sector == oldloc) {
    147 		gpt_warnx(gpt, "Device is already the specified size");
    148 		return 0;
    149 	}
    150 
    151 	if (sector == 0 && last == oldloc) {
    152 		gpt_warnx(gpt, "Device hasn't changed size");
    153 		return 0;
    154 	}
    155 
    156 	for (ent = gpt->tbl->map_data; ent <
    157 	    (struct gpt_ent *)((char *)gpt->tbl->map_data +
    158 	    le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)); ent++) {
    159 		if (!gpt_uuid_is_nil(ent->ent_type) &&
    160 		    ((off_t)le64toh(ent->ent_lba_end) > lastdata)) {
    161 			lastdata = (off_t)le64toh((uint64_t)ent->ent_lba_end);
    162 		}
    163 	}
    164 
    165 	if (sector - gpt_size <= lastdata) {
    166 		gpt_warnx(gpt, "Not enough space at %" PRIu64
    167 		    " for secondary GPT table", sector);
    168 		return -1;
    169 	}
    170 
    171 	if (last - gpt_size <= lastdata) {
    172 		gpt_warnx(gpt, "Not enough space for new secondary GPT table");
    173 		return -1;
    174 	}
    175 
    176 	if (sector > oldloc)
    177 		newloc = sector;
    178 	if (sector > 0 && sector < oldloc && last >= oldloc)
    179 		newloc = sector;
    180 	if (sector == 0 && last > oldloc)
    181 		newloc = last;
    182 
    183 	if (newloc > 0) {
    184 		if (gpt->tpg == NULL) {
    185 			gpt_warnx(gpt, "No secondary GPT header; run recover");
    186 			return -1;
    187 		}
    188 		if (gpt->lbt == NULL) {
    189 			gpt_warnx(gpt, "Run recover");
    190 			return -1;
    191 		}
    192 		gpt->tpg->map_start = newloc;
    193 		gpt->lbt->map_start = newloc - gpt_size;
    194 	} else {
    195 		if (sector > 0)
    196 			newloc = sector;
    197 		else
    198 			newloc = last;
    199 
    200 		if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, newloc) == -1)
    201 			return -1;
    202 
    203 		gpt->lbt = map_add(gpt, newloc - gpt_size, gpt_size,
    204 		    MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0);
    205 		if (gpt->lbt == NULL) {
    206 			gpt_warn(gpt, "Error adding secondary GPT table");
    207 			return -1;
    208 		}
    209 		memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
    210 	}
    211 
    212 	hdr = gpt->gpt->map_data;
    213 	hdr->hdr_lba_alt = (uint64_t)gpt->tpg->map_start;
    214 	hdr->hdr_crc_self = 0;
    215 	hdr->hdr_lba_end = htole64((uint64_t)(gpt->lbt->map_start - 1));
    216 	hdr->hdr_crc_self =
    217 	    htole32(crc32(gpt->gpt->map_data, GPT_HDR_SIZE));
    218 	gpt_write(gpt, gpt->gpt);
    219 
    220 	hdr = gpt->tpg->map_data;
    221 	hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
    222 	hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
    223 	hdr->hdr_lba_end = htole64((uint64_t)(gpt->lbt->map_start - 1));
    224 	hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
    225 
    226 	if (gpt_write_backup(gpt) == -1)
    227 		return -1;
    228 
    229 	for (i = 0; i < 4; i++)
    230 		if (mbr->mbr_part[0].part_typ == MBR_PTYPE_PMBR)
    231 			break;
    232 	if (i == 4) {
    233 		gpt_warnx(gpt, "No valid PMBR partition found");
    234 		return -1;
    235 	}
    236 	if (last > 0xffffffff) {
    237 		mbr->mbr_part[0].part_size_lo = htole16(0xffff);
    238 		mbr->mbr_part[0].part_size_hi = htole16(0xffff);
    239 	} else {
    240 		mbr->mbr_part[0].part_size_lo = htole16((uint16_t)last);
    241 		mbr->mbr_part[0].part_size_hi = htole16((uint16_t)(last >> 16));
    242 	}
    243 	if (gpt_write(gpt, mbrmap) == -1) {
    244 		gpt_warnx(gpt, "Error writing PMBR");
    245 		return -1;
    246 	}
    247 
    248 	return 0;
    249 }
    250 
    251 static int
    252 cmd_resizedisk(gpt_t gpt, int argc, char *argv[])
    253 {
    254 	int ch;
    255 	off_t sector, size = gpt->mediasz;
    256 
    257 	while ((ch = getopt(argc, argv, "s:")) != -1) {
    258 		switch(ch) {
    259 		case 's':
    260 			if (gpt_add_ais(gpt, NULL, NULL, &size, ch) == -1)
    261 				return -1;
    262 			break;
    263 		default:
    264 			return usage();
    265 		}
    266 	}
    267 
    268 	if (argc != optind)
    269 		return usage();
    270 
    271 	if ((sector = gpt_check_ais(gpt, 0, (u_int)~0, size)) == -1)
    272 		return -1;
    273 
    274 	if (--sector == 0) {
    275 		gpt_warnx(gpt, "New size %ju too small", (uintptr_t)size);
    276 		return -1;
    277 	}
    278 
    279 	return resizedisk(gpt, sector, size);
    280 }
    281