Home | History | Annotate | Line # | Download | only in gpt
      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/recover.c,v 1.8 2005/08/31 01:47:19 marcel Exp $");
     34 #endif
     35 #ifdef __RCSID
     36 __RCSID("$NetBSD: recover.c,v 1.23 2025/12/17 16:18:11 nia Exp $");
     37 #endif
     38 
     39 #include <sys/types.h>
     40 #if defined(HAVE_SYS_ENDIAN_H) || ! defined(HAVE_NBTOOL_CONFIG_H)
     41 #include <sys/endian.h>
     42 #endif
     43 
     44 #include <err.h>
     45 #include <stddef.h>
     46 #include <stdio.h>
     47 #include <stdlib.h>
     48 #include <string.h>
     49 #include <unistd.h>
     50 
     51 #include "map.h"
     52 #include "gpt.h"
     53 #include "gpt_private.h"
     54 
     55 static int cmd_recover(gpt_t, int, char *[]);
     56 
     57 static const char *recoverhelp[] = {
     58 	"",
     59 };
     60 
     61 const struct gpt_cmd c_recover = {
     62 	"recover",
     63 	cmd_recover,
     64 	recoverhelp, __arraycount(recoverhelp),
     65 	GPT_SYNC,
     66 };
     67 
     68 #define usage() gpt_usage(NULL, &c_recover)
     69 
     70 static int
     71 recover_gpt_hdr(gpt_t gpt, int type, off_t last)
     72 {
     73 	const char *name, *origname;
     74 	map_t *dgpt, dtbl, sgpt, stbl __unused;
     75 	struct gpt_hdr *hdr;
     76 
     77 	if (gpt_add_hdr(gpt, type, last) == -1)
     78 		return -1;
     79 
     80 	switch (type) {
     81 	case MAP_TYPE_PRI_GPT_HDR:
     82 		dgpt = &gpt->gpt;
     83 		dtbl = gpt->tbl;
     84 		sgpt = gpt->tpg;
     85 		stbl = gpt->lbt;
     86 		origname = "secondary";
     87 		name = "primary";
     88 		break;
     89 	case MAP_TYPE_SEC_GPT_HDR:
     90 		dgpt = &gpt->tpg;
     91 		dtbl = gpt->lbt;
     92 		sgpt = gpt->gpt;
     93 		stbl = gpt->tbl;
     94 		origname = "primary";
     95 		name = "secondary";
     96 		break;
     97 	default:
     98 		gpt_warn(gpt, "Bad table type %d", type);
     99 		return -1;
    100 	}
    101 
    102 	memcpy((*dgpt)->map_data, sgpt->map_data, gpt->secsz);
    103 	hdr = (*dgpt)->map_data;
    104 	hdr->hdr_lba_self = htole64((uint64_t)(*dgpt)->map_start);
    105 	hdr->hdr_lba_alt = htole64((uint64_t)sgpt->map_start);
    106 	hdr->hdr_lba_table = htole64((uint64_t)dtbl->map_start);
    107 	hdr->hdr_crc_self = 0;
    108 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    109 	if (gpt_write(gpt, *dgpt) == -1) {
    110 		gpt_warnx(gpt, "Writing %s GPT header failed", name);
    111 		return -1;
    112 	}
    113 	gpt_msg(gpt, "Recovered %s GPT header from %s", name, origname);
    114 	return 0;
    115 }
    116 
    117 static int
    118 recover_gpt_tbl(gpt_t gpt, int type, off_t start)
    119 {
    120 	const char *name, *origname;
    121 	map_t *dtbl, stbl;
    122 
    123 	switch (type) {
    124 	case MAP_TYPE_PRI_GPT_TBL:
    125 		dtbl = &gpt->tbl;
    126 		stbl = gpt->lbt;
    127 		origname = "secondary";
    128 		name = "primary";
    129 		break;
    130 	case MAP_TYPE_SEC_GPT_TBL:
    131 		dtbl = &gpt->lbt;
    132 		stbl = gpt->tbl;
    133 		origname = "primary";
    134 		name = "secondary";
    135 		break;
    136 	default:
    137 		gpt_warn(gpt, "Bad table type %d", type);
    138 		return -1;
    139 	}
    140 
    141 	*dtbl = map_add(gpt, start, stbl->map_size, type, stbl->map_data, 0);
    142 	if (*dtbl == NULL) {
    143 		gpt_warnx(gpt, "Adding %s GPT table failed", name);
    144 		return -1;
    145 	}
    146 	if (gpt_write(gpt, *dtbl) == -1) {
    147 		gpt_warnx(gpt, "Writing %s GPT table failed", name);
    148 		return -1;
    149 	}
    150 	gpt_msg(gpt, "Recovered %s GPT table from %s", name, origname);
    151 	return 0;
    152 }
    153 
    154 static int
    155 recover(gpt_t gpt)
    156 {
    157 	off_t last = gpt_last(gpt);
    158 	map_t map;
    159 	struct mbr *mbr;
    160 
    161 	if (map_find(gpt, MAP_TYPE_MBR) != NULL) {
    162 		gpt_warnx(gpt, "Device contains an MBR");
    163 		return -1;
    164 	}
    165 
    166 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    167 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    168 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    169 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    170 
    171 	if (gpt->gpt == NULL && gpt->tpg == NULL) {
    172 		gpt_warnx(gpt, "No primary or secondary GPT headers, "
    173 		    "can't recover");
    174 		return -1;
    175 	}
    176 	if (gpt->tbl == NULL && gpt->lbt == NULL) {
    177 		gpt_warnx(gpt, "No primary or secondary GPT tables, "
    178 		    "can't recover");
    179 		return -1;
    180 	}
    181 
    182 	if (gpt->gpt != NULL &&
    183 	    le64toh(((struct gpt_hdr *)(gpt->gpt->map_data))->hdr_lba_alt) !=
    184 	    (uint64_t)last) {
    185 		gpt_warnx(gpt, "Media size has changed, please use "
    186 		   "'%s resizedisk'", getprogname());
    187 		return -1;
    188 	}
    189 
    190 	if (gpt->tbl != NULL && gpt->lbt == NULL) {
    191 		if (recover_gpt_tbl(gpt, MAP_TYPE_SEC_GPT_TBL,
    192 		    last - gpt->tbl->map_size) == -1)
    193 			return -1;
    194 	} else if (gpt->tbl == NULL && gpt->lbt != NULL) {
    195 		if (recover_gpt_tbl(gpt, MAP_TYPE_PRI_GPT_TBL, 2LL) == -1)
    196 			return -1;
    197 	}
    198 
    199 	if (gpt->gpt != NULL && gpt->tpg == NULL) {
    200 		if (recover_gpt_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    201 			return -1;
    202 	} else if (gpt->gpt == NULL && gpt->tpg != NULL) {
    203 		if (recover_gpt_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1LL) == -1)
    204 			return -1;
    205 	}
    206 
    207 	/*
    208 	 * Create PMBR if it doesn't already exist.
    209 	 */
    210 	if (map_find(gpt, MAP_TYPE_PMBR) == NULL) {
    211 		if (map_free(gpt, 0LL, 1LL) == 0) {
    212 			gpt_warnx(gpt, "No room for the PMBR");
    213 			return -1;
    214 		}
    215 		mbr = gpt_read(gpt, 0LL, 1);
    216 		if (mbr == NULL) {
    217 			gpt_warnx(gpt, "Error reading MBR");
    218 			return -1;
    219 		}
    220 		memset(mbr, 0, sizeof(*mbr));
    221 		mbr->mbr_sig = htole16(MBR_SIG);
    222 		gpt_create_pmbr_part(mbr->mbr_part, last, 0);
    223 
    224 		map = map_add(gpt, 0LL, 1LL, MAP_TYPE_PMBR, mbr, 1);
    225 		if (gpt_write(gpt, map) == -1) {
    226 			gpt_warn(gpt, "Can't write PMBR");
    227 			return -1;
    228 		}
    229 		gpt_msg(gpt,
    230 		    "Recreated PMBR (you may need to rerun 'gpt biosboot'");
    231 	}
    232 	return 0;
    233 }
    234 
    235 static int
    236 cmd_recover(gpt_t gpt, int argc, char *argv[])
    237 {
    238 	if (argc != 2)
    239 		return usage();
    240 
    241 	return recover(gpt);
    242 }
    243