Home | History | Annotate | Download | only in dos
      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