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