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