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
pause(void)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 
die(const char * msg)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 
warning(const char * msg)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  */
creat(const char * filename,int mode)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 
close(int fd)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 
rename(const char * oldname,const char * newname)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 
write_file_sl(int fd,const unsigned char _slimg * buf,const unsigned int len)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 
write_file(int fd,const void * buf,size_t count)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 
write_device(int drive,const void * buf,size_t nsecs,unsigned int sector)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 
read_device(int drive,void * buf,size_t nsecs,unsigned int sector)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 
get_partition_offset(int drive)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 
write_mbr(int drive,const void * buf)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 
read_mbr(int drive,const void * buf)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 */
set_attributes(const char * file,int attributes)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  */
libfat_xpread(intptr_t pp,void * buf,size_t secsize,libfat_sector_t sector)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 
get_dos_version(void)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 
set_lock_device(uint8_t device)405 static inline void set_lock_device(uint8_t device)
406 {
407     lock_level  = 0;
408     lock_drive = device;
409 }
410 
do_lock(uint8_t level)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 
lock_device(int level)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 
unlock_device(int level)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 
adjust_mbr(int device,int writembr,int set_active)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 
move_file(int dev_fd,char * pathname,char * filename)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 
main(int argc,char * argv[])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