FAT: change DOS installer to EXE; additional 32K limit fixes
[profile/ivi/syslinux.git] / extlinux / main.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009 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  * extlinux.c
16  *
17  * Install the extlinux boot block on an ext2/3 filesystem
18  */
19
20 #define  _GNU_SOURCE            /* Enable everything */
21 #include <inttypes.h>
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
23 typedef uint64_t u64;
24 #include <alloca.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #ifndef __KLIBC__
30 #include <mntent.h>
31 #endif
32 #include <stdlib.h>
33 #include <string.h>
34 #include <getopt.h>
35 #include <sysexits.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/mount.h>
40 #include <sys/vfs.h>
41
42 #include <linux/fd.h>           /* Floppy geometry */
43 #include <linux/hdreg.h>        /* Hard disk geometry */
44 #define statfs _kernel_statfs   /* HACK to deal with broken 2.4 distros */
45 #include <linux/fs.h>           /* FIGETBSZ, FIBMAP */
46 #undef statfs
47
48 #include "ext2_fs.h"
49 #include "../version.h"
50 #include "syslxint.h"
51
52 #ifdef DEBUG
53 # define dprintf printf
54 #else
55 # define dprintf(...) ((void)0)
56 #endif
57
58 /* Global option handling */
59
60 const char *program;
61
62 /* These are the options we can set and their values */
63 struct my_options {
64   unsigned int sectors;
65   unsigned int heads;
66   int raid_mode;
67   int stupid_mode;
68   int reset_adv;
69   const char *set_once;
70 } opt = {
71   .sectors = 0,
72   .heads = 0,
73   .raid_mode = 0,
74   .stupid_mode = 0,
75   .reset_adv = 0,
76   .set_once = NULL,
77 };
78
79 static void __attribute__((noreturn)) usage(int rv)
80 {
81   fprintf(stderr,
82           "Usage: %s [options] directory\n"
83           "  --install    -i  Install over the current bootsector\n"
84           "  --update     -U  Update a previous EXTLINUX installation\n"
85           "  --zip        -z  Force zipdrive geometry (-H 64 -S 32)\n"
86           "  --sectors=#  -S  Force the number of sectors per track\n"
87           "  --heads=#    -H  Force number of heads\n"
88           "  --stupid     -s  Slow, safe and stupid mode\n"
89           "  --raid       -r  Fall back to the next device on boot failure\n"
90           "  --once=...   -o  Execute a command once upon boot\n"
91           "  --clear-once -O  Clear the boot-once command\n"
92           "  --reset-adv      Reset auxilliary data\n"
93           "\n"
94           "  Note: geometry is determined at boot time for devices which\n"
95           "  are considered hard disks by the BIOS.  Unfortunately, this is\n"
96           "  not possible for devices which are considered floppy disks,\n"
97           "  which includes zipdisks and LS-120 superfloppies.\n"
98           "\n"
99           "  The -z option is useful for USB devices which are considered\n"
100           "  hard disks by some BIOSes and zipdrives by other BIOSes.\n",
101           program);
102
103   exit(rv);
104 }
105
106 enum long_only_opt {
107   OPT_NONE,
108   OPT_RESET_ADV,
109 };
110
111 static const struct option long_options[] = {
112   { "install",    0, NULL, 'i' },
113   { "update",     0, NULL, 'U' },
114   { "zipdrive",   0, NULL, 'z' },
115   { "sectors",    1, NULL, 'S' },
116   { "stupid",     0, NULL, 's' },
117   { "heads",      1, NULL, 'H' },
118   { "raid-mode",  0, NULL, 'r' },
119   { "version",    0, NULL, 'v' },
120   { "help",       0, NULL, 'h' },
121   { "once",       1, NULL, 'o' },
122   { "clear-once", 0, NULL, 'O' },
123   { "reset-adv",  0, NULL, OPT_RESET_ADV },
124   { 0, 0, 0, 0 }
125 };
126
127 static const char short_options[] = "iUuzS:H:rvho:O";
128
129 #if defined(__linux__) && !defined(BLKGETSIZE64)
130 /* This takes a u64, but the size field says size_t.  Someone screwed big. */
131 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
132 #endif
133
134 #ifndef EXT2_SUPER_OFFSET
135 #define EXT2_SUPER_OFFSET 1024
136 #endif
137
138 const char *program;
139
140 /*
141  * Boot block
142  */
143 extern unsigned char extlinux_bootsect[];
144 extern unsigned int  extlinux_bootsect_len;
145 #define boot_block      extlinux_bootsect
146 #define boot_block_len  extlinux_bootsect_len
147
148 /*
149  * Image file
150  */
151 extern unsigned char extlinux_image[];
152 extern unsigned int  extlinux_image_len;
153 #define boot_image      extlinux_image
154 #define boot_image_len  extlinux_image_len
155
156 /*
157  * Common abort function
158  */
159 void __attribute__((noreturn)) die(const char *msg)
160 {
161   fputs(msg, stderr);
162   exit(1);
163 }
164
165 /*
166  * read/write wrapper functions
167  */
168 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
169 {
170   char *bufp = (char *)buf;
171   ssize_t rv;
172   ssize_t done = 0;
173
174   while ( count ) {
175     rv = pread(fd, bufp, count, offset);
176     if ( rv == 0 ) {
177       die("short read");
178     } else if ( rv == -1 ) {
179       if ( errno == EINTR ) {
180         continue;
181       } else {
182         die(strerror(errno));
183       }
184     } else {
185       bufp += rv;
186       offset += rv;
187       done += rv;
188       count -= rv;
189     }
190   }
191
192   return done;
193 }
194
195 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
196 {
197   const char *bufp = (const char *)buf;
198   ssize_t rv;
199   ssize_t done = 0;
200
201   while ( count ) {
202     rv = pwrite(fd, bufp, count, offset);
203     if ( rv == 0 ) {
204       die("short write");
205     } else if ( rv == -1 ) {
206       if ( errno == EINTR ) {
207         continue;
208       } else {
209         die(strerror(errno));
210       }
211     } else {
212       bufp += rv;
213       offset += rv;
214       done += rv;
215       count -= rv;
216     }
217   }
218
219   return done;
220 }
221
222 /*
223  * Produce file map
224  */
225 int
226 sectmap(int fd, uint32_t *sectors, int nsectors)
227 {
228   unsigned int blksize, blk, nblk;
229   unsigned int i;
230
231   /* Get block size */
232   if ( ioctl(fd, FIGETBSZ, &blksize) )
233     return -1;
234
235   /* Number of sectors per block */
236   blksize >>= SECTOR_BITS;
237
238   nblk = 0;
239   while ( nsectors ) {
240
241     blk = nblk++;
242     dprintf("querying block %u\n", blk);
243     if ( ioctl(fd, FIBMAP, &blk) )
244       return -1;
245
246     blk *= blksize;
247     for ( i = 0 ; i < blksize ; i++ ) {
248       if ( !nsectors )
249         return 0;
250
251       dprintf("Sector: %10u\n", blk);
252       *sectors++ = blk++;
253       nsectors--;
254     }
255   }
256
257   return 0;
258 }
259
260 /*
261  * Get the size of a block device
262  */
263 uint64_t get_size(int devfd)
264 {
265   uint64_t bytes;
266   uint32_t sects;
267   struct stat st;
268
269 #ifdef BLKGETSIZE64
270   if ( !ioctl(devfd, BLKGETSIZE64, &bytes) )
271     return bytes;
272 #endif
273   if ( !ioctl(devfd, BLKGETSIZE, &sects) )
274     return (uint64_t)sects << 9;
275   else if ( !fstat(devfd, &st) && st.st_size )
276     return st.st_size;
277   else
278     return 0;
279 }
280
281
282 /*
283  * Get device geometry and partition offset
284  */
285 struct geometry_table {
286   uint64_t bytes;
287   struct hd_geometry g;
288 };
289
290 /* Standard floppy disk geometries, plus LS-120.  Zipdisk geometry
291    (x/64/32) is the final fallback.  I don't know what LS-240 has
292    as its geometry, since I don't have one and don't know anyone that does,
293    and Google wasn't helpful... */
294 static const struct geometry_table standard_geometries[] = {
295   {    360*1024, {  2,  9,  40, 0 } },
296   {    720*1024, {  2,  9,  80, 0 } },
297   {   1200*1024, {  2, 15,  80, 0 } },
298   {   1440*1024, {  2, 18,  80, 0 } },
299   {   1680*1024, {  2, 21,  80, 0 } },
300   {   1722*1024, {  2, 21,  80, 0 } },
301   {   2880*1024, {  2, 36,  80, 0 } },
302   {   3840*1024, {  2, 48,  80, 0 } },
303   { 123264*1024, {  8, 32, 963, 0 } }, /* LS120 */
304   { 0, {0,0,0,0} }
305 };
306
307 int
308 get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
309 {
310   struct floppy_struct fd_str;
311   const struct geometry_table *gp;
312
313   memset(geo, 0, sizeof *geo);
314
315   if ( !ioctl(devfd, HDIO_GETGEO, &geo) ) {
316     return 0;
317   } else if ( !ioctl(devfd, FDGETPRM, &fd_str) ) {
318     geo->heads     = fd_str.head;
319     geo->sectors   = fd_str.sect;
320     geo->cylinders = fd_str.track;
321     geo->start     = 0;
322     return 0;
323   }
324
325   /* Didn't work.  Let's see if this is one of the standard geometries */
326   for ( gp = standard_geometries ; gp->bytes ; gp++ ) {
327     if ( gp->bytes == totalbytes ) {
328       memcpy(geo, &gp->g, sizeof *geo);
329       return 0;
330     }
331   }
332
333   /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
334      what zipdisks use, so this would help if someone has a USB key that
335      they're booting in USB-ZIP mode. */
336
337   geo->heads     = opt.heads ?: 64;
338   geo->sectors   = opt.sectors ?: 32;
339   geo->cylinders = totalbytes/(geo->heads*geo->sectors << SECTOR_BITS);
340   geo->start     = 0;
341
342   if ( !opt.sectors && !opt.heads )
343     fprintf(stderr, "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
344             "         (on hard disks, this is usually harmless.)\n",
345             geo->heads, geo->sectors);
346
347   return 1;
348 }
349
350 /*
351  * Query the device geometry and put it into the boot sector.
352  * Map the file and put the map in the boot sector and file.
353  * Stick the "current directory" inode number into the file.
354  */
355 void
356 patch_file_and_bootblock(int fd, int dirfd, int devfd)
357 {
358   struct stat dirst;
359   struct hd_geometry geo;
360   uint32_t *sectp;
361   uint64_t totalbytes, totalsectors;
362   int nsect;
363   uint32_t *wp;
364   struct boot_sector *bs;
365   struct patch_area *patcharea;
366   int i, dw, nptrs;
367   uint32_t csum;
368
369   if ( fstat(dirfd, &dirst) ) {
370     perror("fstat dirfd");
371     exit(255);                  /* This should never happen */
372   }
373
374   totalbytes = get_size(devfd);
375   get_geometry(devfd, totalbytes, &geo);
376
377   if ( opt.heads )
378     geo.heads = opt.heads;
379   if ( opt.sectors )
380     geo.sectors = opt.sectors;
381
382   /* Patch this into a fake FAT superblock.  This isn't because
383      FAT is a good format in any way, it's because it lets the
384      early bootstrap share code with the FAT version. */
385   dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
386
387   bs = (struct boot_sector *)boot_block;
388
389   totalsectors = totalbytes >> SECTOR_BITS;
390   if ( totalsectors >= 65536 ) {
391     set_16(&bs->bsSectors, 0);
392   } else {
393     set_16(&bs->bsSectors, totalsectors);
394   }
395   set_32(&bs->bsHugeSectors, totalsectors);
396
397   set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
398   set_16(&bs->bsSecPerTrack, geo.sectors);
399   set_16(&bs->bsHeads, geo.heads);
400   set_32(&bs->bsHiddenSecs, geo.start);
401
402   /* If we're in RAID mode then patch the appropriate instruction;
403      either way write the proper boot signature */
404   i = get_16(&bs->bsSignature);
405   if (opt.raid_mode)
406     set_16((uint16_t *)(boot_block+i), 0x18CD); /* INT 18h */
407
408   set_16(&bs->bsSignature, 0xAA55);
409
410   /* Construct the boot file */
411
412   dprintf("directory inode = %lu\n", (unsigned long) dirst.st_ino);
413   nsect = (boot_image_len+SECTOR_SIZE-1) >> SECTOR_BITS;
414   nsect += 2;                   /* Two sectors for the ADV */
415   sectp = alloca(sizeof(uint32_t)*nsect);
416   if ( sectmap(fd, sectp, nsect) ) {
417     perror("bmap");
418     exit(1);
419   }
420
421   /* First sector need pointer in boot sector */
422   set_32(&bs->NextSector, *sectp++);
423
424   /* Stupid mode? */
425   if (opt.stupid_mode)
426     set_16(&bs->MaxTransfer, 1);
427
428   /* Search for LDLINUX_MAGIC to find the patch area */
429   for (wp = (uint32_t *)boot_image; get_32(wp) != LDLINUX_MAGIC; wp++);
430   patcharea = (struct patch_area *)wp;
431
432   /* Set up the totals */
433   dw = boot_image_len >> 2;     /* COMPLETE dwords, excluding ADV */
434   set_16(&patcharea->data_sectors, nsect-2); /* -2 for the ADVs */
435   set_16(&patcharea->adv_sectors, 2);
436   set_32(&patcharea->dwords, dw);
437   set_32(&patcharea->currentdir, dirst.st_ino);
438
439   /* Set the sector pointers */
440   wp = (uint32_t *)((char *)boot_image+get_16(&patcharea->secptroffset));
441   nptrs = get_16(&patcharea->secptrcnt);
442
443   memset(wp, 0, nptrs*4);
444   while ( nsect-- )
445     set_32(wp++, *sectp++);
446
447   /* Now produce a checksum */
448   set_32(&patcharea->checksum, 0);
449
450   csum = LDLINUX_MAGIC;
451   for (i = 0, wp = (uint32_t *)boot_image; i < dw; i++, wp++)
452     csum -= get_32(wp);         /* Negative checksum */
453
454   set_32(&patcharea->checksum, csum);
455 }
456
457 /*
458  * Read the ADV from an existing instance, or initialize if invalid.
459  * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
460  * ADV was found.
461  */
462 int
463 read_adv(const char *path)
464 {
465   char *file;
466   int fd = -1;
467   struct stat st;
468   int err = 0;
469
470   asprintf(&file, "%s%sextlinux.sys",
471            path,
472            path[0] && path[strlen(path)-1] == '/' ? "" : "/");
473
474   if ( !file ) {
475     perror(program);
476     return -1;
477   }
478
479   fd = open(file, O_RDONLY);
480   if ( fd < 0 ) {
481     if ( errno != ENOENT ) {
482       err = -1;
483     } else {
484       syslinux_reset_adv(syslinux_adv);
485     }
486   } else if (fstat(fd, &st)) {
487     err = -1;
488   } else if (st.st_size < 2*ADV_SIZE) {
489     /* Too small to be useful */
490     syslinux_reset_adv(syslinux_adv);
491     err = 0;                    /* Nothing to read... */
492   } else if (xpread(fd, syslinux_adv, 2*ADV_SIZE,
493                     st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) {
494     err = -1;
495   } else {
496     /* We got it... maybe? */
497     err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
498   }
499
500   if (err < 0)
501     perror(file);
502
503   if (fd >= 0)
504     close(fd);
505   if (file)
506     free(file);
507
508   return err;
509 }
510
511 /*
512  * Update the ADV in an existing installation.
513  */
514 int
515 write_adv(const char *path)
516 {
517   unsigned char advtmp[2*ADV_SIZE];
518   char *file;
519   int fd = -1;
520   struct stat st, xst;
521   int err = 0;
522   int flags, nflags;
523
524   asprintf(&file, "%s%sextlinux.sys",
525            path,
526            path[0] && path[strlen(path)-1] == '/' ? "" : "/");
527
528   if ( !file ) {
529     perror(program);
530     return -1;
531   }
532
533   fd = open(file, O_RDONLY);
534   if ( fd < 0 ) {
535     err = -1;
536   } else if (fstat(fd, &st)) {
537     err = -1;
538   } else if (st.st_size < 2*ADV_SIZE) {
539     /* Too small to be useful */
540     err = -2;
541   } else if (xpread(fd, advtmp, 2*ADV_SIZE,
542                     st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) {
543     err = -1;
544   } else {
545     /* We got it... maybe? */
546     err = syslinux_validate_adv(advtmp) ? -2 : 0;
547     if (!err) {
548       /* Got a good one, write our own ADV here */
549       if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
550         nflags = flags & ~EXT2_IMMUTABLE_FL;
551         if (nflags != flags)
552           ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
553       }
554       if (!(st.st_mode & S_IWUSR))
555         fchmod(fd, st.st_mode | S_IWUSR);
556
557       /* Need to re-open read-write */
558       close(fd);
559       fd = open(file, O_RDWR|O_SYNC);
560       if (fd < 0) {
561         err = -1;
562       } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
563                  xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
564         fprintf(stderr, "%s: race condition on write\n", file);
565         err = -2;
566       }
567       /* Write our own version ... */
568       if (xpwrite(fd, syslinux_adv, 2*ADV_SIZE,
569                   st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) {
570         err = -1;
571       }
572
573       sync();
574
575       if (!(st.st_mode & S_IWUSR))
576         fchmod(fd, st.st_mode);
577
578       if (nflags != flags)
579         ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
580     }
581   }
582
583   if (err == -2)
584     fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
585             file);
586   else if (err == -1)
587     perror(file);
588
589   if (fd >= 0)
590     close(fd);
591   if (file)
592     free(file);
593
594   return err;
595 }
596
597 /*
598  * Make any user-specified ADV modifications
599  */
600 int modify_adv(void)
601 {
602   int rv = 0;
603
604   if (opt.set_once) {
605     if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
606       fprintf(stderr, "%s: not enough space for boot-once command\n", program);
607       rv = -1;
608     }
609   }
610
611   return rv;
612 }
613
614 /*
615  * Install the boot block on the specified device.
616  * Must be run AFTER install_file()!
617  */
618 int
619 install_bootblock(int fd, const char *device)
620 {
621   struct ext2_super_block sb;
622
623   if ( xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb ) {
624     perror("reading superblock");
625     return 1;
626   }
627
628   if ( sb.s_magic != EXT2_SUPER_MAGIC ) {
629     fprintf(stderr, "no ext2/ext3 superblock found on %s\n", device);
630     return 1;
631   }
632
633   if ( xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len ) {
634     perror("writing bootblock");
635     return 1;
636   }
637
638   return 0;
639 }
640
641 int
642 install_file(const char *path, int devfd, struct stat *rst)
643 {
644   char *file;
645   int fd = -1, dirfd = -1, flags;
646   struct stat st;
647
648   asprintf(&file, "%s%sextlinux.sys",
649            path,
650            path[0] && path[strlen(path)-1] == '/' ? "" : "/");
651   if ( !file ) {
652     perror(program);
653     return 1;
654   }
655
656   dirfd = open(path, O_RDONLY|O_DIRECTORY);
657   if ( dirfd < 0 ) {
658     perror(path);
659     goto bail;
660   }
661
662   fd = open(file, O_RDONLY);
663   if ( fd < 0 ) {
664     if ( errno != ENOENT ) {
665       perror(file);
666       goto bail;
667     }
668   } else {
669     /* If file exist, remove the immutable flag and set u+w mode */
670     if ( !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) {
671       flags &= ~EXT2_IMMUTABLE_FL;
672       ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
673     }
674     if ( !fstat(fd, &st) ) {
675       fchmod(fd, st.st_mode | S_IWUSR);
676     }
677   }
678   close(fd);
679
680   fd = open(file, O_WRONLY|O_TRUNC|O_CREAT|O_SYNC, S_IRUSR|S_IRGRP|S_IROTH);
681   if ( fd < 0 ) {
682     perror(file);
683     goto bail;
684   }
685
686   /* Write it the first time */
687   if ( xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
688        xpwrite(fd, syslinux_adv, 2*ADV_SIZE, boot_image_len) != 2*ADV_SIZE ) {
689     fprintf(stderr, "%s: write failure on %s\n", program, file);
690     goto bail;
691   }
692
693   /* Map the file, and patch the initial sector accordingly */
694   patch_file_and_bootblock(fd, dirfd, devfd);
695
696   /* Write the first sector again - this relies on the file being
697      overwritten in place! */
698   if ( xpwrite(fd, boot_image, SECTOR_SIZE, 0) != SECTOR_SIZE ) {
699     fprintf(stderr, "%s: write failure on %s\n", program, file);
700     goto bail;
701   }
702
703   /* Attempt to set immutable flag and remove all write access */
704   /* Only set immutable flag if file is owned by root */
705   if ( !fstat(fd, &st) ) {
706     fchmod(fd, st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH));
707     if ( st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) {
708       flags |= EXT2_IMMUTABLE_FL;
709       ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
710     }
711   }
712
713   if ( fstat(fd, rst) ) {
714     perror(file);
715     goto bail;
716   }
717
718   close(dirfd);
719   close(fd);
720   return 0;
721
722  bail:
723   if ( dirfd >= 0 )
724     close(dirfd);
725   if ( fd >= 0 )
726     close(fd);
727
728   return 1;
729 }
730
731 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
732    sector; this is consistent with FAT filesystems. */
733 int
734 already_installed(int devfd)
735 {
736   char buffer[8];
737
738   xpread(devfd, buffer, 8, 3);
739   return !memcmp(buffer, "EXTLINUX", 8);
740 }
741
742
743 #ifdef __KLIBC__
744 static char devname_buf[64];
745
746 static void device_cleanup(void)
747 {
748   unlink(devname_buf);
749 }
750 #endif
751
752 /* Verify that a device fd and a pathname agree.
753    Return 0 on valid, -1 on error. */
754 static int validate_device(const char *path, int devfd)
755 {
756   struct stat pst, dst;
757
758   if (stat(path, &pst) || fstat(devfd, &dst))
759     return -1;
760
761   return (pst.st_dev == dst.st_rdev) ? 0 : -1;
762 }
763
764 #ifndef __KLIBC__
765 static const char *find_device(const char *mtab_file, dev_t dev)
766 {
767   struct mntent *mnt;
768   struct stat dst;
769   FILE *mtab;
770   const char *devname = NULL;
771
772   mtab = setmntent(mtab_file, "r");
773   if (!mtab)
774     return NULL;
775
776   while ( (mnt = getmntent(mtab)) ) {
777     if ( (!strcmp(mnt->mnt_type, "ext2") ||
778           !strcmp(mnt->mnt_type, "ext3")) &&
779          !stat(mnt->mnt_fsname, &dst) && dst.st_rdev == dev ) {
780       devname = strdup(mnt->mnt_fsname);
781       break;
782     }
783   }
784   endmntent(mtab);
785
786   return devname;
787 }
788 #endif
789
790 int
791 install_loader(const char *path, int update_only)
792 {
793   struct stat st, fst;
794   int devfd, rv;
795   const char *devname = NULL;
796   struct statfs sfs;
797
798   if ( stat(path, &st) || !S_ISDIR(st.st_mode) ) {
799     fprintf(stderr, "%s: Not a directory: %s\n", program, path);
800     return 1;
801   }
802
803   if ( statfs(path, &sfs) ) {
804     fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
805     return 1;
806   }
807
808   if ( sfs.f_type != EXT2_SUPER_MAGIC ) {
809     fprintf(stderr, "%s: not an ext2/ext3 filesystem: %s\n", program, path);
810     return 1;
811   }
812
813   devfd = -1;
814
815 #ifdef __KLIBC__
816
817   /* klibc doesn't have getmntent and friends; instead, just create
818      a new device with the appropriate device type */
819   snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
820            major(st.st_dev), minor(st.st_dev));
821
822   if (mknod(devname_buf, S_IFBLK|0600, st.st_dev)) {
823     fprintf(stderr, "%s: cannot create device %s\n", program, devname);
824     return 1;
825   }
826
827   atexit(device_cleanup);       /* unlink the device node on exit */
828   devname = devname_buf;
829
830 #else
831
832   devname = find_device("/proc/mounts", st.st_dev);
833   if (!devname) {
834     /* Didn't find it in /proc/mounts, try /etc/mtab */
835     devname = find_device("/etc/mtab", st.st_dev);
836   }
837   if (!devname) {
838     fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
839     return 1;
840   }
841
842   fprintf(stderr, "%s is device %s\n", path, devname);
843 #endif
844
845   if ( (devfd = open(devname, O_RDWR|O_SYNC)) < 0 ) {
846     fprintf(stderr, "%s: cannot open device %s\n", program, devname);
847     return 1;
848   }
849
850   /* Verify that the device we opened is the device intended */
851   if (validate_device(path, devfd)) {
852     fprintf(stderr, "%s: path %s doesn't match device %s\n",
853             program, path, devname);
854     return 1;
855   }
856
857   if ( update_only && !already_installed(devfd) ) {
858     fprintf(stderr, "%s: no previous extlinux boot sector found\n", program);
859     return 1;
860   }
861
862   /* Read a pre-existing ADV, if already installed */
863   if (opt.reset_adv)
864     syslinux_reset_adv(syslinux_adv);
865   else if (read_adv(path) < 0)
866     return 1;
867
868   if (modify_adv() < 0)
869     return 1;
870
871   /* Install extlinux.sys */
872   if (install_file(path, devfd, &fst))
873     return 1;
874
875   if ( fst.st_dev != st.st_dev ) {
876     fprintf(stderr, "%s: file system changed under us - aborting!\n",
877             program);
878     return 1;
879   }
880
881   sync();
882   rv = install_bootblock(devfd, devname);
883   close(devfd);
884   sync();
885
886   return rv;
887 }
888
889 /*
890  * Modify the ADV of an existing installation
891  */
892 int
893 modify_existing_adv(const char *path)
894 {
895   if (opt.reset_adv)
896     syslinux_reset_adv(syslinux_adv);
897   else if (read_adv(path) < 0)
898     return 1;
899
900   if (modify_adv() < 0)
901     return 1;
902
903   if (write_adv(path) < 0)
904     return 1;
905
906   return 0;
907 }
908
909 int
910 main(int argc, char *argv[])
911 {
912   int o;
913   const char *directory;
914   int update_only = -1;
915
916   program = argv[0];
917
918   while ( (o = getopt_long(argc, argv, short_options,
919                              long_options, NULL)) != EOF ) {
920     switch ( o ) {
921     case 'z':
922       opt.heads = 64;
923       opt.sectors = 32;
924       break;
925     case 'S':
926       opt.sectors = strtoul(optarg, NULL, 0);
927       if ( opt.sectors < 1 || opt.sectors > 63 ) {
928         fprintf(stderr, "%s: invalid number of sectors: %u (must be 1-63)\n",
929                 program, opt.sectors);
930         exit(EX_USAGE);
931       }
932       break;
933     case 'H':
934       opt.heads = strtoul(optarg, NULL, 0);
935       if ( opt.heads < 1 || opt.heads > 256 ) {
936         fprintf(stderr, "%s: invalid number of heads: %u (must be 1-256)\n",
937                 program, opt.heads);
938         exit(EX_USAGE);
939       }
940       break;
941     case 'r':
942       opt.raid_mode = 1;
943       break;
944     case 's':
945       opt.stupid_mode = 1;
946       break;
947     case 'i':
948       update_only = 0;
949       break;
950     case 'u':
951     case 'U':
952       update_only = 1;
953       break;
954     case 'h':
955       usage(0);
956       break;
957     case 'o':
958       opt.set_once = optarg;
959       break;
960     case 'O':
961       opt.set_once = "";
962       break;
963     case OPT_RESET_ADV:
964       opt.reset_adv = 1;
965       break;
966     case 'v':
967       fputs("extlinux " VERSION_STR
968             "  Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
969       exit(0);
970     default:
971       usage(EX_USAGE);
972     }
973   }
974
975   directory = argv[optind];
976
977   if ( !directory )
978     usage(EX_USAGE);
979
980   if ( update_only == -1 ) {
981     if (opt.reset_adv || opt.set_once) {
982       return modify_existing_adv(directory);
983     } else {
984       usage(EX_USAGE);
985     }
986   }
987
988   return install_loader(directory, update_only);
989 }