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