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