1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330, 9 * Boston MA 02111-1307, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- */ 13 14 /* 15 * syslinux.c - Linux installer program for SYSLINUX 16 * 17 * Hacked up for DOS. 18 */ 19 20 #include <errno.h> 21 #include <getopt.h> 22 #include <stdio.h> 23 #include <string.h> 24 #include <stdlib.h> 25 #include <stdarg.h> 26 #include "mystuff.h" 27 28 #include "syslinux.h" 29 #include "libfat.h" 30 #include "setadv.h" 31 #include "sysexits.h" 32 #include "syslxopt.h" 33 #include "syslxint.h" 34 #include "syslxfs.h" 35 36 char *program = "syslinux.com"; /* Name of program */ 37 uint16_t dos_version; 38 39 #ifdef DEBUG 40 # define dprintf printf 41 void pause(void) 42 { 43 uint16_t ax; 44 45 asm volatile("int $0x16" : "=a" (ax) : "a" (0)); 46 } 47 #else 48 # define dprintf(...) ((void)0) 49 # define pause() ((void)0) 50 #endif 51 52 void unlock_device(int); 53 54 void __attribute__ ((noreturn)) die(const char *msg) 55 { 56 unlock_device(0); 57 puts("syslinux: "); 58 puts(msg); 59 putchar('\n'); 60 exit(1); 61 } 62 63 void warning(const char *msg) 64 { 65 puts("syslinux: warning: "); 66 puts(msg); 67 putchar('\n'); 68 } 69 70 /* 71 * read/write wrapper functions 72 */ 73 int creat(const char *filename, int mode) 74 { 75 uint16_t rv; 76 uint8_t err; 77 78 dprintf("creat(\"%s\", 0x%x)\n", filename, mode); 79 80 rv = 0x3C00; 81 asm volatile ("int $0x21 ; setc %0" 82 : "=bcdm" (err), "+a" (rv) 83 : "c" (mode), "d" (filename)); 84 if (err) { 85 dprintf("rv = %d\n", rv); 86 die("cannot open ldlinux.sys"); 87 } 88 89 return rv; 90 } 91 92 void close(int fd) 93 { 94 uint16_t rv = 0x3E00; 95 96 dprintf("close(%d)\n", fd); 97 98 asm volatile ("int $0x21":"+a" (rv) 99 :"b"(fd)); 100 101 /* The only error MS-DOS returns for close is EBADF, 102 and we really don't care... */ 103 } 104 105 int rename(const char *oldname, const char *newname) 106 { 107 uint16_t rv = 0x5600; /* Also support 43FFh? */ 108 uint8_t err; 109 110 dprintf("rename(\"%s\", \"%s\")\n", oldname, newname); 111 112 asm volatile ("int $0x21 ; setc %0":"=bcdm" (err), "+a"(rv) 113 :"d"(oldname), "D"(newname)); 114 115 if (err) { 116 dprintf("rv = %d\n", rv); 117 warning("cannot move ldlinux.sys"); 118 return rv; 119 } 120 121 return 0; 122 } 123 124 ssize_t write_file_sl(int fd, const unsigned char _slimg *buf, 125 const unsigned int len) 126 { 127 uint32_t filepos = 0; 128 uint16_t rv; 129 uint8_t err; 130 131 while (filepos < len) { 132 uint16_t seg = ((size_t)buf >> 4) + ds(); 133 uint16_t offset = (size_t)buf & 15; 134 uint32_t chunk = len - filepos; 135 if (chunk > 32768 - offset) 136 chunk = 32768 - offset; 137 asm volatile ("pushw %%ds ; " 138 "movw %6,%%ds ; " 139 "int $0x21 ; " 140 "popw %%ds ; " "setc %0":"=bcdm" (err), "=a"(rv) 141 :"a"(0x4000), "b"(fd), "c"(chunk), "d" (offset), 142 "SD" (seg)); 143 if (err || rv == 0) 144 die("file write error"); 145 filepos += rv; 146 buf += rv; 147 } 148 149 return filepos; 150 } 151 152 ssize_t write_file(int fd, const void *buf, size_t count) 153 { 154 uint16_t rv; 155 ssize_t done = 0; 156 uint8_t err; 157 158 dprintf("write_file(%d,%p,%u)\n", fd, buf, count); 159 160 while (count) { 161 asm volatile ("int $0x21 ; setc %0":"=bcdm" (err), "=a"(rv) 162 :"a"(0x4000), "b"(fd), "c"(count), "d"(buf)); 163 if (err || rv == 0) 164 die("file write error"); 165 166 done += rv; 167 count -= rv; 168 } 169 170 return done; 171 } 172 173 void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector) 174 { 175 uint16_t errnum = 0x0001; 176 struct diskio dio; 177 178 dprintf("write_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector); 179 180 dio.startsector = sector; 181 dio.sectors = nsecs; 182 dio.bufoffs = (uintptr_t) buf; 183 dio.bufseg = ds(); 184 185 if (dos_version >= 0x070a) { 186 /* Try FAT32-aware system call first */ 187 asm volatile("int $0x21 ; jc 1f ; xorw %0,%0\n" 188 "1:" 189 : "=a" (errnum) 190 : "a" (0x7305), "b" (&dio), "c" (-1), "d" (drive), 191 "S" (1), "m" (dio) 192 : "memory"); 193 dprintf(" rv(7305) = %04x", errnum); 194 } 195 196 /* If not supported, try the legacy system call (int2526.S) */ 197 if (errnum == 0x0001) 198 errnum = int26_write_sector(drive, &dio); 199 200 if (errnum) { 201 dprintf("rv = %04x\n", errnum); 202 die("sector write error"); 203 } 204 } 205 206 void read_device(int drive, void *buf, size_t nsecs, unsigned int sector) 207 { 208 uint16_t errnum = 0x0001; 209 struct diskio dio; 210 211 dprintf("read_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector); 212 213 dio.startsector = sector; 214 dio.sectors = nsecs; 215 dio.bufoffs = (uintptr_t) buf; 216 dio.bufseg = ds(); 217 218 if (dos_version >= 0x070a) { 219 /* Try FAT32-aware system call first */ 220 asm volatile("int $0x21 ; jc 1f ; xorw %0,%0\n" 221 "1:" 222 : "=a" (errnum) 223 : "a" (0x7305), "b" (&dio), "c" (-1), "d" (drive), 224 "S" (0), "m" (dio)); 225 dprintf(" rv(7305) = %04x", errnum); 226 } 227 228 /* If not supported, try the legacy system call (int2526.S) */ 229 if (errnum == 0x0001) 230 errnum = int25_read_sector(drive, &dio); 231 232 if (errnum) { 233 dprintf("rv = %04x\n", errnum); 234 die("sector read error"); 235 } 236 } 237 238 /* Both traditional DOS and FAT32 DOS return this structure, but 239 FAT32 return a lot more data, so make sure we have plenty of space */ 240 struct deviceparams { 241 uint8_t specfunc; 242 uint8_t devtype; 243 uint16_t devattr; 244 uint16_t cylinders; 245 uint8_t mediatype; 246 uint16_t bytespersec; 247 uint8_t secperclust; 248 uint16_t ressectors; 249 uint8_t fats; 250 uint16_t rootdirents; 251 uint16_t sectors; 252 uint8_t media; 253 uint16_t fatsecs; 254 uint16_t secpertrack; 255 uint16_t heads; 256 uint32_t hiddensecs; 257 uint32_t hugesectors; 258 uint8_t lotsofpadding[224]; 259 } __attribute__ ((packed)); 260 261 uint32_t get_partition_offset(int drive) 262 { 263 uint8_t err; 264 uint16_t rv; 265 struct deviceparams dp; 266 267 dp.specfunc = 1; /* Get current information */ 268 269 rv = 0x440d; 270 asm volatile ("int $0x21 ; setc %0" 271 :"=abcdm" (err), "+a"(rv), "=m"(dp) 272 :"b" (drive), "c" (0x0860), "d" (&dp)); 273 274 if (!err) 275 return dp.hiddensecs; 276 277 rv = 0x440d; 278 asm volatile ("int $0x21 ; setc %0" 279 : "=abcdm" (err), "+a" (rv), "=m" (dp) 280 : "b" (drive), "c" (0x4860), "d" (&dp)); 281 282 if (!err) 283 return dp.hiddensecs; 284 285 die("could not find partition start offset"); 286 } 287 288 struct rwblock { 289 uint8_t special; 290 uint16_t head; 291 uint16_t cylinder; 292 uint16_t firstsector; 293 uint16_t sectors; 294 uint16_t bufferoffset; 295 uint16_t bufferseg; 296 } __attribute__ ((packed)); 297 298 static struct rwblock mbr = { 299 .special = 0, 300 .head = 0, 301 .cylinder = 0, 302 .firstsector = 0, /* MS-DOS, unlike the BIOS, zero-base sectors */ 303 .sectors = 1, 304 .bufferoffset = 0, 305 .bufferseg = 0 306 }; 307 308 void write_mbr(int drive, const void *buf) 309 { 310 uint16_t rv; 311 uint8_t err; 312 313 dprintf("write_mbr(%d,%p)", drive, buf); 314 315 mbr.bufferoffset = (uintptr_t) buf; 316 mbr.bufferseg = ds(); 317 318 rv = 0x440d; 319 asm volatile ("int $0x21 ; setc %0" : "=bcdm" (err), "+a"(rv) 320 :"c"(0x0841), "d"(&mbr), "b"(drive), "m"(mbr)); 321 322 dprintf(" rv(0841) = %04x", rv); 323 if (!err) { 324 dprintf("\n"); 325 return; 326 } 327 328 rv = 0x440d; 329 asm volatile ("int $0x21 ; setc %0" : "=bcdm" (err), "+a"(rv) 330 :"c"(0x4841), "d"(&mbr), "b"(drive), "m"(mbr)); 331 332 dprintf(" rv(4841) = %04x\n", rv); 333 if (err) 334 die("mbr write error"); 335 } 336 337 void read_mbr(int drive, const void *buf) 338 { 339 uint16_t rv; 340 uint8_t err; 341 342 dprintf("read_mbr(%d,%p)", drive, buf); 343 344 mbr.bufferoffset = (uintptr_t) buf; 345 mbr.bufferseg = ds(); 346 347 rv = 0x440d; 348 asm volatile ("int $0x21 ; setc %0":"=abcdm" (err), "+a"(rv) 349 :"c"(0x0861), "d"(&mbr), "b"(drive), "m"(mbr)); 350 351 dprintf(" rv(0861) = %04x", rv); 352 if (!err) { 353 dprintf("\n"); 354 return; 355 } 356 357 rv = 0x440d; 358 asm volatile ("int $0x21 ; setc %0":"=abcdm" (err), "+a"(rv) 359 :"c"(0x4861), "d"(&mbr), "b"(drive), "m"(mbr)); 360 361 dprintf(" rv(4841) = %04x\n", rv); 362 if (err) 363 die("mbr read error"); 364 365 dprintf("Bytes: %02x %02x %02x %02x %02x %02x %02x %02x\n", 366 ((const uint8_t *)buf)[0], 367 ((const uint8_t *)buf)[1], 368 ((const uint8_t *)buf)[2], 369 ((const uint8_t *)buf)[3], 370 ((const uint8_t *)buf)[4], 371 ((const uint8_t *)buf)[5], 372 ((const uint8_t *)buf)[6], 373 ((const uint8_t *)buf)[7]); 374 } 375 376 /* This call can legitimately fail, and we don't care, so ignore error return */ 377 void set_attributes(const char *file, int attributes) 378 { 379 uint16_t rv = 0x4301; 380 381 dprintf("set_attributes(\"%s\", 0x%02x)\n", file, attributes); 382 383 asm volatile ("int $0x21":"+a" (rv) 384 :"c"(attributes), "d"(file)); 385 } 386 387 /* 388 * Version of the read_device function suitable for libfat 389 */ 390 int libfat_xpread(intptr_t pp, void *buf, size_t secsize, 391 libfat_sector_t sector) 392 { 393 read_device(pp, buf, 1, sector); 394 return secsize; 395 } 396 397 static inline void get_dos_version(void) 398 { 399 dprintf("DOS version %d.%d\n", (dos_version >> 8), dos_version & 0xff); 400 } 401 402 /* The locking interface relies on static variables. A massive hack :( */ 403 static uint8_t lock_level, lock_drive; 404 405 static inline void set_lock_device(uint8_t device) 406 { 407 lock_level = 0; 408 lock_drive = device; 409 } 410 411 static int do_lock(uint8_t level) 412 { 413 uint16_t level_arg = lock_drive + (level << 8); 414 uint16_t rv; 415 uint8_t err; 416 #if 0 417 /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */ 418 uint16_t lock_call = (dos_version >= 0x070a) ? 0x484A : 0x084A; 419 #else 420 uint16_t lock_call = 0x084A; /* MSDN says this is OK for all filesystems */ 421 #endif 422 423 dprintf("Trying lock %04x... ", level_arg); 424 asm volatile ("int $0x21 ; setc %0" 425 : "=bcdm" (err), "=a" (rv) 426 : "a" (0x440d), "b" (level_arg), 427 "c" (lock_call), "d" (0x0001)); 428 dprintf("%s %04x\n", err ? "err" : "ok", rv); 429 430 return err ? rv : 0; 431 } 432 433 void lock_device(int level) 434 { 435 static int hard_lock = 0; 436 int err; 437 438 if (dos_version < 0x0700) 439 return; /* Win9x/NT only */ 440 441 if (!hard_lock) { 442 /* Assume hierarchial "soft" locking supported */ 443 444 while (lock_level < level) { 445 int new_level = lock_level + 1; 446 err = do_lock(new_level); 447 if (err) { 448 if (err == 0x0001) { 449 /* Try hard locking next */ 450 hard_lock = 1; 451 } 452 goto soft_fail; 453 } 454 455 lock_level = new_level; 456 } 457 return; 458 } 459 460 soft_fail: 461 if (hard_lock) { 462 /* Hard locking, only level 4 supported */ 463 /* This is needed for Win9x in DOS mode */ 464 465 err = do_lock(4); 466 if (err) { 467 if (err == 0x0001) { 468 /* Assume locking is not needed */ 469 return; 470 } 471 goto hard_fail; 472 } 473 474 lock_level = 4; 475 return; 476 } 477 478 hard_fail: 479 die("could not lock device"); 480 } 481 482 void unlock_device(int level) 483 { 484 uint16_t rv; 485 uint8_t err; 486 uint16_t unlock_call; 487 488 if (dos_version < 0x0700) 489 return; /* Win9x/NT only */ 490 491 #if 0 492 /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */ 493 unlock_call = (dos_version >= 0x070a) ? 0x486A : 0x086A; 494 #else 495 unlock_call = 0x086A; /* MSDN says this is OK for all filesystems */ 496 #endif 497 498 if (lock_level == 4 && level > 0) 499 return; /* Only drop the hard lock at the end */ 500 501 while (lock_level > level) { 502 uint8_t new_level = (lock_level == 4) ? 0 : lock_level - 1; 503 uint16_t level_arg = (new_level << 8) + lock_drive; 504 rv = 0x440d; 505 dprintf("Trying unlock %04x... ", new_level); 506 asm volatile ("int $0x21 ; setc %0" 507 : "=bcdm" (err), "+a" (rv) 508 : "b" (level_arg), "c" (unlock_call)); 509 dprintf("%s %04x\n", err ? "err" : "ok", rv); 510 lock_level = new_level; 511 } 512 } 513 514 /* 515 * This function does any desired MBR manipulation; called with the device lock held. 516 */ 517 struct mbr_entry { 518 uint8_t active; /* Active flag */ 519 uint8_t bhead; /* Begin head */ 520 uint8_t bsector; /* Begin sector */ 521 uint8_t bcylinder; /* Begin cylinder */ 522 uint8_t filesystem; /* Filesystem value */ 523 uint8_t ehead; /* End head */ 524 uint8_t esector; /* End sector */ 525 uint8_t ecylinder; /* End cylinder */ 526 uint32_t startlba; /* Start sector LBA */ 527 uint32_t sectors; /* Length in sectors */ 528 } __attribute__ ((packed)); 529 530 static void adjust_mbr(int device, int writembr, int set_active) 531 { 532 static unsigned char sectbuf[SECTOR_SIZE]; 533 int i; 534 535 if (!writembr && !set_active) 536 return; /* Nothing to do */ 537 538 read_mbr(device, sectbuf); 539 540 if (writembr) { 541 memcpy(sectbuf, syslinux_mbr, syslinux_mbr_len); 542 *(uint16_t *) (sectbuf + 510) = 0xaa55; 543 } 544 545 if (set_active) { 546 uint32_t offset = get_partition_offset(device); 547 struct mbr_entry *me = (struct mbr_entry *)(sectbuf + 446); 548 int found = 0; 549 550 dprintf("Searching for partition offset: %08x\n", offset); 551 552 for (i = 0; i < 4; i++) { 553 if (me->startlba == offset) { 554 me->active = 0x80; 555 found++; 556 } else { 557 me->active = 0; 558 } 559 me++; 560 } 561 562 if (found < 1) { 563 die("partition not found (-a is not implemented for logical partitions)"); 564 } else if (found > 1) { 565 die("multiple aliased partitions found"); 566 } 567 } 568 569 write_mbr(device, sectbuf); 570 } 571 572 static void move_file(int dev_fd, char *pathname, char *filename) 573 { 574 char new_name[160]; 575 char *cp = new_name + 3; 576 const char *sd; 577 int slash = 1; 578 579 new_name[0] = dev_fd | 0x40; 580 new_name[1] = ':'; 581 new_name[2] = '\\'; 582 583 for (sd = opt.directory; *sd; sd++) { 584 char c = *sd; 585 586 if (c == '/' || c == '\\') { 587 if (slash) 588 continue; 589 c = '\\'; 590 slash = 1; 591 } else { 592 slash = 0; 593 } 594 595 *cp++ = c; 596 } 597 598 /* Skip if subdirectory == root */ 599 if (cp > new_name + 3) { 600 if (!slash) 601 *cp++ = '\\'; 602 603 memcpy(cp, filename, 12); 604 605 set_attributes(pathname, 0); 606 if (rename(pathname, new_name)) 607 set_attributes(pathname, 0x07); 608 else 609 set_attributes(new_name, 0x07); 610 } 611 } 612 613 int main(int argc, char *argv[]) 614 { 615 static unsigned char sectbuf[SECTOR_SIZE]; 616 int dev_fd, fd; 617 static char ldlinux_name[] = "@:\\ldlinux.sys"; 618 static char ldlinuxc32_name[] = "@:\\ldlinux.c32"; 619 struct libfat_filesystem *fs; 620 libfat_sector_t s, *secp; 621 libfat_sector_t *sectors; 622 int ldlinux_sectors; 623 int32_t ldlinux_cluster; 624 int nsectors; 625 const char *errmsg; 626 int i; 627 int patch_sectors; 628 unsigned char *dp; 629 630 dprintf("argv = %p\n", argv); 631 for (i = 0; i <= argc; i++) 632 dprintf("argv[%d] = %p = \"%s\"\n", i, argv[i], argv[i]); 633 634 get_dos_version(); 635 636 argv[0] = program; 637 parse_options(argc, argv, MODE_SYSLINUX_DOSWIN); 638 639 if (!opt.device) 640 usage(EX_USAGE, MODE_SYSLINUX_DOSWIN); 641 if (opt.sectors || opt.heads || opt.reset_adv || opt.set_once 642 || (opt.update_only > 0) || opt.menu_save || opt.offset) { 643 fprintf(stderr, 644 "At least one specified option not yet implemented" 645 " for this installer.\n"); 646 exit(1); 647 } 648 649 /* 650 * Create an ADV in memory... this should be smarter. 651 */ 652 syslinux_reset_adv(syslinux_adv); 653 654 /* 655 * Figure out which drive we're talking to 656 */ 657 dev_fd = (opt.device[0] & ~0x20) - 0x40; 658 if (dev_fd < 1 || dev_fd > 26 || opt.device[1] != ':' || opt.device[2]) 659 usage(EX_USAGE, MODE_SYSLINUX_DOSWIN); 660 661 set_lock_device(dev_fd); 662 663 lock_device(2); /* Make sure we can lock the device */ 664 read_device(dev_fd, sectbuf, 1, 0); 665 unlock_device(1); 666 667 /* 668 * Check to see that what we got was indeed an MS-DOS boot sector/superblock 669 */ 670 if ((errmsg = syslinux_check_bootsect(sectbuf, NULL))) { 671 unlock_device(0); 672 puts(errmsg); 673 putchar('\n'); 674 exit(1); 675 } 676 677 ldlinux_name[0] = dev_fd | 0x40; 678 ldlinuxc32_name[0] = dev_fd | 0x40; 679 680 set_attributes(ldlinux_name, 0); 681 fd = creat(ldlinux_name, 0); /* SYSTEM HIDDEN READONLY */ 682 write_file_sl(fd, syslinux_ldlinux, syslinux_ldlinux_len); 683 write_file(fd, syslinux_adv, 2 * ADV_SIZE); 684 close(fd); 685 set_attributes(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */ 686 687 set_attributes(ldlinuxc32_name, 0); 688 fd = creat(ldlinuxc32_name, 0); /* SYSTEM HIDDEN READONLY */ 689 write_file_sl(fd, syslinux_ldlinuxc32, syslinux_ldlinuxc32_len); 690 close(fd); 691 set_attributes(ldlinuxc32_name, 0x07); /* SYSTEM HIDDEN READONLY */ 692 693 /* 694 * Now, use libfat to create a block map. This probably 695 * should be changed to use ioctl(...,FIBMAP,...) since 696 * this is supposed to be a simple, privileged version 697 * of the installer. 698 */ 699 ldlinux_sectors = (syslinux_ldlinux_len + 2 * ADV_SIZE 700 + SECTOR_SIZE - 1) >> SECTOR_SHIFT; 701 sectors = calloc(ldlinux_sectors, sizeof *sectors); 702 lock_device(2); 703 fs = libfat_open(libfat_xpread, dev_fd); 704 ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); 705 secp = sectors; 706 nsectors = 0; 707 s = libfat_clustertosector(fs, ldlinux_cluster); 708 while (s && nsectors < ldlinux_sectors) { 709 *secp++ = s; 710 nsectors++; 711 s = libfat_nextsector(fs, s); 712 } 713 libfat_close(fs); 714 715 /* 716 * If requested, move ldlinux.sys 717 */ 718 if (opt.directory) { 719 move_file(dev_fd, ldlinux_name, "ldlinux.sys"); 720 move_file(dev_fd, ldlinuxc32_name, "ldlinux.c32"); 721 } 722 723 /* 724 * Patch ldlinux.sys and the boot sector 725 */ 726 i = syslinux_patch(sectors, nsectors, opt.stupid_mode, opt.raid_mode, opt.directory, NULL); 727 patch_sectors = (i + SECTOR_SIZE - 1) >> SECTOR_SHIFT; 728 729 /* 730 * Overwrite the now-patched ldlinux.sys 731 */ 732 /* lock_device(3); -- doesn't seem to be needed */ 733 dp = syslinux_ldlinux; 734 for (i = 0; i < patch_sectors; i++) { 735 memcpy_from_sl(sectbuf, dp, SECTOR_SIZE); 736 dp += SECTOR_SIZE; 737 write_device(dev_fd, sectbuf, 1, sectors[i]); 738 } 739 740 /* 741 * Muck with the MBR, if desired, while we hold the lock 742 */ 743 adjust_mbr(dev_fd, opt.install_mbr, opt.activate_partition); 744 745 /* 746 * To finish up, write the boot sector 747 */ 748 749 /* Read the superblock again since it might have changed while mounted */ 750 read_device(dev_fd, sectbuf, 1, 0); 751 752 /* Copy the syslinux code into the boot sector */ 753 syslinux_make_bootsect(sectbuf, VFAT); 754 755 /* Write new boot sector */ 756 if (opt.bootsecfile) { 757 unlock_device(0); 758 fd = creat(opt.bootsecfile, 0x20); /* ARCHIVE */ 759 write_file(fd, sectbuf, SECTOR_SIZE); 760 close(fd); 761 } else { 762 write_device(dev_fd, sectbuf, 1, 0); 763 unlock_device(0); 764 } 765 766 /* Done! */ 767 768 return 0; 769 } 770