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/create.c,v 1.11 2005/08/31 01:47:19 marcel Exp $"); 34 #endif 35 #ifdef __RCSID 36 __RCSID("$NetBSD: restore.c,v 1.20 2020/06/07 05:42:25 thorpej Exp $"); 37 #endif 38 39 #include <sys/types.h> 40 #include <sys/bootblock.h> 41 #include <sys/disklabel_gpt.h> 42 43 #include <err.h> 44 #include <stddef.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <prop/proplib.h> 50 51 #include "map.h" 52 #include "gpt.h" 53 #include "gpt_private.h" 54 55 static int cmd_restore(gpt_t, int, char *[]); 56 57 static const char *restorehelp[] = { 58 "[-F] [-i infile]", 59 }; 60 61 struct gpt_cmd c_restore = { 62 "restore", 63 cmd_restore, 64 restorehelp, __arraycount(restorehelp), 65 GPT_SYNC, 66 }; 67 68 #define usage() gpt_usage(NULL, &c_restore) 69 70 #define PROP_ERR(x) if (!(x)) { \ 71 gpt_warnx(gpt, "proplib failure"); \ 72 return -1; \ 73 } 74 75 static int 76 restore_mbr(gpt_t gpt, struct mbr *mbr, prop_dictionary_t mbr_dict, off_t last) 77 { 78 unsigned int i; 79 struct mbr_part *part; 80 81 PROP_ERR(prop_dictionary_get_uint(mbr_dict, "index", &i)); 82 part = &mbr->mbr_part[i]; 83 84 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "flag", &part->part_flag)); 85 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "start_head", 86 &part->part_shd)); 87 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "start_sector", 88 &part->part_ssect )); 89 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "start_cylinder", 90 &part->part_scyl)); 91 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "type", 92 &part->part_typ)); 93 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "end_head", 94 &part->part_ehd)); 95 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "end_sector", 96 &part->part_esect)); 97 PROP_ERR(prop_dictionary_get_uint8(mbr_dict, "end_cylinder", 98 &part->part_ecyl)); 99 PROP_ERR(prop_dictionary_get_uint16(mbr_dict, "lba_start_low", 100 &part->part_start_lo)); 101 part->part_start_lo = htole16(part->part_start_lo); 102 PROP_ERR(prop_dictionary_get_uint16(mbr_dict, "lba_start_high", 103 &part->part_start_hi)); 104 part->part_start_hi = htole16(part->part_start_hi); 105 106 /* adjust PMBR size to size of device */ 107 if (part->part_typ == MBR_PTYPE_PMBR) { 108 if (last > 0xffffffff) { 109 mbr->mbr_part[0].part_size_lo = htole16(0xffff); 110 mbr->mbr_part[0].part_size_hi = htole16(0xffff); 111 } else { 112 mbr->mbr_part[0].part_size_lo = htole16((uint16_t)last); 113 mbr->mbr_part[0].part_size_hi = htole16( 114 (uint16_t)(last >> 16)); 115 } 116 } else { 117 PROP_ERR(prop_dictionary_get_uint16(mbr_dict, "lba_size_low", 118 &part->part_size_lo)); 119 part->part_size_lo = htole16(part->part_size_lo); 120 PROP_ERR(prop_dictionary_get_uint16(mbr_dict, "lba_size_high", 121 &part->part_size_hi)); 122 part->part_size_hi = htole16(part->part_size_hi); 123 } 124 return 0; 125 } 126 127 static int 128 restore_ent(gpt_t gpt, prop_dictionary_t gpt_dict, void *secbuf, u_int gpt_size, 129 u_int entries) 130 { 131 unsigned int i; 132 struct gpt_ent ent; 133 const char *s; 134 135 memset(&ent, 0, sizeof(ent)); 136 PROP_ERR(prop_dictionary_get_string(gpt_dict, "type", &s)); 137 if (gpt_uuid_parse(s, ent.ent_type) != 0) { 138 gpt_warnx(gpt, "%s: not able to convert to an UUID", s); 139 return -1; 140 } 141 PROP_ERR(prop_dictionary_get_string(gpt_dict, "guid", &s)); 142 if (gpt_uuid_parse(s, ent.ent_guid) != 0) { 143 gpt_warnx(gpt, "%s: not able to convert to an UUID", s); 144 return -1; 145 } 146 PROP_ERR(prop_dictionary_get_uint64(gpt_dict, "start", 147 &ent.ent_lba_start)); 148 ent.ent_lba_start = htole64(ent.ent_lba_start); 149 PROP_ERR(prop_dictionary_get_uint64(gpt_dict, "end", 150 &ent.ent_lba_end)); 151 ent.ent_lba_end = htole64(ent.ent_lba_end); 152 PROP_ERR(prop_dictionary_get_uint64(gpt_dict, "attributes", 153 &ent.ent_attr)); 154 ent.ent_attr = htole64(ent.ent_attr); 155 156 if (prop_dictionary_get_string(gpt_dict, "name", &s)) { 157 utf8_to_utf16((const uint8_t *)s, ent.ent_name, 158 __arraycount(ent.ent_name)); 159 } 160 PROP_ERR(prop_dictionary_get_uint(gpt_dict, "index", &i)); 161 if (i > entries) { 162 gpt_warnx(gpt, "Entity index out of bounds %u > %u\n", 163 i, entries); 164 return -1; 165 } 166 memcpy((char *)secbuf + gpt->secsz + ((i - 1) * sizeof(ent)), 167 &ent, sizeof(ent)); 168 return 0; 169 } 170 171 static int 172 restore(gpt_t gpt, const char *infile, int force) 173 { 174 gpt_uuid_t gpt_guid, uuid; 175 off_t firstdata, last, lastdata, gpe_start, gpe_end; 176 map_t map; 177 struct mbr *mbr; 178 struct gpt_hdr *hdr; 179 unsigned int i, gpt_size; 180 prop_dictionary_t props, gpt_dict, mbr_dict, type_dict; 181 prop_object_iterator_t propiter; 182 prop_data_t propdata; 183 prop_array_t mbr_array, gpt_array; 184 prop_number_t propnum; 185 unsigned int entries; 186 const char *s; 187 void *secbuf = NULL; 188 int rv = -1; 189 190 last = gpt->mediasz / gpt->secsz - 1LL; 191 192 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL || 193 map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) { 194 if (!force) { 195 gpt_warnx(gpt, "Device contains a GPT"); 196 return -1; 197 } 198 } 199 map = map_find(gpt, MAP_TYPE_MBR); 200 if (map != NULL) { 201 if (!force) { 202 gpt_warnx(gpt, "Device contains an MBR"); 203 return -1; 204 } 205 /* Nuke the MBR in our internal map. */ 206 map->map_type = MAP_TYPE_UNUSED; 207 } 208 209 props = prop_dictionary_internalize_from_file( 210 strcmp(infile, "-") == 0 ? "/dev/stdin" : infile); 211 if (props == NULL) { 212 gpt_warnx(gpt, "Unable to read/parse backup file"); 213 return -1; 214 } 215 216 propnum = prop_dictionary_get(props, "sector_size"); 217 PROP_ERR(propnum); 218 if (!prop_number_equals_signed(propnum, gpt->secsz)) { 219 gpt_warnx(gpt, "Sector size does not match backup"); 220 prop_object_release(props); 221 return -1; 222 } 223 224 gpt_dict = prop_dictionary_get(props, "GPT_HDR"); 225 PROP_ERR(gpt_dict); 226 227 propnum = prop_dictionary_get(gpt_dict, "revision"); 228 PROP_ERR(propnum); 229 if (!prop_number_equals_unsigned(propnum, 0x10000)) { 230 gpt_warnx(gpt, "backup is not revision 1.0"); 231 prop_object_release(gpt_dict); 232 prop_object_release(props); 233 return -1; 234 } 235 236 PROP_ERR(prop_dictionary_get_uint(gpt_dict, "entries", &entries)); 237 238 gpt_size = (u_int)(entries * sizeof(struct gpt_ent) / gpt->secsz); 239 if (gpt_size * sizeof(struct gpt_ent) % gpt->secsz) 240 gpt_size++; 241 242 PROP_ERR(prop_dictionary_get_string(gpt_dict, "guid", &s)); 243 if (gpt_uuid_parse(s, gpt_guid) != 0) { 244 gpt_warnx(gpt, "%s: not able to convert to an UUID", s); 245 goto out; 246 } 247 firstdata = gpt_size + 2; /* PMBR and GPT header */ 248 lastdata = last - gpt_size - 1; /* alt. GPT table and header */ 249 250 type_dict = prop_dictionary_get(props, "GPT_TBL"); 251 PROP_ERR(type_dict); 252 gpt_array = prop_dictionary_get(type_dict, "gpt_array"); 253 PROP_ERR(gpt_array); 254 propiter = prop_array_iterator(gpt_array); 255 PROP_ERR(propiter); 256 while ((gpt_dict = prop_object_iterator_next(propiter)) != NULL) { 257 PROP_ERR(prop_dictionary_get_string(gpt_dict, "type", &s)); 258 if (gpt_uuid_parse(s, uuid) != 0) { 259 gpt_warnx(gpt, "%s: not able to convert to an UUID", s); 260 goto out; 261 } 262 if (gpt_uuid_is_nil(uuid)) 263 continue; 264 PROP_ERR(prop_dictionary_get_int64(gpt_dict, "start", 265 &gpe_start)); 266 PROP_ERR(prop_dictionary_get_int64(gpt_dict, "end", 267 &gpe_end)); 268 if (gpe_start < firstdata || gpe_end > lastdata) { 269 gpt_warnx(gpt, "Backup GPT doesn't fit"); 270 goto out; 271 } 272 } 273 prop_object_iterator_release(propiter); 274 275 /* GPT TABLE + GPT HEADER */ 276 if ((secbuf = calloc(gpt_size + 1, gpt->secsz)) == NULL) { 277 gpt_warn(gpt, "not enough memory to create a sector buffer"); 278 goto out; 279 } 280 281 if (lseek(gpt->fd, 0LL, SEEK_SET) == -1) { 282 gpt_warn(gpt, "Can't seek to beginning"); 283 goto out; 284 } 285 for (i = 0; i < firstdata; i++) { 286 if (write(gpt->fd, secbuf, gpt->secsz) != (ssize_t)gpt->secsz) { 287 gpt_warn(gpt, "Error writing"); 288 goto out; 289 } 290 } 291 if (lseek(gpt->fd, (lastdata + 1) * gpt->secsz, SEEK_SET) == -1) { 292 gpt_warn(gpt, "Can't seek to end"); 293 goto out; 294 } 295 for (i = (u_int)(lastdata + 1); i <= (u_int)last; i++) { 296 if (write(gpt->fd, secbuf, gpt->secsz) != (ssize_t)gpt->secsz) { 297 gpt_warn(gpt, "Error writing"); 298 goto out; 299 } 300 } 301 302 mbr = secbuf; 303 type_dict = prop_dictionary_get(props, "MBR"); 304 PROP_ERR(type_dict); 305 propdata = prop_dictionary_get(type_dict, "code"); 306 PROP_ERR(propdata); 307 memcpy(mbr->mbr_code, prop_data_value(propdata), 308 sizeof(mbr->mbr_code)); 309 mbr_array = prop_dictionary_get(type_dict, "mbr_array"); 310 PROP_ERR(mbr_array); 311 propiter = prop_array_iterator(mbr_array); 312 PROP_ERR(propiter); 313 while ((mbr_dict = prop_object_iterator_next(propiter)) != NULL) { 314 if (restore_mbr(gpt, mbr, mbr_dict, last) == -1) 315 goto out; 316 } 317 318 prop_object_iterator_release(propiter); 319 mbr->mbr_sig = htole16(MBR_SIG); 320 if (lseek(gpt->fd, 0LL, SEEK_SET) == -1 || 321 write(gpt->fd, mbr, gpt->secsz) != (ssize_t)gpt->secsz) { 322 gpt_warn(gpt, "Unable to seek/write MBR"); 323 return -1; 324 } 325 326 propiter = prop_array_iterator(gpt_array); 327 PROP_ERR(propiter); 328 329 while ((gpt_dict = prop_object_iterator_next(propiter)) != NULL) { 330 if (restore_ent(gpt, gpt_dict, secbuf, gpt_size, entries) == -1) 331 goto out; 332 } 333 prop_object_iterator_release(propiter); 334 335 size_t len = gpt_size * gpt->secsz; 336 if (lseek(gpt->fd, 2 * gpt->secsz, SEEK_SET) == -1 || 337 write(gpt->fd, (char *)secbuf + gpt->secsz, len) != (ssize_t) len) { 338 gpt_warn(gpt, "Unable to write primary GPT"); 339 goto out; 340 } 341 342 if (lseek(gpt->fd, (lastdata + 1) * gpt->secsz, SEEK_SET) == -1 || 343 write(gpt->fd, (char *)secbuf + gpt->secsz, len) != (ssize_t) len) { 344 gpt_warn(gpt, "Unable to write secondary GPT"); 345 goto out; 346 } 347 348 memset(secbuf, 0, gpt->secsz); 349 hdr = secbuf; 350 memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); 351 hdr->hdr_revision = htole32(GPT_HDR_REVISION); 352 hdr->hdr_size = htole32(GPT_HDR_SIZE); 353 hdr->hdr_lba_self = htole64(GPT_HDR_BLKNO); 354 hdr->hdr_lba_alt = htole64((uint64_t)last); 355 hdr->hdr_lba_start = htole64((uint64_t)firstdata); 356 hdr->hdr_lba_end = htole64((uint64_t)lastdata); 357 gpt_uuid_copy(hdr->hdr_guid, gpt_guid); 358 hdr->hdr_lba_table = htole64(2); 359 hdr->hdr_entries = htole32(entries); 360 hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); 361 hdr->hdr_crc_table = htole32(crc32((char *)secbuf + gpt->secsz, len)); 362 hdr->hdr_crc_self = htole32(crc32(hdr, GPT_HDR_SIZE)); 363 if (lseek(gpt->fd, gpt->secsz, SEEK_SET) == -1 || 364 write(gpt->fd, hdr, gpt->secsz) != (ssize_t)gpt->secsz) { 365 gpt_warn(gpt, "Unable to write primary header"); 366 goto out; 367 } 368 369 hdr->hdr_lba_self = htole64((uint64_t)last); 370 hdr->hdr_lba_alt = htole64(GPT_HDR_BLKNO); 371 hdr->hdr_lba_table = htole64((uint64_t)(lastdata + 1)); 372 hdr->hdr_crc_self = 0; 373 hdr->hdr_crc_self = htole32(crc32(hdr, GPT_HDR_SIZE)); 374 if (lseek(gpt->fd, last * gpt->secsz, SEEK_SET) == -1 || 375 write(gpt->fd, hdr, gpt->secsz) != (ssize_t)gpt->secsz) { 376 gpt_warn(gpt, "Unable to write secondary header"); 377 goto out; 378 } 379 rv = 0; 380 381 out: 382 free(secbuf); 383 prop_object_release(props); 384 return rv; 385 } 386 387 static int 388 cmd_restore(gpt_t gpt, int argc, char *argv[]) 389 { 390 int ch; 391 int force = 0; 392 const char *infile = "-"; 393 394 while ((ch = getopt(argc, argv, "Fi:")) != -1) { 395 switch(ch) { 396 case 'i': 397 infile = optarg; 398 break; 399 case 'F': 400 force = 1; 401 break; 402 default: 403 return usage(); 404 } 405 } 406 407 if (argc != optind) 408 return usage(); 409 410 return restore(gpt, infile, force); 411 } 412