Merge branch '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, *xdirpath, *xsubpath;
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     for (;;) {
461         if (*subpath == '/') {
462             if (subpath > dirpath) {
463                 *subpath = '\0';
464                 xsubpath = subpath+1;
465                 xdirpath = dirpath;
466             } else {
467                 xsubpath = subpath;
468                 xdirpath = "/";
469             }
470             if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
471                 subpath = strchr(subpath+1, '/');
472                 if (!subpath)
473                     subpath = "/"; /* It's the root of the filesystem */
474                 break;
475             }
476             *subpath = '/';
477         }
478
479         if (subpath == dirpath)
480             break;
481
482         subpath--;
483     }
484
485     /* Now subpath should contain the path relative to the fs base */
486     dprintf("subpath = %s\n", subpath);
487
488     totalbytes = get_size(devfd);
489     get_geometry(devfd, totalbytes, &geo);
490
491     if (opt.heads)
492         geo.heads = opt.heads;
493     if (opt.sectors)
494         geo.sectors = opt.sectors;
495
496     /* Patch this into a fake FAT superblock.  This isn't because
497        FAT is a good format in any way, it's because it lets the
498        early bootstrap share code with the FAT version. */
499     dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
500
501     bs = (struct boot_sector *)boot_block;
502
503     totalsectors = totalbytes >> SECTOR_SHIFT;
504     if (totalsectors >= 65536) {
505         set_16(&bs->bsSectors, 0);
506     } else {
507         set_16(&bs->bsSectors, totalsectors);
508     }
509     set_32(&bs->bsHugeSectors, totalsectors);
510
511     set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
512     set_16(&bs->bsSecPerTrack, geo.sectors);
513     set_16(&bs->bsHeads, geo.heads);
514     set_32(&bs->bsHiddenSecs, geo.start);
515
516     /* If we're in RAID mode then patch the appropriate instruction;
517        either way write the proper boot signature */
518     i = get_16(&bs->bsSignature);
519     if (opt.raid_mode)
520         set_16((uint16_t *) (boot_block + i), 0x18CD);  /* INT 18h */
521
522     set_16(&bs->bsSignature, 0xAA55);
523
524     /* Construct the boot file */
525
526     dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
527     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
528     nsect += 2;                 /* Two sectors for the ADV */
529     sectp = alloca(sizeof(uint32_t) * nsect);
530     if (fs_type == EXT2 || fs_type == VFAT) {
531         if (sectmap(fd, sectp, nsect)) {
532                 perror("bmap");
533                 exit(1);
534         }
535     } else if (fs_type == BTRFS) {
536         int i;
537
538         for (i = 0; i < nsect; i++)
539                 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
540     }
541
542     /* First sector need pointer in boot sector */
543     set_32(&bs->NextSector, *sectp++);
544     /* Stupid mode? */
545     if (opt.stupid_mode)
546         set_16(&bs->MaxTransfer, 1);
547
548     /* Search for LDLINUX_MAGIC to find the patch area */
549     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
550     patcharea = (struct patch_area *)wp;
551
552     /* Set up the totals */
553     dw = boot_image_len >> 2;   /* COMPLETE dwords, excluding ADV */
554     set_16(&patcharea->data_sectors, nsect - 2);        /* -2 for the ADVs */
555     set_16(&patcharea->adv_sectors, 2);
556     set_32(&patcharea->dwords, dw);
557
558     /* Set the sector pointers */
559     secptroffset = get_16(&patcharea->secptroffset);
560     wp = (uint32_t *) ((char *)boot_image + secptroffset);
561     nptrs = get_16(&patcharea->secptrcnt);
562
563     memset(wp, 0, nptrs * 4);
564     while (nsect--)
565         set_32(wp++, *sectp++);
566
567     /* Poke in the base directory path */
568     diroffset = get_16(&patcharea->diroffset);
569     dirlen = get_16(&patcharea->dirlen);
570     if (dirlen <= strlen(subpath)) {
571         fprintf(stderr, "Subdirectory path too long... aborting install!\n");
572         exit(1);
573     }
574     strncpy((char *)boot_image + diroffset, subpath, dirlen);
575     free(dirpath);
576
577     /* write subvol info if we have */
578     subvoloffset = get_16(&patcharea->subvoloffset);
579     subvollen = get_16(&patcharea->subvollen);
580     if (subvollen <= strlen(subvol)) {
581         fprintf(stderr, "Subvol name too long... aborting install!\n");
582         exit(1);
583     }
584     strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
585
586     /* Now produce a checksum */
587     set_32(&patcharea->checksum, 0);
588
589     csum = LDLINUX_MAGIC;
590     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
591         csum -= get_32(wp);     /* Negative checksum */
592
593     set_32(&patcharea->checksum, csum);
594
595     /*
596      * Assume all bytes modified.  This can be optimized at the expense
597      * of keeping track of what the highest modified address ever was.
598      */
599     return dw << 2;
600 }
601
602 /*
603  * Read the ADV from an existing instance, or initialize if invalid.
604  * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
605  * ADV was found.
606  */
607 int read_adv(const char *path, int devfd)
608 {
609     char *file;
610     int fd = -1;
611     struct stat st;
612     int err = 0;
613
614     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
615         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
616                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
617                 perror("writing adv");
618                 return 1;
619         }
620         return 0;
621     }
622     asprintf(&file, "%s%sextlinux.sys",
623              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
624
625     if (!file) {
626         perror(program);
627         return -1;
628     }
629
630     fd = open(file, O_RDONLY);
631     if (fd < 0) {
632         if (errno != ENOENT) {
633             err = -1;
634         } else {
635             syslinux_reset_adv(syslinux_adv);
636         }
637     } else if (fstat(fd, &st)) {
638         err = -1;
639     } else if (st.st_size < 2 * ADV_SIZE) {
640         /* Too small to be useful */
641         syslinux_reset_adv(syslinux_adv);
642         err = 0;                /* Nothing to read... */
643     } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
644                       st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
645         err = -1;
646     } else {
647         /* We got it... maybe? */
648         err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
649     }
650
651     if (err < 0)
652         perror(file);
653
654     if (fd >= 0)
655         close(fd);
656     if (file)
657         free(file);
658
659     return err;
660 }
661
662 /*
663  * Update the ADV in an existing installation.
664  */
665 int write_adv(const char *path, int devfd)
666 {
667     unsigned char advtmp[2 * ADV_SIZE];
668     char *file;
669     int fd = -1;
670     struct stat st, xst;
671     int err = 0;
672
673     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
674         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
675                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
676                 perror("writing adv");
677                 return 1;
678         }
679         return 0;
680     }
681     asprintf(&file, "%s%sextlinux.sys",
682              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
683
684     if (!file) {
685         perror(program);
686         return -1;
687     }
688
689     fd = open(file, O_RDONLY);
690     if (fd < 0) {
691         err = -1;
692     } else if (fstat(fd, &st)) {
693         err = -1;
694     } else if (st.st_size < 2 * ADV_SIZE) {
695         /* Too small to be useful */
696         err = -2;
697     } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
698                       st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
699         err = -1;
700     } else {
701         /* We got it... maybe? */
702         err = syslinux_validate_adv(advtmp) ? -2 : 0;
703         if (!err) {
704             /* Got a good one, write our own ADV here */
705             clear_attributes(fd);
706
707             /* Need to re-open read-write */
708             close(fd);
709             fd = open(file, O_RDWR | O_SYNC);
710             if (fd < 0) {
711                 err = -1;
712             } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
713                        xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
714                 fprintf(stderr, "%s: race condition on write\n", file);
715                 err = -2;
716             }
717             /* Write our own version ... */
718             if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
719                         st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
720                 err = -1;
721             }
722
723             sync();
724             set_attributes(fd);
725         }
726     }
727
728     if (err == -2)
729         fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
730                 file);
731     else if (err == -1)
732         perror(file);
733
734     if (fd >= 0)
735         close(fd);
736     if (file)
737         free(file);
738
739     return err;
740 }
741
742 /*
743  * Make any user-specified ADV modifications
744  */
745 int modify_adv(void)
746 {
747     int rv = 0;
748
749     if (opt.set_once) {
750         if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
751             fprintf(stderr, "%s: not enough space for boot-once command\n",
752                     program);
753             rv = -1;
754         }
755     }
756
757     return rv;
758 }
759
760 /*
761  * Install the boot block on the specified device.
762  * Must be run AFTER install_file()!
763  */
764 int install_bootblock(int fd, const char *device)
765 {
766     struct ext2_super_block sb;
767     struct btrfs_super_block sb2;
768     struct boot_sector sb3;
769     bool ok = false;
770
771     if (fs_type == EXT2) {
772         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
773                 perror("reading superblock");
774                 return 1;
775         }
776         if (sb.s_magic == EXT2_SUPER_MAGIC)
777                 ok = true;
778     } else if (fs_type == BTRFS) {
779         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
780                         != sizeof sb2) {
781                 perror("reading superblock");
782                 return 1;
783         }
784         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
785                 ok = true;
786     } else if (fs_type == VFAT) {
787         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
788                 perror("reading fat superblock");
789                 return 1;
790         }
791         if (sb3.bsResSectors && sb3.bsFATs &&
792             (strstr(sb3.bs16.FileSysType, "FAT") ||
793              strstr(sb3.bs32.FileSysType, "FAT")))
794                 ok = true;
795     }
796     if (!ok) {
797         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
798                         device);
799         return 1;
800     }
801     if (fs_type == VFAT) {
802         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
803         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
804             xpwrite(fd, &bs->bsCode, bsCodeLen,
805                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
806             perror("writing fat bootblock");
807             return 1;
808         }
809     } else {
810         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
811             perror("writing bootblock");
812             return 1;
813         }
814     }
815
816     return 0;
817 }
818
819 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
820 {
821     char *file;
822     int fd = -1, dirfd = -1;
823     int modbytes;
824
825     asprintf(&file, "%s%sextlinux.sys",
826              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
827     if (!file) {
828         perror(program);
829         return 1;
830     }
831
832     dirfd = open(path, O_RDONLY | O_DIRECTORY);
833     if (dirfd < 0) {
834         perror(path);
835         goto bail;
836     }
837
838     fd = open(file, O_RDONLY);
839     if (fd < 0) {
840         if (errno != ENOENT) {
841             perror(file);
842             goto bail;
843         }
844     } else {
845         clear_attributes(fd);
846     }
847     close(fd);
848
849     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
850               S_IRUSR | S_IRGRP | S_IROTH);
851     if (fd < 0) {
852         perror(file);
853         goto bail;
854     }
855
856     /* Write it the first time */
857     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
858         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
859                 boot_image_len) != 2 * ADV_SIZE) {
860         fprintf(stderr, "%s: write failure on %s\n", program, file);
861         goto bail;
862     }
863
864     /* Map the file, and patch the initial sector accordingly */
865     modbytes = patch_file_and_bootblock(fd, path, devfd);
866
867     /* Write the patch area again - this relies on the file being
868        overwritten in place! */
869     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
870         fprintf(stderr, "%s: write failure on %s\n", program, file);
871         goto bail;
872     }
873
874     /* Attempt to set immutable flag and remove all write access */
875     /* Only set immutable flag if file is owned by root */
876     set_attributes(fd);
877
878     if (fstat(fd, rst)) {
879         perror(file);
880         goto bail;
881     }
882
883     close(dirfd);
884     close(fd);
885     return 0;
886
887 bail:
888     if (dirfd >= 0)
889         close(dirfd);
890     if (fd >= 0)
891         close(fd);
892
893     return 1;
894 }
895
896 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
897    is not managered by btrfs tree, so actually this is not installed as files.
898    since the cow feature of btrfs will move the extlinux.sys every where */
899 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
900 {
901     patch_file_and_bootblock(-1, path, devfd);
902     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
903                 != boot_image_len) {
904         perror("writing bootblock");
905         return 1;
906     }
907     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
908     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
909                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
910         perror("writing adv");
911         return 1;
912     }
913     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
914     if (stat(path, rst)) {
915         perror(path);
916         return 1;
917     }
918     return 0;
919 }
920
921 int install_file(const char *path, int devfd, struct stat *rst)
922 {
923         if (fs_type == EXT2 || fs_type == VFAT)
924                 return ext2_fat_install_file(path, devfd, rst);
925         else if (fs_type == BTRFS)
926                 return btrfs_install_file(path, devfd, rst);
927         return 1;
928 }
929
930 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
931    sector; this is consistent with FAT filesystems. */
932 int already_installed(int devfd)
933 {
934     char buffer[8];
935
936     xpread(devfd, buffer, 8, 3);
937     return !memcmp(buffer, "EXTLINUX", 8);
938 }
939
940 #ifdef __KLIBC__
941 static char devname_buf[64];
942
943 static void device_cleanup(void)
944 {
945     unlink(devname_buf);
946 }
947 #endif
948
949 /* Verify that a device fd and a pathname agree.
950    Return 0 on valid, -1 on error. */
951 static int validate_device(const char *path, int devfd)
952 {
953     struct stat pst, dst;
954     struct statfs sfs;
955
956     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
957         return -1;
958     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
959     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
960         return 0;
961     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
962 }
963
964 #ifndef __KLIBC__
965 static const char *find_device(const char *mtab_file, dev_t dev)
966 {
967     struct mntent *mnt;
968     struct stat dst;
969     FILE *mtab;
970     const char *devname = NULL;
971     bool done;
972
973     mtab = setmntent(mtab_file, "r");
974     if (!mtab)
975         return NULL;
976
977     done = false;
978     while ((mnt = getmntent(mtab))) {
979         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
980         switch (fs_type) {
981         case BTRFS:
982                 if (!strcmp(mnt->mnt_type, "btrfs") &&
983                     !stat(mnt->mnt_dir, &dst) &&
984                     dst.st_dev == dev) {
985                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
986
987                     if (opt) {
988                         if (!subvol[0]) {
989                             char *tmp;
990
991                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
992                             tmp = strchr(subvol, 32);
993                             if (tmp)
994                                 *tmp = '\0';
995                         }
996                         break; /* should break and let upper layer try again */
997                     } else
998                         done = true;
999                 }
1000                 break;
1001         case EXT2:
1002                 if ((!strcmp(mnt->mnt_type, "ext2") ||
1003                      !strcmp(mnt->mnt_type, "ext3") ||
1004                      !strcmp(mnt->mnt_type, "ext4")) &&
1005                     !stat(mnt->mnt_fsname, &dst) &&
1006                     dst.st_rdev == dev) {
1007                     done = true;
1008                     break;
1009                 }
1010         case VFAT:
1011                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
1012                     !stat(mnt->mnt_fsname, &dst) &&
1013                     dst.st_rdev == dev) {
1014                     done = true;
1015                     break;
1016                 }
1017         }
1018         if (done) {
1019                 devname = strdup(mnt->mnt_fsname);
1020                 break;
1021         }
1022     }
1023     endmntent(mtab);
1024
1025     return devname;
1026 }
1027 #endif
1028
1029 static const char *get_devname(const char *path)
1030 {
1031     const char *devname = NULL;
1032     struct stat st;
1033     struct statfs sfs;
1034
1035     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
1036         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1037         return devname;
1038     }
1039     if (statfs(path, &sfs)) {
1040         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1041         return devname;
1042     }
1043 #ifdef __KLIBC__
1044
1045     /* klibc doesn't have getmntent and friends; instead, just create
1046        a new device with the appropriate device type */
1047     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1048              major(st.st_dev), minor(st.st_dev));
1049
1050     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1051         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1052         return devname;
1053     }
1054
1055     atexit(device_cleanup);     /* unlink the device node on exit */
1056     devname = devname_buf;
1057
1058 #else
1059
1060     /* check /etc/mtab first, since btrfs subvol info is only in here */
1061     devname = find_device("/etc/mtab", st.st_dev);
1062     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1063         char parent[256];
1064         char *tmp;
1065
1066         strcpy(parent, path);
1067         tmp = strrchr(parent, '/');
1068         if (tmp) {
1069             *tmp = '\0';
1070             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1071             devname = get_devname(parent);
1072         } else
1073             devname = NULL;
1074     }
1075     if (!devname) {
1076         /* Didn't find it in /etc/mtab, try /proc/mounts */
1077         devname = find_device("/proc/mounts", st.st_dev);
1078     }
1079     if (!devname) {
1080         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1081         return devname;
1082     }
1083
1084     fprintf(stderr, "%s is device %s\n", path, devname);
1085 #endif
1086     return devname;
1087 }
1088
1089 static int open_device(const char *path, struct stat *st, const char **_devname)
1090 {
1091     int devfd;
1092     const char *devname = NULL;
1093     struct statfs sfs;
1094
1095     if (st)
1096         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1097                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1098                 return -1;
1099         }
1100
1101     if (statfs(path, &sfs)) {
1102         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1103         return -1;
1104     }
1105     if (sfs.f_type == EXT2_SUPER_MAGIC)
1106         fs_type = EXT2;
1107     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1108         fs_type = BTRFS;
1109     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1110         fs_type = VFAT;
1111
1112     if (!fs_type) {
1113         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1114                 program, path);
1115         return -1;
1116     }
1117
1118     devfd = -1;
1119     devname = get_devname(path);
1120     if (_devname)
1121         *_devname = devname;
1122
1123     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1124         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1125         return -1;
1126     }
1127
1128     /* Verify that the device we opened is the device intended */
1129     if (validate_device(path, devfd)) {
1130         fprintf(stderr, "%s: path %s doesn't match device %s\n",
1131                 program, path, devname);
1132         close(devfd);
1133         return -1;
1134     }
1135     return devfd;
1136 }
1137
1138 int install_loader(const char *path, int update_only)
1139 {
1140     struct stat st, fst;
1141     int devfd, rv;
1142     const char *devname;
1143
1144     devfd = open_device(path, &st, &devname);
1145     if (devfd < 0)
1146         return 1;
1147
1148     if (update_only && !already_installed(devfd)) {
1149         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1150                 program);
1151         close(devfd);
1152         return 1;
1153     }
1154
1155     /* Read a pre-existing ADV, if already installed */
1156     if (opt.reset_adv)
1157         syslinux_reset_adv(syslinux_adv);
1158     else if (read_adv(path, devfd) < 0) {
1159         close(devfd);
1160         return 1;
1161     }
1162     if (modify_adv() < 0) {
1163         close(devfd);
1164         return 1;
1165     }
1166
1167     /* Install extlinux.sys */
1168     if (install_file(path, devfd, &fst)) {
1169         close(devfd);
1170         return 1;
1171     }
1172     if (fst.st_dev != st.st_dev) {
1173         fprintf(stderr, "%s: file system changed under us - aborting!\n",
1174                 program);
1175         close(devfd);
1176         return 1;
1177     }
1178
1179     sync();
1180     rv = install_bootblock(devfd, devname);
1181     close(devfd);
1182     sync();
1183
1184     return rv;
1185 }
1186
1187 /*
1188  * Modify the ADV of an existing installation
1189  */
1190 int modify_existing_adv(const char *path)
1191 {
1192     int devfd;
1193
1194     devfd = open_device(path, NULL, NULL);
1195     if (devfd < 0)
1196         return 1;
1197
1198     if (opt.reset_adv)
1199         syslinux_reset_adv(syslinux_adv);
1200     else if (read_adv(path, devfd) < 0) {
1201         close(devfd);
1202         return 1;
1203     }
1204     if (modify_adv() < 0) {
1205         close(devfd);
1206         return 1;
1207     }
1208     if (write_adv(path, devfd) < 0) {
1209         close(devfd);
1210         return 1;
1211     }
1212     close(devfd);
1213     return 0;
1214 }
1215
1216 int main(int argc, char *argv[])
1217 {
1218     int o;
1219     const char *directory;
1220     int update_only = -1;
1221
1222     program = argv[0];
1223
1224     while ((o = getopt_long(argc, argv, short_options,
1225                             long_options, NULL)) != EOF) {
1226         switch (o) {
1227         case 'z':
1228             opt.heads = 64;
1229             opt.sectors = 32;
1230             break;
1231         case 'S':
1232             opt.sectors = strtoul(optarg, NULL, 0);
1233             if (opt.sectors < 1 || opt.sectors > 63) {
1234                 fprintf(stderr,
1235                         "%s: invalid number of sectors: %u (must be 1-63)\n",
1236                         program, opt.sectors);
1237                 exit(EX_USAGE);
1238             }
1239             break;
1240         case 'H':
1241             opt.heads = strtoul(optarg, NULL, 0);
1242             if (opt.heads < 1 || opt.heads > 256) {
1243                 fprintf(stderr,
1244                         "%s: invalid number of heads: %u (must be 1-256)\n",
1245                         program, opt.heads);
1246                 exit(EX_USAGE);
1247             }
1248             break;
1249         case 'r':
1250             opt.raid_mode = 1;
1251             break;
1252         case 's':
1253             opt.stupid_mode = 1;
1254             break;
1255         case 'i':
1256             update_only = 0;
1257             break;
1258         case 'u':
1259         case 'U':
1260             update_only = 1;
1261             break;
1262         case 'h':
1263             usage(0);
1264             break;
1265         case 'o':
1266             opt.set_once = optarg;
1267             break;
1268         case 'O':
1269             opt.set_once = "";
1270             break;
1271         case OPT_RESET_ADV:
1272             opt.reset_adv = 1;
1273             break;
1274         case 'v':
1275             fputs("extlinux " VERSION_STR
1276                   "  Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1277             exit(0);
1278         default:
1279             usage(EX_USAGE);
1280         }
1281     }
1282
1283     directory = argv[optind];
1284
1285     if (!directory)
1286         usage(EX_USAGE);
1287
1288     if (update_only == -1) {
1289         if (opt.reset_adv || opt.set_once) {
1290             return modify_existing_adv(directory);
1291         } else {
1292             usage(EX_USAGE);
1293         }
1294     }
1295
1296     return install_loader(directory, update_only);
1297 }