3027 installgrub can segfault when encountering bogus data on disk
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <assert.h>
30 #include <locale.h>
31 #include <strings.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/multiboot.h>
35 #include <sys/sysmacros.h>
36
37 #include "installboot.h"
38 #include "./../common/bblk_einfo.h"
39 #include "./../common/boot_utils.h"
40 #include "./../common/mboot_extra.h"
41
42 #ifndef TEXT_DOMAIN
43 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
44 #endif
45
46 /*
47 * SPARC bootblock installation:
48 *
49 * The bootblock resides in blocks 1 to 15 (disk label is at block 0).
50 * The ZFS boot block is larger than what will fit into these first 7.5K so we
51 * break it up and write the remaining portion into the ZFS provided boot block
52 * region at offset 512K. If versioning is requested, we add a multiboot
53 * header at the end of the bootblock, followed by the extra payload area and
54 * place the extended information structure within the latter.
55 */
56
57 static boolean_t force_update = B_FALSE;
58 static boolean_t do_getinfo = B_FALSE;
59 static boolean_t do_version = B_FALSE;
60 static boolean_t do_mirror_bblk = B_FALSE;
61 static boolean_t strip = B_FALSE;
62 static boolean_t verbose_dump = B_FALSE;
63
64 static char *update_str;
65 static int tgt_fs_type = TARGET_IS_UFS;
66 char mboot_scan[MBOOT_SCAN_SIZE];
67
68 /* Function prototypes. */
69 static int read_bootblock_from_file(char *, ib_data_t *data);
70 static int read_bootblock_from_disk(int, ib_bootblock_t *);
71 static void add_bootblock_einfo(ib_bootblock_t *, char *);
72 static int prepare_bootblock(ib_data_t *, char *);
73 static int write_zfs_bootblock(ib_data_t *);
74 static int write_bootblock(ib_data_t *);
75 static int open_device(ib_device_t *);
76 static int init_device(ib_device_t *, char *);
77 static void cleanup_device(ib_device_t *);
78 static int commit_to_disk(ib_data_t *, char *);
79 static int handle_install(char *, char **);
80 static int handle_getinfo(char *, char **);
81 static int handle_mirror(char *, char **);
82 static boolean_t is_update_necessary(ib_data_t *, char *);
83 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
84 static void usage(char *);
85
86 static int
87 read_bootblock_from_file(char *file, ib_data_t *data)
88 {
89 ib_device_t *device = &data->device;
90 ib_bootblock_t *bblock = &data->bootblock;
91 struct stat sb;
92 uint32_t buf_size;
93 int fd = -1;
94 int retval = BC_ERROR;
95
96 assert(data != NULL);
97 assert(file != NULL);
98
99 fd = open(file, O_RDONLY);
100 if (fd == -1) {
101 BOOT_DEBUG("Error opening %s\n", file);
102 perror("open");
103 goto out;
104 }
105
106 if (fstat(fd, &sb) == -1) {
107 BOOT_DEBUG("Error getting information (stat) about %s", file);
108 perror("stat");
109 goto outfd;
110 }
111
112 bblock->file_size = sb.st_size;
113 BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size);
114
115 /* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
116 if (!is_zfs(device->type)) {
117 buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE);
118 if (buf_size > BBLK_DATA_RSVD_SIZE) {
119 BOOT_DEBUG("boot block size is bigger than allowed\n");
120 goto outfd;
121 }
122 } else {
123 buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE,
124 SECTOR_SIZE);
125 if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) {
126 (void) fprintf(stderr, gettext("WARNING, bootblock size"
127 " does not allow to place extended versioning "
128 "information.. skipping\n"));
129 do_version = B_FALSE;
130 }
131 }
132
133 bblock->buf_size = buf_size;
134 BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
135 bblock->buf_size);
136
137 bblock->buf = malloc(buf_size);
138 if (bblock->buf == NULL) {
139 perror(gettext("Memory allocation failure"));
140 goto outbuf;
141 }
142 bblock->file = bblock->buf;
143
144 if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) {
145 BOOT_DEBUG("Read from %s failed\n", file);
146 perror("read");
147 goto outfd;
148 }
149
150 /* If not on ZFS, we are done here. */
151 if (!is_zfs(device->type)) {
152 BOOT_DEBUG("Reading of the bootblock done\n");
153 retval = BC_SUCCESS;
154 goto outfd;
155 }
156 /*
157 * We place the multiboot header right after the file, followed by
158 * the extended information structure.
159 */
160 bblock->mboot = (multiboot_header_t *)(bblock->file +
161 P2ROUNDUP(bblock->file_size, 8));
162 bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
163 BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
164 bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size);
165
166 (void) close(fd);
167 return (BC_SUCCESS);
168
169 outbuf:
170 (void) free(bblock->buf);
171 bblock->buf = NULL;
172 outfd:
173 (void) close(fd);
174 out:
175 return (retval);
176 }
177
178 static int
179 read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock)
180 {
181 char *dest;
182 uint32_t size;
183 uint32_t buf_size;
184 uint32_t mboot_off;
185 multiboot_header_t *mboot;
186
187 assert(bblock != NULL);
188 assert(dev_fd != -1);
189
190 /*
191 * The ZFS bootblock is divided in two parts, but the fake multiboot
192 * header can only be in the second part (the one contained in the ZFS
193 * reserved area).
194 */
195 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
196 BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
197 BOOT_DEBUG("Error reading ZFS reserved area\n");
198 perror("read");
199 return (BC_ERROR);
200 }
201
202 /* No multiboot means no chance of knowing bootblock size */
203 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
204 != BC_SUCCESS) {
205 BOOT_DEBUG("Unable to find multiboot header\n");
206 return (BC_NOEXTRA);
207 }
208 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
209
210 /*
211 * Currently, the amount of space reserved for extra information
212 * is "fixed". We may have to scan for the terminating extra payload
213 * in the future.
214 */
215 size = mboot->load_end_addr - mboot->load_addr;
216 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
217 bblock->file_size = size;
218
219 bblock->buf = malloc(buf_size);
220 if (bblock->buf == NULL) {
221 BOOT_DEBUG("Unable to allocate enough memory to read"
222 " the extra bootblock from the disk\n");
223 perror(gettext("Memory allocation failure"));
224 return (BC_ERROR);
225 }
226 bblock->buf_size = buf_size;
227
228 dest = bblock->buf;
229 size = BBLK_DATA_RSVD_SIZE;
230
231 if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) {
232 BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
233 size);
234 (void) free(bblock->buf);
235 bblock->buf = NULL;
236 return (BC_ERROR);
237 }
238
239 dest += BBLK_DATA_RSVD_SIZE;
240 size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
241
242 if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
243 BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
244 (void) free(bblock->buf);
245 bblock->buf = NULL;
246 return (BC_ERROR);
247 }
248
249 /* Update pointers. */
250 bblock->file = bblock->buf;
251 bblock->mboot_off = mboot_off;
252 bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off
253 + BBLK_DATA_RSVD_SIZE);
254 bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
255 bblock->extra_size = bblock->buf_size - bblock->mboot_off
256 - BBLK_DATA_RSVD_SIZE - sizeof (multiboot_header_t);
257 return (BC_SUCCESS);
258 }
259
260 static boolean_t
261 is_update_necessary(ib_data_t *data, char *updt_str)
262 {
263 bblk_einfo_t *einfo;
264 bblk_hs_t bblock_hs;
265 ib_bootblock_t bblock_disk;
266 ib_bootblock_t *bblock_file = &data->bootblock;
267 ib_device_t *device = &data->device;
268 int dev_fd = device->fd;
269
270 assert(data != NULL);
271 assert(device->fd != -1);
272
273 /* Nothing to do if we are not updating a ZFS bootblock. */
274 if (!is_zfs(device->type))
275 return (B_TRUE);
276
277 bzero(&bblock_disk, sizeof (ib_bootblock_t));
278
279 if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
280 BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
281 return (B_TRUE);
282 }
283
284 einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size);
285 if (einfo == NULL) {
286 BOOT_DEBUG("No extended information available\n");
287 return (B_TRUE);
288 }
289
290 if (!do_version || updt_str == NULL) {
291 (void) fprintf(stdout, "WARNING: target device %s has a "
292 "versioned bootblock that is going to be overwritten by a "
293 "non versioned one\n", device->path);
294 return (B_TRUE);
295 }
296
297 if (force_update) {
298 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
299 return (B_TRUE);
300 }
301
302 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
303
304 bblock_hs.src_buf = (unsigned char *)bblock_file->file;
305 bblock_hs.src_size = bblock_file->file_size;
306
307 return (einfo_should_update(einfo, &bblock_hs, updt_str));
308 }
309
310 static void
311 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
312 {
313 bblk_hs_t hs;
314 uint32_t avail_space;
315
316 assert(bblock != NULL);
317
318 if (updt_str == NULL) {
319 BOOT_DEBUG("WARNING: no update string passed to "
320 "add_bootblock_einfo()\n");
321 return;
322 }
323
324 /* Fill bootblock hashing source information. */
325 hs.src_buf = (unsigned char *)bblock->file;
326 hs.src_size = bblock->file_size;
327 /* How much space for the extended information structure? */
328 avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
329 /* Place the extended information structure. */
330 add_einfo(bblock->extra, updt_str, &hs, avail_space);
331 }
332
333
334 static int
335 prepare_bootblock(ib_data_t *data, char *updt_str)
336 {
337 ib_device_t *device = &data->device;
338 ib_bootblock_t *bblock = &data->bootblock;
339 multiboot_header_t *mboot;
340
341 assert(data != NULL);
342
343 /* Nothing to do if we are not on ZFS. */
344 if (!is_zfs(device->type))
345 return (BC_SUCCESS);
346
347 /*
348 * Write the fake multiboot structure followed by the extra information
349 * data. Both mboot and extra pointers have already been filled up to
350 * point to the right location in the buffer. We prepare the fake
351 * multiboot regardless if versioning was requested or not because
352 * we need it for mirroring support.
353 */
354 assert(bblock->mboot != NULL);
355 assert(bblock->extra != NULL);
356
357 mboot = bblock->mboot;
358
359 mboot->magic = MB_HEADER_MAGIC;
360 mboot->flags = MB_HEADER_FLAGS_64;
361 mboot->checksum = -(mboot->flags + mboot->magic);
362 /*
363 * Flags include the AOUT_KLUDGE and we use the extra members to specify
364 * the size of the bootblock.
365 */
366 mboot->header_addr = bblock->mboot_off;
367 mboot->load_addr = 0;
368 mboot->load_end_addr = bblock->file_size;
369
370 /*
371 * Now that we have the mboot header in place, we can add the extended
372 * versioning information. Since the multiboot header has been placed
373 * after the file image, the hashing will still reflect the one of the
374 * file on the disk.
375 */
376 if (do_version)
377 add_bootblock_einfo(bblock, updt_str);
378
379 return (BC_SUCCESS);
380 }
381
382 static int
383 write_zfs_bootblock(ib_data_t *data)
384 {
385 ib_device_t *device = &data->device;
386 ib_bootblock_t *bblock = &data->bootblock;
387 char *bufptr;
388 uint32_t size;
389
390 assert(data != NULL);
391 assert(device->fd != -1);
392
393 /*
394 * In the ZFS case we actually perform two different steps:
395 * - write the first 15 blocks of the bootblock to the reserved disk
396 * blocks.
397 * - write the remaining blocks in the ZFS reserved area at offset
398 * 512K.
399 */
400 bufptr = bblock->buf;
401 size = BBLK_DATA_RSVD_SIZE;
402
403 if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
404 BOOT_DEBUG("Error writing first 15 blocks of %s\n",
405 device->path);
406 perror("write");
407 return (BC_ERROR);
408 }
409
410 bufptr += BBLK_DATA_RSVD_SIZE;
411 size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
412
413 if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
414 != BC_SUCCESS) {
415 BOOT_DEBUG("Error writing the second part of ZFS bootblock "
416 "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
417 return (BC_ERROR);
418 }
419 return (BC_SUCCESS);
420 }
421
422 static int
423 write_bootblock(ib_data_t *data)
424 {
425 ib_device_t *device = &data->device;
426 ib_bootblock_t *bblock = &data->bootblock;
427 int ret;
428
429 assert(data != NULL);
430
431 /*
432 * If we are on UFS or HSFS we simply write out to the reserved
433 * blocks (1 to 15) the boot block.
434 */
435 if (!is_zfs(device->type)) {
436 if (write_out(device->fd, bblock->buf, bblock->buf_size,
437 SECTOR_SIZE) != BC_SUCCESS) {
438 BOOT_DEBUG("Error writing bootblock to %s\n",
439 device->path);
440 return (BC_ERROR);
441 } else {
442 return (BC_SUCCESS);
443 }
444 } else {
445 ret = write_zfs_bootblock(data);
446 return (ret);
447 }
448 }
449
450 static int
451 open_device(ib_device_t *device)
452 {
453 struct stat statbuf;
454
455 device->fd = open(device->path, O_RDWR);
456 if (device->fd == -1) {
457 BOOT_DEBUG("Unable to open %s\n", device->path);
458 perror("open");
459 return (BC_ERROR);
460 }
461
462 if (fstat(device->fd, &statbuf) != 0) {
463 BOOT_DEBUG("Unable to stat %s\n", device->path);
464 perror("stat");
465 (void) close(device->fd);
466 return (BC_ERROR);
467 }
468
469 if (S_ISCHR(statbuf.st_mode) == 0) {
470 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
471 device->path);
472 return (BC_ERROR);
473 }
474
475 return (BC_SUCCESS);
476 }
477
478 static int
479 init_device(ib_device_t *device, char *path)
480 {
481 bzero(device, sizeof (*device));
482 device->fd = -1;
483
484 device->path = strdup(path);
485 if (path == NULL) {
486 perror(gettext("Memory allocation failure"));
487 return (BC_ERROR);
488 }
489
490 device->type = tgt_fs_type;
491 if (open_device(device) != BC_SUCCESS)
492 return (BC_ERROR);
493
494 return (BC_SUCCESS);
495 }
496
497 static void
498 cleanup_device(ib_device_t *device)
499 {
500 free(device->path);
501 bzero(device, sizeof (*device));
502
503 if (device->fd != -1)
504 (void) close(device->fd);
505 }
506
507 static void
508 cleanup_bootblock(ib_bootblock_t *bblock)
509 {
510 free(bblock->buf);
511 bzero(bblock, sizeof (ib_bootblock_t));
512 }
513
514 /*
515 * Propagate the bootblock on the source disk to the destination disk and
516 * version it with 'updt_str' in the process. Since we cannot trust any data
517 * on the attaching disk, we do not perform any specific check on a potential
518 * target extended information structure and we just blindly update.
519 */
520 static int
521 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
522 {
523 ib_bootblock_t *src_bblock = &src->bootblock;
524 ib_bootblock_t *dest_bblock = &dest->bootblock;
525 uint32_t buf_size;
526
527 assert(src != NULL);
528 assert(dest != NULL);
529
530 cleanup_bootblock(dest_bblock);
531
532 if (updt_str != NULL) {
533 do_version = B_TRUE;
534 } else {
535 do_version = B_FALSE;
536 }
537
538 buf_size = src_bblock->file_size + SECTOR_SIZE;
539
540 dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
541 dest_bblock->buf = malloc(dest_bblock->buf_size);
542 if (dest_bblock->buf == NULL) {
543 perror(gettext("Memory Allocation Failure"));
544 return (BC_ERROR);
545 }
546 dest_bblock->file = dest_bblock->buf;
547 dest_bblock->file_size = src_bblock->file_size;
548 (void) memcpy(dest_bblock->file, src_bblock->file,
549 dest_bblock->file_size);
550
551 dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
552 P2ROUNDUP(dest_bblock->file_size, 8));
553 dest_bblock->extra = (char *)dest_bblock->mboot +
554 sizeof (multiboot_header_t);
555
556 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
557 src->device.path, dest->device.path);
558
559 return (commit_to_disk(dest, updt_str));
560 }
561
562 static int
563 commit_to_disk(ib_data_t *data, char *update_str)
564 {
565 assert(data != NULL);
566
567 if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
568 (void) fprintf(stderr, gettext("Error updating the bootblock "
569 "image\n"));
570 return (BC_ERROR);
571 }
572
573 if (write_bootblock(data) != BC_SUCCESS) {
574 (void) fprintf(stderr, gettext("Error writing bootblock to "
575 "disk\n"));
576 return (BC_ERROR);
577 }
578
579 return (BC_SUCCESS);
580 }
581
582
583 /*
584 * Install a new bootblock on the given device. handle_install() expects argv
585 * to contain 2 parameters (the target device path and the path to the
586 * bootblock.
587 *
588 * Returns: BC_SUCCESS - if the installation is successful
589 * BC_ERROR - if the installation failed
590 * BC_NOUPDT - if no installation was performed because the
591 * version currently installed is more recent than the
592 * supplied one.
593 *
594 */
595 static int
596 handle_install(char *progname, char **argv)
597 {
598 ib_data_t install_data;
599 char *bootblock = NULL;
600 char *device_path = NULL;
601 int ret = BC_ERROR;
602
603 bootblock = strdup(argv[0]);
604 device_path = strdup(argv[1]);
605
606 if (!device_path || !bootblock) {
607 (void) fprintf(stderr, gettext("Missing parameter"));
608 usage(progname);
609 goto out;
610 }
611
612 BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
613 bootblock);
614 bzero(&install_data, sizeof (ib_data_t));
615
616 if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
617 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
618 device_path);
619 goto out;
620 }
621
622 if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
623 (void) fprintf(stderr, gettext("Error reading %s\n"),
624 bootblock);
625 goto out_dev;
626 }
627 /* Versioning is only supported for the ZFS bootblock. */
628 if (do_version && !is_zfs(install_data.device.type)) {
629 (void) fprintf(stderr, gettext("Versioning is only supported on"
630 " ZFS... skipping.\n"));
631 do_version = B_FALSE;
632 }
633
634 /*
635 * is_update_necessary() will take care of checking if versioning and/or
636 * forcing the update have been specified. It will also emit a warning
637 * if a non-versioned update is attempted over a versioned bootblock.
638 */
639 if (!is_update_necessary(&install_data, update_str)) {
640 (void) fprintf(stderr, gettext("bootblock version installed "
641 "on %s is more recent or identical\n"
642 "Use -F to override or install without the -u option\n"),
643 device_path);
644 ret = BC_NOUPDT;
645 goto out_dev;
646 }
647
648 BOOT_DEBUG("Ready to commit to disk\n");
649 ret = commit_to_disk(&install_data, update_str);
650
651 out_dev:
652 cleanup_device(&install_data.device);
653 out:
654 free(bootblock);
655 free(device_path);
656 return (ret);
657 }
658
659 /*
660 * Retrieves from a device the extended information (einfo) associated to the
661 * installed bootblock.
662 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
663 * Returns:
664 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
665 * - BC_ERROR (on error)
666 * - BC_NOEINFO (no extended information available)
667 */
668 static int
669 handle_getinfo(char *progname, char **argv)
670 {
671
672 ib_data_t data;
673 ib_bootblock_t *bblock = &data.bootblock;
674 ib_device_t *device = &data.device;
675 bblk_einfo_t *einfo;
676 uint8_t flags = 0;
677 uint32_t size;
678 char *device_path;
679 int retval = BC_ERROR;
680 int ret;
681
682 device_path = strdup(argv[0]);
683 if (!device_path) {
684 (void) fprintf(stderr, gettext("Missing parameter"));
685 usage(progname);
686 goto out;
687 }
688
689 bzero(&data, sizeof (ib_data_t));
690 BOOT_DEBUG("device path: %s\n", device_path);
691
692 if (init_device(device, device_path) != BC_SUCCESS) {
693 (void) fprintf(stderr, gettext("Unable to gather device "
694 "information from %s\n"), device_path);
695 goto out_dev;
696 }
697
698 if (!is_zfs(device->type)) {
699 (void) fprintf(stderr, gettext("Versioning only supported on "
700 "ZFS\n"));
701 goto out_dev;
702 }
703
704 ret = read_bootblock_from_disk(device->fd, bblock);
705 if (ret == BC_ERROR) {
706 (void) fprintf(stderr, gettext("Error reading bootblock from "
707 "%s\n"), device_path);
708 goto out_dev;
709 }
710
711 if (ret == BC_NOEXTRA) {
712 BOOT_DEBUG("No multiboot header found on %s, unable "
713 "to locate extra information area (old/non versioned "
714 "bootblock?) \n", device_path);
715 (void) fprintf(stderr, gettext("No extended information "
716 "found\n"));
717 retval = BC_NOEINFO;
718 goto out_dev;
719 }
720
721 einfo = find_einfo(bblock->extra, bblock->extra_size);
722 if (einfo == NULL) {
723 retval = BC_NOEINFO;
724 (void) fprintf(stderr, gettext("No extended information "
725 "found\n"));
726 goto out_dev;
727 }
728
729 /* Print the extended information. */
730 if (strip)
731 flags |= EINFO_EASY_PARSE;
732 if (verbose_dump)
733 flags |= EINFO_PRINT_HEADER;
734
735 size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
736 sizeof (multiboot_header_t);
737 print_einfo(flags, einfo, size);
738 retval = BC_SUCCESS;
739
740 out_dev:
741 cleanup_device(&data.device);
742 out:
743 free(device_path);
744 return (retval);
745
746 }
747
748 /*
749 * Attempt to mirror (propagate) the current bootblock over the attaching disk.
750 *
751 * Returns:
752 * - BC_SUCCESS (a successful propagation happened)
753 * - BC_ERROR (an error occurred)
754 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
755 * there is no multiboot information)
756 */
757 static int
758 handle_mirror(char *progname, char **argv)
759 {
760 ib_data_t curr_data;
761 ib_data_t attach_data;
762 ib_device_t *curr_device = &curr_data.device;
763 ib_device_t *attach_device = &attach_data.device;
764 ib_bootblock_t *bblock_curr = &curr_data.bootblock;
765 ib_bootblock_t *bblock_attach = &attach_data.bootblock;
766 bblk_einfo_t *einfo_curr = NULL;
767 char *curr_device_path;
768 char *attach_device_path;
769 char *updt_str = NULL;
770 int retval = BC_ERROR;
771 int ret;
772
773 curr_device_path = strdup(argv[0]);
774 attach_device_path = strdup(argv[1]);
775
776 if (!curr_device_path || !attach_device_path) {
777 (void) fprintf(stderr, gettext("Missing parameter"));
778 usage(progname);
779 goto out;
780 }
781 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
782 " %s\n", curr_device_path, attach_device_path);
783
784 bzero(&curr_data, sizeof (ib_data_t));
785 bzero(&attach_data, sizeof (ib_data_t));
786
787 if (tgt_fs_type != TARGET_IS_ZFS) {
788 (void) fprintf(stderr, gettext("Mirroring is only supported on "
789 "ZFS\n"));
790 return (BC_ERROR);
791 }
792
793 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
794 (void) fprintf(stderr, gettext("Unable to gather device "
795 "information from %s (current device)\n"),
796 curr_device_path);
797 goto out_currdev;
798 }
799
800 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
801 (void) fprintf(stderr, gettext("Unable to gather device "
802 "information from %s (attaching device)\n"),
803 attach_device_path);
804 goto out_devs;
805 }
806
807 ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
808 if (ret == BC_ERROR) {
809 BOOT_DEBUG("Error reading bootblock from %s\n",
810 curr_device->path);
811 retval = BC_ERROR;
812 goto out_devs;
813 }
814
815 if (ret == BC_NOEXTRA) {
816 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
817 " the bootblock\n", curr_device->path);
818 retval = BC_NOEXTRA;
819 goto out_devs;
820 }
821
822 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size);
823 if (einfo_curr != NULL)
824 updt_str = einfo_get_string(einfo_curr);
825
826 retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
827 cleanup_bootblock(bblock_curr);
828 cleanup_bootblock(bblock_attach);
829 out_devs:
830 cleanup_device(attach_device);
831 out_currdev:
832 cleanup_device(curr_device);
833 out:
834 free(curr_device_path);
835 free(attach_device_path);
836 return (retval);
837 }
838
839 #define USAGE_STRING "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk " \
840 "raw-device\n" \
841 "\t%s [-e|-V] -i -F zfs raw-device\n" \
842 "\t%s -M -F zfs raw-device attach-raw-device\n" \
843 "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
844
845 #define CANON_USAGE_STR gettext(USAGE_STRING)
846
847 static void
848 usage(char *progname)
849 {
850 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
851 }
852
853 int
854 main(int argc, char **argv)
855 {
856 int opt;
857 int params = 2;
858 int ret;
859 char *progname;
860 char **handle_args;
861
862 (void) setlocale(LC_ALL, "");
863 (void) textdomain(TEXT_DOMAIN);
864
865 while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
866 switch (opt) {
867 case 'F':
868 if (strcmp(optarg, "ufs") == 0) {
869 tgt_fs_type = TARGET_IS_UFS;
870 } else if (strcmp(optarg, "hsfs") == 0) {
871 tgt_fs_type = TARGET_IS_HSFS;
872 } else if (strcmp(optarg, "zfs") == 0) {
873 tgt_fs_type = TARGET_IS_ZFS;
874 } else {
875 (void) fprintf(stderr, gettext("Wrong "
876 "filesystem specified\n\n"));
877 usage(argv[0]);
878 exit(BC_ERROR);
879 }
880 break;
881 case 'e':
882 strip = B_TRUE;
883 break;
884 case 'f':
885 force_update = B_TRUE;
886 break;
887 case 'V':
888 verbose_dump = B_TRUE;
889 break;
890 case 'i':
891 do_getinfo = B_TRUE;
892 params = 1;
893 break;
894 case 'u':
895 do_version = B_TRUE;
896
897 update_str = malloc(strlen(optarg) + 1);
898 if (update_str == NULL) {
899 perror(gettext("Memory allocation failure"));
900 exit(BC_ERROR);
901 }
902 (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
903 break;
904 case 'M':
905 do_mirror_bblk = B_TRUE;
906 break;
907 case 'h':
908 usage(argv[0]);
909 exit(BC_SUCCESS);
910 break;
911 case 'd':
912 boot_debug = B_TRUE;
913 break;
914 case 'n':
915 nowrite = B_TRUE;
916 break;
917 default:
918 /* fall through to process non-optional args */
919 break;
920 }
921 }
922
923 /* check arguments */
924 if (argc != optind + params) {
925 usage(argv[0]);
926 exit(BC_ERROR);
927 }
928 progname = argv[0];
929 handle_args = argv + optind;
930
931 /* check options. */
932 if (do_getinfo && do_mirror_bblk) {
933 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
934 "specified at the same time\n"));
935 usage(progname);
936 exit(BC_ERROR);
937 }
938
939 if (nowrite)
940 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
941 " be written to disk.\n"));
942
943 if (do_getinfo) {
944 ret = handle_getinfo(progname, handle_args);
945 } else if (do_mirror_bblk) {
946 ret = handle_mirror(progname, handle_args);
947 } else {
948 ret = handle_install(progname, handle_args);
949 }
950 return (ret);
951 }
--- EOF ---