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