Home | History | Annotate | Line # | Download | only in gpt
      1   1.1  christos /*-
      2   1.1  christos  * Copyright (c) 2002 Marcel Moolenaar
      3   1.1  christos  * All rights reserved.
      4   1.1  christos  *
      5   1.1  christos  * Redistribution and use in source and binary forms, with or without
      6   1.1  christos  * modification, are permitted provided that the following conditions
      7   1.1  christos  * are met:
      8   1.1  christos  *
      9   1.1  christos  * 1. Redistributions of source code must retain the above copyright
     10   1.1  christos  *    notice, this list of conditions and the following disclaimer.
     11   1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     12   1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     13   1.1  christos  *    documentation and/or other materials provided with the distribution.
     14   1.1  christos  *
     15   1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16   1.1  christos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17   1.1  christos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18   1.1  christos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19   1.1  christos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20   1.1  christos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21   1.1  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22   1.1  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23   1.1  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24   1.1  christos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25   1.1  christos  */
     26   1.1  christos 
     27   1.5  christos #if HAVE_NBTOOL_CONFIG_H
     28   1.5  christos #include "nbtool_config.h"
     29   1.5  christos #endif
     30   1.5  christos 
     31   1.1  christos #include <sys/cdefs.h>
     32   1.2  christos #ifdef __FBSDID
     33   1.1  christos __FBSDID("$FreeBSD: src/sbin/gpt/recover.c,v 1.8 2005/08/31 01:47:19 marcel Exp $");
     34   1.2  christos #endif
     35   1.2  christos #ifdef __RCSID
     36  1.21  christos __RCSID("$NetBSD: recover.c,v 1.21 2025/02/24 17:07:04 christos Exp $");
     37   1.2  christos #endif
     38   1.1  christos 
     39   1.1  christos #include <sys/types.h>
     40   1.1  christos 
     41   1.1  christos #include <err.h>
     42   1.1  christos #include <stddef.h>
     43   1.1  christos #include <stdio.h>
     44   1.1  christos #include <stdlib.h>
     45   1.1  christos #include <string.h>
     46   1.1  christos #include <unistd.h>
     47   1.1  christos 
     48   1.1  christos #include "map.h"
     49   1.1  christos #include "gpt.h"
     50   1.8  christos #include "gpt_private.h"
     51   1.1  christos 
     52   1.9  christos static int cmd_recover(gpt_t, int, char *[]);
     53   1.3       riz 
     54   1.9  christos static const char *recoverhelp[] = {
     55  1.12  christos 	"",
     56   1.9  christos };
     57   1.9  christos 
     58  1.20  christos const struct gpt_cmd c_recover = {
     59   1.9  christos 	"recover",
     60   1.9  christos 	cmd_recover,
     61   1.9  christos 	recoverhelp, __arraycount(recoverhelp),
     62  1.18   jnemeth 	GPT_SYNC,
     63   1.9  christos };
     64   1.1  christos 
     65   1.9  christos #define usage() gpt_usage(NULL, &c_recover)
     66   1.1  christos 
     67   1.8  christos static int
     68  1.10  christos recover_gpt_hdr(gpt_t gpt, int type, off_t last)
     69  1.10  christos {
     70  1.10  christos 	const char *name, *origname;
     71  1.16   jnemeth 	map_t *dgpt, dtbl, sgpt, stbl __unused;
     72  1.10  christos 	struct gpt_hdr *hdr;
     73  1.10  christos 
     74  1.15  christos 	if (gpt_add_hdr(gpt, type, last) == -1)
     75  1.15  christos 		return -1;
     76  1.15  christos 
     77  1.10  christos 	switch (type) {
     78  1.10  christos 	case MAP_TYPE_PRI_GPT_HDR:
     79  1.10  christos 		dgpt = &gpt->gpt;
     80  1.10  christos 		dtbl = gpt->tbl;
     81  1.10  christos 		sgpt = gpt->tpg;
     82  1.10  christos 		stbl = gpt->lbt;
     83  1.10  christos 		origname = "secondary";
     84  1.10  christos 		name = "primary";
     85  1.10  christos 		break;
     86  1.10  christos 	case MAP_TYPE_SEC_GPT_HDR:
     87  1.10  christos 		dgpt = &gpt->tpg;
     88  1.10  christos 		dtbl = gpt->lbt;
     89  1.10  christos 		sgpt = gpt->gpt;
     90  1.10  christos 		stbl = gpt->tbl;
     91  1.10  christos 		origname = "primary";
     92  1.10  christos 		name = "secondary";
     93  1.10  christos 		break;
     94  1.10  christos 	default:
     95  1.10  christos 		gpt_warn(gpt, "Bad table type %d", type);
     96  1.10  christos 		return -1;
     97  1.10  christos 	}
     98  1.10  christos 
     99  1.10  christos 	memcpy((*dgpt)->map_data, sgpt->map_data, gpt->secsz);
    100  1.10  christos 	hdr = (*dgpt)->map_data;
    101  1.13  christos 	hdr->hdr_lba_self = htole64((uint64_t)(*dgpt)->map_start);
    102  1.16   jnemeth 	hdr->hdr_lba_alt = htole64((uint64_t)sgpt->map_start);
    103  1.13  christos 	hdr->hdr_lba_table = htole64((uint64_t)dtbl->map_start);
    104  1.10  christos 	hdr->hdr_crc_self = 0;
    105  1.10  christos 	hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
    106  1.10  christos 	if (gpt_write(gpt, *dgpt) == -1) {
    107  1.10  christos 		gpt_warnx(gpt, "Writing %s GPT header failed", name);
    108  1.10  christos 		return -1;
    109  1.10  christos 	}
    110  1.10  christos 	gpt_msg(gpt, "Recovered %s GPT header from %s", name, origname);
    111  1.10  christos 	return 0;
    112  1.10  christos }
    113  1.10  christos 
    114  1.10  christos static int
    115  1.10  christos recover_gpt_tbl(gpt_t gpt, int type, off_t start)
    116  1.10  christos {
    117  1.10  christos 	const char *name, *origname;
    118  1.10  christos 	map_t *dtbl, stbl;
    119  1.10  christos 
    120  1.10  christos 	switch (type) {
    121  1.10  christos 	case MAP_TYPE_PRI_GPT_TBL:
    122  1.10  christos 		dtbl = &gpt->tbl;
    123  1.10  christos 		stbl = gpt->lbt;
    124  1.10  christos 		origname = "secondary";
    125  1.10  christos 		name = "primary";
    126  1.10  christos 		break;
    127  1.10  christos 	case MAP_TYPE_SEC_GPT_TBL:
    128  1.10  christos 		dtbl = &gpt->lbt;
    129  1.10  christos 		stbl = gpt->tbl;
    130  1.10  christos 		origname = "primary";
    131  1.10  christos 		name = "secondary";
    132  1.10  christos 		break;
    133  1.10  christos 	default:
    134  1.10  christos 		gpt_warn(gpt, "Bad table type %d", type);
    135  1.10  christos 		return -1;
    136  1.10  christos 	}
    137  1.10  christos 
    138  1.14  christos 	*dtbl = map_add(gpt, start, stbl->map_size, type, stbl->map_data, 0);
    139  1.10  christos 	if (*dtbl == NULL) {
    140  1.10  christos 		gpt_warnx(gpt, "Adding %s GPT table failed", name);
    141  1.10  christos 		return -1;
    142  1.10  christos 	}
    143  1.10  christos 	if (gpt_write(gpt, *dtbl) == -1) {
    144  1.10  christos 		gpt_warnx(gpt, "Writing %s GPT table failed", name);
    145  1.10  christos 		return -1;
    146  1.10  christos 	}
    147  1.10  christos 	gpt_msg(gpt, "Recovered %s GPT table from %s", name, origname);
    148  1.10  christos 	return 0;
    149  1.10  christos }
    150  1.10  christos 
    151  1.10  christos static int
    152  1.19   jnemeth recover(gpt_t gpt)
    153   1.1  christos {
    154  1.16   jnemeth 	off_t last = gpt_last(gpt);
    155  1.16   jnemeth 	map_t map;
    156  1.16   jnemeth 	struct mbr *mbr;
    157   1.1  christos 
    158   1.8  christos 	if (map_find(gpt, MAP_TYPE_MBR) != NULL) {
    159   1.8  christos 		gpt_warnx(gpt, "Device contains an MBR");
    160   1.8  christos 		return -1;
    161   1.8  christos 	}
    162   1.8  christos 
    163   1.8  christos 	gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
    164   1.8  christos 	gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
    165   1.8  christos 	gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
    166   1.8  christos 	gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
    167   1.8  christos 
    168   1.8  christos 	if (gpt->gpt == NULL && gpt->tpg == NULL) {
    169   1.8  christos 		gpt_warnx(gpt, "No primary or secondary GPT headers, "
    170   1.8  christos 		    "can't recover");
    171   1.8  christos 		return -1;
    172   1.8  christos 	}
    173   1.8  christos 	if (gpt->tbl == NULL && gpt->lbt == NULL) {
    174   1.8  christos 		gpt_warnx(gpt, "No primary or secondary GPT tables, "
    175   1.8  christos 		    "can't recover");
    176   1.8  christos 		return -1;
    177   1.8  christos 	}
    178   1.8  christos 
    179   1.8  christos 	if (gpt->gpt != NULL &&
    180  1.17    martin 	    le64toh(((struct gpt_hdr *)(gpt->gpt->map_data))->hdr_lba_alt) !=
    181  1.16   jnemeth 	    (uint64_t)last) {
    182   1.8  christos 		gpt_warnx(gpt, "Media size has changed, please use "
    183  1.11  christos 		   "'%s resizedisk'", getprogname());
    184   1.8  christos 		return -1;
    185   1.8  christos 	}
    186   1.8  christos 
    187   1.8  christos 	if (gpt->tbl != NULL && gpt->lbt == NULL) {
    188  1.10  christos 		if (recover_gpt_tbl(gpt, MAP_TYPE_SEC_GPT_TBL,
    189  1.16   jnemeth 		    last - gpt->tbl->map_size) == -1)
    190   1.8  christos 			return -1;
    191   1.8  christos 	} else if (gpt->tbl == NULL && gpt->lbt != NULL) {
    192  1.10  christos 		if (recover_gpt_tbl(gpt, MAP_TYPE_PRI_GPT_TBL, 2LL) == -1)
    193   1.8  christos 			return -1;
    194   1.8  christos 	}
    195   1.8  christos 
    196   1.8  christos 	if (gpt->gpt != NULL && gpt->tpg == NULL) {
    197  1.16   jnemeth 		if (recover_gpt_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
    198   1.8  christos 			return -1;
    199   1.8  christos 	} else if (gpt->gpt == NULL && gpt->tpg != NULL) {
    200  1.10  christos 		if (recover_gpt_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1LL) == -1)
    201   1.8  christos 			return -1;
    202   1.1  christos 	}
    203  1.16   jnemeth 
    204  1.16   jnemeth 	/*
    205  1.16   jnemeth 	 * Create PMBR if it doesn't already exist.
    206  1.16   jnemeth 	 */
    207  1.16   jnemeth 	if (map_find(gpt, MAP_TYPE_PMBR) == NULL) {
    208  1.16   jnemeth 		if (map_free(gpt, 0LL, 1LL) == 0) {
    209  1.16   jnemeth 			gpt_warnx(gpt, "No room for the PMBR");
    210  1.16   jnemeth 			return -1;
    211  1.16   jnemeth 		}
    212  1.16   jnemeth 		mbr = gpt_read(gpt, 0LL, 1);
    213  1.16   jnemeth 		if (mbr == NULL) {
    214  1.16   jnemeth 			gpt_warnx(gpt, "Error reading MBR");
    215  1.16   jnemeth 			return -1;
    216  1.16   jnemeth 		}
    217  1.16   jnemeth 		memset(mbr, 0, sizeof(*mbr));
    218  1.16   jnemeth 		mbr->mbr_sig = htole16(MBR_SIG);
    219  1.16   jnemeth 		gpt_create_pmbr_part(mbr->mbr_part, last, 0);
    220  1.16   jnemeth 
    221  1.16   jnemeth 		map = map_add(gpt, 0LL, 1LL, MAP_TYPE_PMBR, mbr, 1);
    222  1.16   jnemeth 		if (gpt_write(gpt, map) == -1) {
    223  1.16   jnemeth 			gpt_warn(gpt, "Can't write PMBR");
    224  1.16   jnemeth 			return -1;
    225  1.16   jnemeth 		}
    226  1.16   jnemeth 		gpt_msg(gpt,
    227  1.16   jnemeth 		    "Recreated PMBR (you may need to rerun 'gpt biosboot'");
    228  1.16   jnemeth 	}
    229   1.8  christos 	return 0;
    230   1.1  christos }
    231   1.1  christos 
    232   1.9  christos static int
    233   1.8  christos cmd_recover(gpt_t gpt, int argc, char *argv[])
    234   1.1  christos {
    235  1.21  christos 	if (argc != 2)
    236   1.9  christos 		return usage();
    237   1.1  christos 
    238  1.19   jnemeth 	return recover(gpt);
    239   1.1  christos }
    240