extlinux: don't report failure after writing a FAT superblock
[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         }
741     } else {
742         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
743             perror("writing bootblock");
744             return 1;
745         }
746     }
747
748     return 0;
749 }
750
751 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
752 {
753     char *file;
754     int fd = -1, dirfd = -1, flags;
755     struct stat st;
756     int modbytes;
757
758     asprintf(&file, "%s%sextlinux.sys",
759              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
760     if (!file) {
761         perror(program);
762         return 1;
763     }
764
765     dirfd = open(path, O_RDONLY | O_DIRECTORY);
766     if (dirfd < 0) {
767         perror(path);
768         goto bail;
769     }
770
771     fd = open(file, O_RDONLY);
772     if (fd < 0) {
773         if (errno != ENOENT) {
774             perror(file);
775             goto bail;
776         }
777     } else if (fs_type == EXT2) {
778         /* If file exist, remove the immutable flag and set u+w mode */
779         if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
780             flags &= ~EXT2_IMMUTABLE_FL;
781             ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
782         }
783         if (!fstat(fd, &st)) {
784             fchmod(fd, st.st_mode | S_IWUSR);
785         }
786     }
787     close(fd);
788
789     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
790               S_IRUSR | S_IRGRP | S_IROTH);
791     if (fd < 0) {
792         perror(file);
793         goto bail;
794     }
795
796     /* Write it the first time */
797     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
798         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
799                 boot_image_len) != 2 * ADV_SIZE) {
800         fprintf(stderr, "%s: write failure on %s\n", program, file);
801         goto bail;
802     }
803
804     /* Map the file, and patch the initial sector accordingly */
805     modbytes = patch_file_and_bootblock(fd, path, devfd);
806
807     /* Write the patch area again - this relies on the file being
808        overwritten in place! */
809     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
810         fprintf(stderr, "%s: write failure on %s\n", program, file);
811         goto bail;
812     }
813
814     /* Attempt to set immutable flag and remove all write access */
815     /* Only set immutable flag if file is owned by root */
816     if (!fstat(fd, &st)) {
817         fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
818         if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
819             flags |= EXT2_IMMUTABLE_FL;
820             ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
821         }
822     }
823
824     if (fstat(fd, rst)) {
825         perror(file);
826         goto bail;
827     }
828
829     close(dirfd);
830     close(fd);
831     return 0;
832
833 bail:
834     if (dirfd >= 0)
835         close(dirfd);
836     if (fd >= 0)
837         close(fd);
838
839     return 1;
840 }
841
842 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
843    is not managered by btrfs tree, so actually this is not installed as files.
844    since the cow feature of btrfs will move the extlinux.sys every where */
845 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
846 {
847     patch_file_and_bootblock(-1, path, devfd);
848     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
849                 != boot_image_len) {
850         perror("writing bootblock");
851         return 1;
852     }
853     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
854     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
855                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
856         perror("writing adv");
857         return 1;
858     }
859     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
860     if (stat(path, rst)) {
861         perror(path);
862         return 1;
863     }
864     return 0;
865 }
866
867 int install_file(const char *path, int devfd, struct stat *rst)
868 {
869         if (fs_type == EXT2 || fs_type == VFAT)
870                 return ext2_fat_install_file(path, devfd, rst);
871         else if (fs_type == BTRFS)
872                 return btrfs_install_file(path, devfd, rst);
873         return 1;
874 }
875
876 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
877    sector; this is consistent with FAT filesystems. */
878 int already_installed(int devfd)
879 {
880     char buffer[8];
881
882     xpread(devfd, buffer, 8, 3);
883     return !memcmp(buffer, "EXTLINUX", 8);
884 }
885
886 #ifdef __KLIBC__
887 static char devname_buf[64];
888
889 static void device_cleanup(void)
890 {
891     unlink(devname_buf);
892 }
893 #endif
894
895 /* Verify that a device fd and a pathname agree.
896    Return 0 on valid, -1 on error. */
897 static int validate_device(const char *path, int devfd)
898 {
899     struct stat pst, dst;
900     struct statfs sfs;
901
902     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
903         return -1;
904     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
905     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
906         return 0;
907     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
908 }
909
910 #ifndef __KLIBC__
911 static const char *find_device(const char *mtab_file, dev_t dev)
912 {
913     struct mntent *mnt;
914     struct stat dst;
915     FILE *mtab;
916     const char *devname = NULL;
917     bool done;
918
919     mtab = setmntent(mtab_file, "r");
920     if (!mtab)
921         return NULL;
922
923     done = false;
924     while ((mnt = getmntent(mtab))) {
925         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
926         switch (fs_type) {
927         case BTRFS:
928                 if (!strcmp(mnt->mnt_type, "btrfs") &&
929                     !stat(mnt->mnt_dir, &dst) &&
930                     dst.st_dev == dev) {
931                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
932
933                     if (opt) {
934                         if (!subvol[0]) {
935                             char *tmp;
936
937                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
938                             tmp = strchr(subvol, 32);
939                             if (tmp)
940                                 *tmp = '\0';
941                         }
942                         break; /* should break and let upper layer try again */
943                     } else
944                         done = true;
945                 }
946                 break;
947         case EXT2:
948                 if ((!strcmp(mnt->mnt_type, "ext2") ||
949                      !strcmp(mnt->mnt_type, "ext3") ||
950                      !strcmp(mnt->mnt_type, "ext4")) &&
951                     !stat(mnt->mnt_fsname, &dst) &&
952                     dst.st_rdev == dev) {
953                     done = true;
954                     break;
955                 }
956         case VFAT:
957                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
958                     !stat(mnt->mnt_fsname, &dst) &&
959                     dst.st_rdev == dev) {
960                     done = true;
961                     break;
962                 }
963         }
964         if (done) {
965                 devname = strdup(mnt->mnt_fsname);
966                 break;
967         }
968     }
969     endmntent(mtab);
970
971     return devname;
972 }
973 #endif
974
975 static const char *get_devname(const char *path)
976 {
977     const char *devname = NULL;
978     struct stat st;
979     struct statfs sfs;
980
981     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
982         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
983         return devname;
984     }
985     if (statfs(path, &sfs)) {
986         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
987         return devname;
988     }
989 #ifdef __KLIBC__
990
991     /* klibc doesn't have getmntent and friends; instead, just create
992        a new device with the appropriate device type */
993     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
994              major(st.st_dev), minor(st.st_dev));
995
996     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
997         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
998         return devname;
999     }
1000
1001     atexit(device_cleanup);     /* unlink the device node on exit */
1002     devname = devname_buf;
1003
1004 #else
1005
1006     /* check /etc/mtab first, since btrfs subvol info is only in here */
1007     devname = find_device("/etc/mtab", st.st_dev);
1008     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1009         char parent[256];
1010         char *tmp;
1011
1012         strcpy(parent, path);
1013         tmp = strrchr(parent, '/');
1014         if (tmp) {
1015             *tmp = '\0';
1016             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1017             devname = get_devname(parent);
1018         } else
1019             devname = NULL;
1020     }
1021     if (!devname) {
1022         /* Didn't find it in /etc/mtab, try /proc/mounts */
1023         devname = find_device("/proc/mounts", st.st_dev);
1024     }
1025     if (!devname) {
1026         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1027         return devname;
1028     }
1029
1030     fprintf(stderr, "%s is device %s\n", path, devname);
1031 #endif
1032     return devname;
1033 }
1034
1035 static int open_device(const char *path, struct stat *st, const char **_devname)
1036 {
1037     int devfd;
1038     const char *devname = NULL;
1039     struct statfs sfs;
1040
1041     if (st)
1042         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1043                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1044                 return -1;
1045         }
1046
1047     if (statfs(path, &sfs)) {
1048         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1049         return -1;
1050     }
1051     if (sfs.f_type == EXT2_SUPER_MAGIC)
1052         fs_type = EXT2;
1053     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1054         fs_type = BTRFS;
1055     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1056         fs_type = VFAT;
1057
1058     if (!fs_type) {
1059         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1060                 program, path);
1061         return -1;
1062     }
1063
1064     devfd = -1;
1065     devname = get_devname(path);
1066     if (_devname)
1067         *_devname = devname;
1068
1069     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1070         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1071         return -1;
1072     }
1073
1074     /* Verify that the device we opened is the device intended */
1075     if (validate_device(path, devfd)) {
1076         fprintf(stderr, "%s: path %s doesn't match device %s\n",
1077                 program, path, devname);
1078         close(devfd);
1079         return -1;
1080     }
1081     return devfd;
1082 }
1083
1084 int install_loader(const char *path, int update_only)
1085 {
1086     struct stat st, fst;
1087     int devfd, rv;
1088     const char *devname;
1089
1090     devfd = open_device(path, &st, &devname);
1091     if (devfd < 0)
1092         return 1;
1093
1094     if (update_only && !already_installed(devfd)) {
1095         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1096                 program);
1097         close(devfd);
1098         return 1;
1099     }
1100
1101     /* Read a pre-existing ADV, if already installed */
1102     if (opt.reset_adv)
1103         syslinux_reset_adv(syslinux_adv);
1104     else if (read_adv(path, devfd) < 0) {
1105         close(devfd);
1106         return 1;
1107     }
1108     if (modify_adv() < 0) {
1109         close(devfd);
1110         return 1;
1111     }
1112
1113     /* Install extlinux.sys */
1114     if (install_file(path, devfd, &fst)) {
1115         close(devfd);
1116         return 1;
1117     }
1118     if (fst.st_dev != st.st_dev) {
1119         fprintf(stderr, "%s: file system changed under us - aborting!\n",
1120                 program);
1121         close(devfd);
1122         return 1;
1123     }
1124
1125     sync();
1126     rv = install_bootblock(devfd, devname);
1127     close(devfd);
1128     sync();
1129
1130     return rv;
1131 }
1132
1133 /*
1134  * Modify the ADV of an existing installation
1135  */
1136 int modify_existing_adv(const char *path)
1137 {
1138     int devfd;
1139
1140     devfd = open_device(path, NULL, NULL);
1141     if (devfd < 0)
1142         return 1;
1143
1144     if (opt.reset_adv)
1145         syslinux_reset_adv(syslinux_adv);
1146     else if (read_adv(path, devfd) < 0) {
1147         close(devfd);
1148         return 1;
1149     }
1150     if (modify_adv() < 0) {
1151         close(devfd);
1152         return 1;
1153     }
1154     if (write_adv(path, devfd) < 0) {
1155         close(devfd);
1156         return 1;
1157     }
1158     close(devfd);
1159     return 0;
1160 }
1161
1162 int main(int argc, char *argv[])
1163 {
1164     int o;
1165     const char *directory;
1166     int update_only = -1;
1167
1168     program = argv[0];
1169
1170     while ((o = getopt_long(argc, argv, short_options,
1171                             long_options, NULL)) != EOF) {
1172         switch (o) {
1173         case 'z':
1174             opt.heads = 64;
1175             opt.sectors = 32;
1176             break;
1177         case 'S':
1178             opt.sectors = strtoul(optarg, NULL, 0);
1179             if (opt.sectors < 1 || opt.sectors > 63) {
1180                 fprintf(stderr,
1181                         "%s: invalid number of sectors: %u (must be 1-63)\n",
1182                         program, opt.sectors);
1183                 exit(EX_USAGE);
1184             }
1185             break;
1186         case 'H':
1187             opt.heads = strtoul(optarg, NULL, 0);
1188             if (opt.heads < 1 || opt.heads > 256) {
1189                 fprintf(stderr,
1190                         "%s: invalid number of heads: %u (must be 1-256)\n",
1191                         program, opt.heads);
1192                 exit(EX_USAGE);
1193             }
1194             break;
1195         case 'r':
1196             opt.raid_mode = 1;
1197             break;
1198         case 's':
1199             opt.stupid_mode = 1;
1200             break;
1201         case 'i':
1202             update_only = 0;
1203             break;
1204         case 'u':
1205         case 'U':
1206             update_only = 1;
1207             break;
1208         case 'h':
1209             usage(0);
1210             break;
1211         case 'o':
1212             opt.set_once = optarg;
1213             break;
1214         case 'O':
1215             opt.set_once = "";
1216             break;
1217         case OPT_RESET_ADV:
1218             opt.reset_adv = 1;
1219             break;
1220         case 'v':
1221             fputs("extlinux " VERSION_STR
1222                   "  Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1223             exit(0);
1224         default:
1225             usage(EX_USAGE);
1226         }
1227     }
1228
1229     directory = argv[optind];
1230
1231     if (!directory)
1232         usage(EX_USAGE);
1233
1234     if (update_only == -1) {
1235         if (opt.reset_adv || opt.set_once) {
1236             return modify_existing_adv(directory);
1237         } else {
1238             usage(EX_USAGE);
1239         }
1240     }
1241
1242     return install_loader(directory, update_only);
1243 }