PSARC/2006/379 Solaris on Extended partition
[illumos-gate.git] / usr / src / cmd / boot / installgrub / installgrub.c
blob209c993e976014b0131f456485f9445be203d879
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <libgen.h>
29 #include <malloc.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <strings.h>
36 #include <sys/mount.h>
37 #include <sys/mnttab.h>
38 #include <sys/dktp/fdisk.h>
39 #include <sys/dkio.h>
40 #include <sys/vtoc.h>
42 #include <libintl.h>
43 #include <locale.h>
44 #include "message.h"
45 #include <errno.h>
46 #include <libfdisk.h>
48 #ifndef TEXT_DOMAIN
49 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
50 #endif
52 #define SECTOR_SIZE 0x200
53 #define STAGE2_MEMADDR 0x8000 /* loading addr of stage2 */
55 #define STAGE1_BPB_OFFSET 0x3
56 #define STAGE1_BPB_SIZE 0x3B
57 #define STAGE1_BOOT_DRIVE 0x40
58 #define STAGE1_FORCE_LBA 0x41
59 #define STAGE1_STAGE2_ADDRESS 0x42
60 #define STAGE1_STAGE2_SECTOR 0x44
61 #define STAGE1_STAGE2_SEGMENT 0x48
63 #define STAGE2_BLOCKLIST (SECTOR_SIZE - 0x8)
64 #define STAGE2_INSTALLPART (SECTOR_SIZE + 0x8)
65 #define STAGE2_FORCE_LBA (SECTOR_SIZE + 0x11)
66 #define STAGE2_VER_STRING (SECTOR_SIZE + 0x12)
67 #define STAGE2_BLKOFF 50 /* offset from start of fdisk part */
69 static int nowrite = 0;
70 static int write_mboot = 0;
71 static int force_mboot = 0;
72 static int is_floppy = 0;
73 static int is_bootpar = 0;
74 static int stage2_fd;
75 static int partition, slice = 0xff;
76 static char *device_p0;
77 static uint32_t stage2_first_sector, stage2_second_sector;
80 static char bpb_sect[SECTOR_SIZE];
81 static char boot_sect[SECTOR_SIZE];
82 static char stage1_buffer[SECTOR_SIZE];
83 static char stage2_buffer[2 * SECTOR_SIZE];
84 static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)];
86 static int open_device(char *);
87 static void read_bpb_sect(int);
88 static void read_boot_sect(char *);
89 static void write_boot_sect(char *);
90 static void read_stage1_stage2(char *, char *);
91 static void modify_and_write_stage1(int);
92 static void modify_and_write_stage2(int);
93 static unsigned int get_start_sector(int);
94 static void copy_stage2(int, char *);
95 static char *get_raw_partition(char *);
96 static void usage(char *);
98 extern int read_stage2_blocklist(int, unsigned int *);
101 main(int argc, char *argv[])
103 int dev_fd, opt;
104 char *stage1, *stage2, *device;
106 (void) setlocale(LC_ALL, "");
107 (void) textdomain(TEXT_DOMAIN);
109 while ((opt = getopt(argc, argv, "fmn")) != EOF) {
110 switch (opt) {
111 case 'm':
112 write_mboot = 1;
113 break;
114 case 'n':
115 nowrite = 1;
116 break;
117 case 'f':
118 force_mboot = 1;
119 break;
120 default:
121 /* fall through to process non-optional args */
122 break;
126 /* check arguments */
127 if (argc != optind + 3) {
128 usage(argv[0]);
131 if (nowrite) {
132 (void) fprintf(stdout, DRY_RUN);
135 stage1 = strdup(argv[optind]);
136 stage2 = strdup(argv[optind + 1]);
137 device = strdup(argv[optind + 2]);
139 if (!stage1 || !stage2 || !device) {
140 usage(argv[0]);
143 /* open and check device type */
144 dev_fd = open_device(device);
146 /* read in stage1 and stage2 into buffer */
147 read_stage1_stage2(stage1, stage2);
149 /* In the pcfs case, write a fresh stage2 */
150 if (is_floppy || is_bootpar) {
151 copy_stage2(dev_fd, device);
152 read_bpb_sect(dev_fd);
155 /* read in boot sector */
156 if (!is_floppy)
157 read_boot_sect(device);
159 /* modify stage1 based on grub needs */
160 modify_and_write_stage1(dev_fd);
162 /* modify stage2 and write to media */
163 modify_and_write_stage2(dev_fd);
165 if (!is_floppy && write_mboot)
166 write_boot_sect(device);
167 (void) close(dev_fd);
169 return (0);
172 static unsigned int
173 get_start_sector(int fd)
175 static unsigned int start_sect = 0;
176 uint32_t secnum, numsec;
177 int i, pno, rval, ext_sol_part_found = 0;
178 struct mboot *mboot;
179 struct ipart *part;
180 ext_part_t *epp;
182 if (start_sect)
183 return (start_sect);
185 mboot = (struct mboot *)boot_sect;
186 for (i = 0; i < FD_NUMPART; i++) {
187 part = (struct ipart *)mboot->parts + i;
188 if (is_bootpar) {
189 if (part->systid == 0xbe)
190 break;
194 /* Read extended partition to find a solaris partition */
195 if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK))
196 != FDISK_SUCCESS) {
197 switch (rval) {
199 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
200 * be considered as soft errors and hence
201 * we do not exit
203 case FDISK_EBADLOGDRIVE:
204 break;
205 case FDISK_ENOLOGDRIVE:
206 break;
207 case FDISK_ENOVGEOM:
208 fprintf(stderr, "Could not get virtual"
209 " geometry for this device\n");
210 exit(1);
211 break;
212 case FDISK_ENOPGEOM:
213 fprintf(stderr, "Could not get physical"
214 " geometry for this device\n");
215 exit(1);
216 break;
217 case FDISK_ENOLGEOM:
218 fprintf(stderr, "Could not get label"
219 " geometry for this device\n");
220 exit(1);
221 break;
222 default:
223 perror("Failed to initialise libfdisk.\n");
224 exit(1);
225 break;
229 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
230 if (rval == FDISK_SUCCESS) {
231 ext_sol_part_found = 1;
233 libfdisk_fini(&epp);
236 * If there is no boot partition, find the solaris partition
239 if (i == FD_NUMPART) {
240 struct part_info dkpi;
241 struct extpart_info edkpi;
244 * Get the solaris partition information from the device
245 * and compare the offset of S2 with offset of solaris partition
246 * from fdisk partition table.
248 if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
249 if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
250 (void) fprintf(stderr, PART_FAIL);
251 exit(-1);
252 } else {
253 edkpi.p_start = dkpi.p_start;
257 for (i = 0; i < FD_NUMPART; i++) {
258 part = (struct ipart *)mboot->parts + i;
260 if (part->relsect == 0) {
261 (void) fprintf(stderr, BAD_PART, i);
262 exit(-1);
265 if (fdisk_is_dos_extended(part->systid))
266 continue;
268 if (edkpi.p_start >= part->relsect &&
269 edkpi.p_start < (part->relsect + part->numsect)) {
270 /* Found the partition */
271 break;
276 if ((i == FD_NUMPART) && (!ext_sol_part_found)) {
277 (void) fprintf(stderr, BOOTPAR);
278 exit(-1);
281 /* get confirmation for -m */
282 if (write_mboot && !force_mboot) {
283 (void) fprintf(stdout, MBOOT_PROMPT);
284 if (getchar() != 'y') {
285 write_mboot = 0;
286 (void) fprintf(stdout, MBOOT_NOT_UPDATED);
290 if ((i == FD_NUMPART) && (ext_sol_part_found)) {
291 start_sect = secnum;
292 partition = pno;
293 } else {
294 start_sect = part->relsect;
295 partition = i;
298 if (part->bootid != 128 && write_mboot == 0) {
299 (void) fprintf(stdout, BOOTPAR_INACTIVE, i + 1);
302 return (start_sect);
305 static void
306 usage(char *progname)
308 (void) fprintf(stderr, USAGE, basename(progname));
309 exit(-1);
312 static int
313 open_device(char *device)
315 int dev_fd;
316 struct stat stat;
317 char *raw_part;
319 is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) &&
320 strncmp(device, "/dev/dsk", strlen("/dev/dsk"));
322 /* handle boot partition specification */
323 if (!is_floppy && strstr(device, "p0:boot")) {
324 is_bootpar = 1;
327 raw_part = get_raw_partition(device);
329 if (nowrite)
330 dev_fd = open(raw_part, O_RDONLY);
331 else
332 dev_fd = open(raw_part, O_RDWR);
334 if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) {
335 (void) fprintf(stderr, OPEN_FAIL, raw_part);
336 exit(-1);
338 if (S_ISCHR(stat.st_mode) == 0) {
339 (void) fprintf(stderr, NOT_RAW_DEVICE, raw_part);
340 exit(-1);
343 return (dev_fd);
346 static void
347 read_stage1_stage2(char *stage1, char *stage2)
349 int fd;
351 /* read the stage1 file from filesystem */
352 fd = open(stage1, O_RDONLY);
353 if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
354 (void) fprintf(stderr, READ_FAIL_STAGE1, stage1);
355 exit(-1);
357 (void) close(fd);
359 /* read first two blocks of stage 2 from filesystem */
360 stage2_fd = open(stage2, O_RDONLY);
361 if (stage2_fd == -1 ||
362 read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE)
363 != 2 * SECTOR_SIZE) {
364 (void) fprintf(stderr, READ_FAIL_STAGE2, stage2);
365 exit(-1);
367 /* leave the stage2 file open for later */
370 static void
371 read_bpb_sect(int dev_fd)
373 if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) {
374 (void) fprintf(stderr, READ_FAIL_BPB);
375 exit(-1);
379 static void
380 read_boot_sect(char *device)
382 static int read_mbr = 0;
383 int i, fd;
384 char save[2];
386 if (read_mbr)
387 return;
388 read_mbr = 1;
390 /* get the whole disk (p0) */
391 i = strlen(device);
392 save[0] = device[i - 2];
393 save[1] = device[i - 1];
394 device[i - 2] = 'p';
395 device[i - 1] = '0';
397 device_p0 = strdup(device);
398 fd = open(device, O_RDONLY);
399 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
400 (void) fprintf(stderr, READ_FAIL_MBR, device);
401 if (fd == -1)
402 perror("open");
403 else
404 perror("read");
405 exit(-1);
407 (void) close(fd);
408 device[i - 2] = save[0];
409 device[i - 1] = save[1];
412 static void
413 write_boot_sect(char *device)
415 int fd, len;
416 char *raw, *end;
417 struct stat stat;
419 /* make a copy and chop off ":boot" */
420 raw = strdup(device);
421 end = strstr(raw, "p0:boot");
422 if (end)
423 end[2] = 0;
425 /* open p0 (whole disk) */
426 len = strlen(raw);
427 raw[len - 2] = 'p';
428 raw[len - 1] = '0';
429 fd = open(raw, O_WRONLY);
430 if (fd == -1 || fstat(fd, &stat) != 0) {
431 (void) fprintf(stderr, OPEN_FAIL, raw);
432 exit(-1);
434 if (!nowrite &&
435 pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
436 (void) fprintf(stderr, WRITE_FAIL_BOOTSEC);
437 exit(-1);
439 (void) fprintf(stdout, WRITE_MBOOT);
440 (void) close(fd);
443 static void
444 modify_and_write_stage1(int dev_fd)
446 if (is_floppy) {
447 stage2_first_sector = blocklist[0];
448 /* copy bios parameter block (for fat fs) */
449 bcopy(bpb_sect + STAGE1_BPB_OFFSET,
450 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
451 } else if (is_bootpar) {
452 stage2_first_sector = get_start_sector(dev_fd) + blocklist[0];
453 /* copy bios parameter block (for fat fs) and MBR */
454 bcopy(bpb_sect + STAGE1_BPB_OFFSET,
455 stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
456 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
457 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
458 } else {
459 stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF;
460 /* copy MBR to stage1 in case of overwriting MBR sector */
461 bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
462 *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
465 /* modify default stage1 file generated by GRUB */
466 *((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR))
467 = stage2_first_sector;
468 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS))
469 = STAGE2_MEMADDR;
470 *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT))
471 = STAGE2_MEMADDR >> 4;
474 * XXX the default grub distribution also:
475 * - Copy the possible MBR/extended part table
476 * - Set the boot drive of stage1
479 /* write stage1/pboot to 1st sector */
480 if (!nowrite &&
481 pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
482 (void) fprintf(stderr, WRITE_FAIL_PBOOT);
483 exit(-1);
486 if (is_floppy) {
487 (void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY);
488 } else {
489 (void) fprintf(stdout, WRITE_PBOOT,
490 partition, get_start_sector(dev_fd));
494 #define START_BLOCK(pos) (*(ulong_t *)(pos))
495 #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4))
496 #define START_SEG(pos) (*(ushort_t *)((pos) + 6))
498 static void
499 modify_and_write_stage2(int dev_fd)
501 int nrecord;
502 off_t offset;
504 if (is_floppy || is_bootpar) {
505 int i = 0;
506 uint32_t partition_offset;
507 uint32_t install_addr = 0x8200;
508 uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST;
510 stage2_first_sector = blocklist[0];
512 /* figure out the second sector */
513 if (blocklist[1] > 1) {
514 blocklist[0]++;
515 blocklist[1]--;
516 } else {
517 i += 2;
519 stage2_second_sector = blocklist[i];
521 if (is_floppy)
522 partition_offset = 0;
523 else /* solaris boot partition */
524 partition_offset = get_start_sector(dev_fd);
526 /* install the blocklist at the end of stage2_buffer */
527 while (blocklist[i]) {
528 if (START_BLOCK(pos - 8) != 0 &&
529 START_BLOCK(pos - 8) != blocklist[i + 2]) {
530 (void) fprintf(stderr, PCFS_FRAGMENTED);
531 exit(-1);
533 START_BLOCK(pos) = blocklist[i] + partition_offset;
534 START_SEG(pos) = (ushort_t)(install_addr >> 4);
535 NUM_BLOCK(pos) = blocklist[i + 1];
536 install_addr += blocklist[i + 1] * SECTOR_SIZE;
537 pos -= 8;
538 i += 2;
541 } else {
543 * In a solaris partition, stage2 is written to contiguous
544 * blocks. So we update the starting block only.
546 *((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) =
547 stage2_first_sector + 1;
550 if (is_floppy) {
551 /* modify the config file to add (fd0) */
552 char *config_file = stage2_buffer + STAGE2_VER_STRING;
553 while (*config_file++)
555 strcpy(config_file, "(fd0)/boot/grub/menu.lst");
556 } else {
557 /* force lba and set disk partition */
558 *((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1;
559 *((long *)(stage2_buffer + STAGE2_INSTALLPART))
560 = (partition << 16) | (slice << 8) | 0xff;
563 /* modification done, now do the writing */
564 if (is_floppy || is_bootpar) {
565 /* we rewrite block 0 and 1 and that's it */
566 if (!nowrite &&
567 (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
568 stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE ||
569 pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
570 stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) {
571 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
572 exit(-1);
574 (void) fprintf(stdout, WRITE_STAGE2_PCFS);
575 return;
578 /* for disk, write stage2 starting at STAGE2_BLKOFF sector */
579 offset = STAGE2_BLKOFF;
581 /* write the modified first two sectors */
582 if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
583 offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) {
584 (void) fprintf(stderr, WRITE_FAIL_STAGE2);
585 exit(-1);
588 /* write the remaining sectors */
589 nrecord = 2;
590 offset += 2;
591 for (;;) {
592 int nread, nwrite;
593 nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE,
594 nrecord * SECTOR_SIZE);
595 if (nread > 0 && !nowrite)
596 nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
597 offset * SECTOR_SIZE);
598 else
599 nwrite = SECTOR_SIZE;
600 if (nread < 0 || nwrite != SECTOR_SIZE) {
601 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
602 nread, nwrite);
603 break;
605 if (nread > 0) {
606 nrecord ++;
607 offset ++;
609 if (nread < SECTOR_SIZE)
610 break; /* end of file */
612 (void) fprintf(stdout, WRITE_STAGE2_DISK,
613 partition, nrecord, STAGE2_BLKOFF, stage2_first_sector);
616 static char *
617 get_raw_partition(char *device)
619 int len;
620 struct mboot *mboot;
621 static char *raw = NULL;
623 if (raw)
624 return (raw);
625 raw = strdup(device);
627 if (is_floppy)
628 return (raw);
630 if (is_bootpar) {
631 int i;
632 char *end = strstr(raw, "p0:boot");
634 end[2] = 0; /* chop off :boot */
635 read_boot_sect(raw);
636 mboot = (struct mboot *)boot_sect;
637 for (i = 0; i < FD_NUMPART; i++) {
638 struct ipart *part = (struct ipart *)mboot->parts + i;
639 if (part->systid == 0xbe) /* solaris boot part */
640 break;
643 if (i == FD_NUMPART) {
644 (void) fprintf(stderr, BOOTPAR_NOTFOUND, device);
645 exit(-1);
647 end[1] = '1' + i; /* set partition name */
648 return (raw);
651 /* For disk, remember slice and return whole fdisk partition */
652 len = strlen(raw);
653 if (raw[len - 2] != 's' || raw[len - 1] == '2') {
654 (void) fprintf(stderr, NOT_ROOT_SLICE);
655 exit(-1);
657 slice = atoi(&raw[len - 1]);
659 raw[len - 2] = 's';
660 raw[len - 1] = '2';
661 return (raw);
664 #define TMP_MNTPT "/tmp/installgrub_pcfs"
665 static void
666 copy_stage2(int dev_fd, char *device)
668 FILE *mntfp;
669 int i, pcfs_fp;
670 char buf[SECTOR_SIZE];
671 char *cp;
672 struct mnttab mp = {0}, mpref = {0};
674 /* convert raw to block device name by removing the first 'r' */
675 (void) strncpy(buf, device, sizeof (buf));
676 buf[sizeof (buf) - 1] = 0;
677 cp = strchr(buf, 'r');
678 if (cp == NULL) {
679 (void) fprintf(stderr, CONVERT_FAIL, device);
680 exit(-1);
682 do {
683 *cp = *(cp + 1);
684 } while (*(++cp));
686 /* get the mount point, if any */
687 mntfp = fopen("/etc/mnttab", "r");
688 if (mntfp == NULL) {
689 (void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
690 exit(-1);
693 mpref.mnt_special = buf;
694 if (getmntany(mntfp, &mp, &mpref) != 0) {
695 char cmd[128];
697 /* not mounted, try remount */
698 (void) mkdir(TMP_MNTPT, S_IRWXU);
699 (void) snprintf(cmd, sizeof (cmd), "mount -F pcfs %s %s",
700 buf, TMP_MNTPT);
701 (void) system(cmd);
702 rewind(mntfp);
703 bzero(&mp, sizeof (mp));
704 if (getmntany(mntfp, &mp, &mpref) != 0) {
705 (void) fprintf(stderr, MOUNT_FAIL, buf);
706 exit(-1);
710 (void) snprintf(buf, sizeof (buf),
711 "%s/boot", mp.mnt_mountp);
712 (void) mkdir(buf, S_IRWXU);
713 (void) strcat(buf, "/grub");
714 (void) mkdir(buf, S_IRWXU);
716 (void) strcat(buf, "/stage2");
717 pcfs_fp = open(buf, O_WRONLY | O_CREAT, S_IRWXU);
718 if (pcfs_fp == -1) {
719 (void) fprintf(stderr, OPEN_FAIL_FILE, buf);
720 perror("open:");
721 (void) umount(TMP_MNTPT);
722 exit(-1);
725 /* write stage2 to pcfs */
726 for (i = 0; ; i++) {
727 int nread, nwrite;
728 nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE);
729 if (nowrite)
730 nwrite = nread;
731 else
732 nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE);
733 if (nread < 0 || nwrite != nread) {
734 (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
735 nread, nwrite);
736 break;
738 if (nread < SECTOR_SIZE)
739 break; /* end of file */
741 (void) close(pcfs_fp);
742 (void) umount(TMP_MNTPT);
745 * Now, get the blocklist from the device.
747 bzero(blocklist, sizeof (blocklist));
748 if (read_stage2_blocklist(dev_fd, blocklist) != 0)
749 exit(-1);