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