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