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