Fix prototype for generate_extents
[profile/ivi/syslinux.git] / extlinux / main.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * extlinux.c
16  *
17  * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
18  */
19
20 #define  _GNU_SOURCE            /* Enable everything */
21 #include <inttypes.h>
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
23 typedef uint64_t u64;
24 #include <alloca.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #ifndef __KLIBC__
30 #include <mntent.h>
31 #endif
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <getopt.h>
37 #include <sysexits.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/mount.h>
42 #include <sys/vfs.h>
43
44 #include <linux/fd.h>           /* Floppy geometry */
45 #include <linux/hdreg.h>        /* Hard disk geometry */
46 #define statfs _kernel_statfs   /* HACK to deal with broken 2.4 distros */
47 #include <linux/fs.h>           /* FIGETBSZ, FIBMAP */
48 #include <linux/msdos_fs.h>     /* FAT_IOCTL_SET_ATTRIBUTES */
49 #ifndef FAT_IOCTL_SET_ATTRIBUTES
50 # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
51 #endif
52 #undef statfs
53 #undef SECTOR_SIZE              /* Garbage from <linux/msdos_fs.h> */
54
55 #include "ext2_fs.h"
56 #include "btrfs.h"
57 #include "fat.h"
58 #include "../version.h"
59 #include "syslxint.h"
60 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
61 #include "setadv.h"
62 #include "syslxopt.h" /* unified options */
63
64 #ifdef DEBUG
65 # define dprintf printf
66 #else
67 # define dprintf(...) ((void)0)
68 #endif
69
70 #if defined(__linux__) && !defined(BLKGETSIZE64)
71 /* This takes a u64, but the size field says size_t.  Someone screwed big. */
72 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
73 #endif
74
75 #ifndef EXT2_SUPER_OFFSET
76 #define EXT2_SUPER_OFFSET 1024
77 #endif
78
79 /* the btrfs partition first 64K blank area is used to store boot sector and
80    boot image, the boot sector is from 0~512, the boot image starts at 2K */
81 #define BTRFS_EXTLINUX_OFFSET (2*1024)
82 #define BTRFS_SUBVOL_OPT "subvol="
83 #define BTRFS_SUBVOL_MAX 256    /* By btrfs specification */
84 static char subvol[BTRFS_SUBVOL_MAX];
85
86 /*
87  * Boot block
88  */
89 extern unsigned char extlinux_bootsect[];
90 extern unsigned int extlinux_bootsect_len;
91 #undef  boot_block
92 #undef  boot_block_len
93 #define boot_block      extlinux_bootsect
94 #define boot_block_len  extlinux_bootsect_len
95
96 /*
97  * Image file
98  */
99 extern unsigned char extlinux_image[];
100 extern unsigned int extlinux_image_len;
101 #undef  boot_image
102 #undef  boot_image_len
103 #define boot_image      extlinux_image
104 #define boot_image_len  extlinux_image_len
105
106 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
107
108 /*
109  * Get the size of a block device
110  */
111 uint64_t get_size(int devfd)
112 {
113     uint64_t bytes;
114     uint32_t sects;
115     struct stat st;
116
117 #ifdef BLKGETSIZE64
118     if (!ioctl(devfd, BLKGETSIZE64, &bytes))
119         return bytes;
120 #endif
121     if (!ioctl(devfd, BLKGETSIZE, &sects))
122         return (uint64_t) sects << 9;
123     else if (!fstat(devfd, &st) && st.st_size)
124         return st.st_size;
125     else
126         return 0;
127 }
128
129 /*
130  * Get device geometry and partition offset
131  */
132 struct geometry_table {
133     uint64_t bytes;
134     struct hd_geometry g;
135 };
136
137 /* Standard floppy disk geometries, plus LS-120.  Zipdisk geometry
138    (x/64/32) is the final fallback.  I don't know what LS-240 has
139    as its geometry, since I don't have one and don't know anyone that does,
140    and Google wasn't helpful... */
141 static const struct geometry_table standard_geometries[] = {
142     {360 * 1024, {2, 9, 40, 0}},
143     {720 * 1024, {2, 9, 80, 0}},
144     {1200 * 1024, {2, 15, 80, 0}},
145     {1440 * 1024, {2, 18, 80, 0}},
146     {1680 * 1024, {2, 21, 80, 0}},
147     {1722 * 1024, {2, 21, 80, 0}},
148     {2880 * 1024, {2, 36, 80, 0}},
149     {3840 * 1024, {2, 48, 80, 0}},
150     {123264 * 1024, {8, 32, 963, 0}},   /* LS120 */
151     {0, {0, 0, 0, 0}}
152 };
153
154 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
155 {
156     struct floppy_struct fd_str;
157     const struct geometry_table *gp;
158
159     memset(geo, 0, sizeof *geo);
160
161     if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
162         return 0;
163     } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
164         geo->heads = fd_str.head;
165         geo->sectors = fd_str.sect;
166         geo->cylinders = fd_str.track;
167         geo->start = 0;
168         return 0;
169     }
170
171     /* Didn't work.  Let's see if this is one of the standard geometries */
172     for (gp = standard_geometries; gp->bytes; gp++) {
173         if (gp->bytes == totalbytes) {
174             memcpy(geo, &gp->g, sizeof *geo);
175             return 0;
176         }
177     }
178
179     /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
180        what zipdisks use, so this would help if someone has a USB key that
181        they're booting in USB-ZIP mode. */
182
183     geo->heads = opt.heads ? : 64;
184     geo->sectors = opt.sectors ? : 32;
185     geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
186     geo->start = 0;
187
188     if (!opt.sectors && !opt.heads)
189         fprintf(stderr,
190                 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
191                 "         (on hard disks, this is usually harmless.)\n",
192                 geo->heads, geo->sectors);
193
194     return 1;
195 }
196
197 /*
198  * Generate sector extents
199  */
200 static void generate_extents(struct syslinux_extent *ex, int nptrs,
201                              const sector_t *sectp, int nsect)
202 {
203     uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
204     uint32_t base;
205     sector_t sect, lba;
206     unsigned int len;
207
208     len = lba = base = 0;
209
210     memset(ex, 0, nptrs * sizeof *ex);
211
212     while (nsect) {
213         sect = *sectp++;
214
215         if (len && sect == lba + len &&
216             ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
217             /* We can add to the current extent */
218             len++;
219             goto next;
220         }
221
222         if (len) {
223             set_64(&ex->lba, lba);
224             set_16(&ex->len, len);
225             ex++;
226         }
227
228         base = addr;
229         lba  = sect;
230         len  = 1;
231
232     next:
233         addr += SECTOR_SIZE;
234         nsect--;
235     }
236
237     if (len) {
238         set_64(&ex->lba, lba);
239         set_16(&ex->len, len);
240         ex++;
241     }
242 }
243
244 /*
245  * Query the device geometry and put it into the boot sector.
246  * Map the file and put the map in the boot sector and file.
247  * Stick the "current directory" inode number into the file.
248  *
249  * Returns the number of modified bytes in the boot file.
250  */
251 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
252 {
253     struct stat dirst, xdst;
254     struct hd_geometry geo;
255     sector_t *sectp;
256     uint64_t totalbytes, totalsectors;
257     int nsect;
258     uint32_t *wp;
259     struct boot_sector *bs;
260     struct patch_area *patcharea;
261     struct syslinux_extent *ex;
262     int i, dw, nptrs;
263     uint32_t csum;
264     int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
265     char *dirpath, *subpath, *xdirpath, *xsubpath;
266     uint64_t *advptrs;
267
268     dirpath = realpath(dir, NULL);
269     if (!dirpath || stat(dir, &dirst)) {
270         perror("accessing install directory");
271         exit(255);              /* This should never happen */
272     }
273
274     if (lstat(dirpath, &xdst) ||
275         dirst.st_ino != xdst.st_ino ||
276         dirst.st_dev != xdst.st_dev) {
277         perror("realpath returned nonsense");
278         exit(255);
279     }
280
281     subpath = strchr(dirpath, '\0');
282     for (;;) {
283         if (*subpath == '/') {
284             if (subpath > dirpath) {
285                 *subpath = '\0';
286                 xsubpath = subpath+1;
287                 xdirpath = dirpath;
288             } else {
289                 xsubpath = subpath;
290                 xdirpath = "/";
291             }
292             if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
293                 subpath = strchr(subpath+1, '/');
294                 if (!subpath)
295                     subpath = "/"; /* It's the root of the filesystem */
296                 break;
297             }
298             *subpath = '/';
299         }
300
301         if (subpath == dirpath)
302             break;
303
304         subpath--;
305     }
306
307     /* Now subpath should contain the path relative to the fs base */
308     dprintf("subpath = %s\n", subpath);
309
310     totalbytes = get_size(devfd);
311     get_geometry(devfd, totalbytes, &geo);
312
313     if (opt.heads)
314         geo.heads = opt.heads;
315     if (opt.sectors)
316         geo.sectors = opt.sectors;
317
318     /* Patch this into a fake FAT superblock.  This isn't because
319        FAT is a good format in any way, it's because it lets the
320        early bootstrap share code with the FAT version. */
321     dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
322
323     bs = (struct boot_sector *)boot_block;
324
325     totalsectors = totalbytes >> SECTOR_SHIFT;
326     if (totalsectors >= 65536) {
327         set_16(&bs->bsSectors, 0);
328     } else {
329         set_16(&bs->bsSectors, totalsectors);
330     }
331     set_32(&bs->bsHugeSectors, totalsectors);
332
333     set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
334     set_16(&bs->bsSecPerTrack, geo.sectors);
335     set_16(&bs->bsHeads, geo.heads);
336     set_32(&bs->bsHiddenSecs, geo.start);
337
338     /* If we're in RAID mode then patch the appropriate instruction;
339        either way write the proper boot signature */
340     i = get_16(&bs->bsSignature);
341     if (opt.raid_mode)
342         set_16((uint16_t *) (boot_block + i), 0x18CD);  /* INT 18h */
343
344     set_16(&bs->bsSignature, 0xAA55);
345
346     /* Construct the boot file */
347
348     dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
349     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
350     nsect += 2;                 /* Two sectors for the ADV */
351     sectp = alloca(sizeof(sector_t) * nsect);
352     if (fs_type == EXT2 || fs_type == VFAT) {
353         if (sectmap(fd, sectp, nsect)) {
354                 perror("bmap");
355                 exit(1);
356         }
357     } else if (fs_type == BTRFS) {
358         int i;
359
360         for (i = 0; i < nsect; i++)
361                 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
362     }
363
364     /* First sector need pointer in boot sector */
365     set_64(&bs->NextSector, *sectp++);
366
367     /* Search for LDLINUX_MAGIC to find the patch area */
368     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
369     patcharea = (struct patch_area *)wp;
370
371     /* Set up the totals */
372     dw = boot_image_len >> 2;   /* COMPLETE dwords, excluding ADV */
373     set_16(&patcharea->data_sectors, nsect - 2);        /* -2 for the ADVs */
374     set_16(&patcharea->adv_sectors, 2);
375     set_32(&patcharea->dwords, dw);
376
377     /* Stupid mode? */
378     if (opt.stupid_mode)
379         set_16(&patcharea->maxtransfer, 1);
380
381     /* Set the sector extents */
382     secptroffset = get_16(&patcharea->secptroffset);
383     ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
384     nptrs = get_16(&patcharea->secptrcnt);
385
386     if (nsect > nptrs) {
387         /* Not necessarily an error in this case, but a general problem */
388         fprintf(stderr, "Insufficient extent space, build error!\n");
389         exit(1);
390     }
391
392     /* -1 for the pointer in the boot sector, -2 for the two ADVs */
393     generate_extents(ex, nptrs, sectp, nsect-1-2);
394
395     /* ADV pointers */
396     advptrs = (uint64_t *)((char *)boot_image +
397                            get_16(&patcharea->advptroffset));
398     set_64(&advptrs[0], sectp[nsect-1-2]);
399     set_64(&advptrs[1], sectp[nsect-1-1]);
400
401     /* Poke in the base directory path */
402     diroffset = get_16(&patcharea->diroffset);
403     dirlen = get_16(&patcharea->dirlen);
404     if (dirlen <= strlen(subpath)) {
405         fprintf(stderr, "Subdirectory path too long... aborting install!\n");
406         exit(1);
407     }
408     strncpy((char *)boot_image + diroffset, subpath, dirlen);
409     free(dirpath);
410
411     /* write subvol info if we have */
412     subvoloffset = get_16(&patcharea->subvoloffset);
413     subvollen = get_16(&patcharea->subvollen);
414     if (subvollen <= strlen(subvol)) {
415         fprintf(stderr, "Subvol name too long... aborting install!\n");
416         exit(1);
417     }
418     strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
419
420     /* Now produce a checksum */
421     set_32(&patcharea->checksum, 0);
422
423     csum = LDLINUX_MAGIC;
424     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
425         csum -= get_32(wp);     /* Negative checksum */
426
427     set_32(&patcharea->checksum, csum);
428
429     /*
430      * Assume all bytes modified.  This can be optimized at the expense
431      * of keeping track of what the highest modified address ever was.
432      */
433     return dw << 2;
434 }
435
436 /*
437  * Make any user-specified ADV modifications
438  */
439 int modify_adv(void)
440 {
441     int rv = 0;
442
443     if (opt.set_once) {
444         if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
445             fprintf(stderr, "%s: not enough space for boot-once command\n",
446                     program);
447             rv = -1;
448         }
449     }
450     if (opt.menu_save) {
451         if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
452             fprintf(stderr, "%s: not enough space for menu-save label\n",
453                     program);
454             rv = -1;
455         }
456     }
457
458     return rv;
459 }
460
461 /*
462  * Install the boot block on the specified device.
463  * Must be run AFTER install_file()!
464  */
465 int install_bootblock(int fd, const char *device)
466 {
467     struct ext2_super_block sb;
468     struct btrfs_super_block sb2;
469     struct boot_sector sb3;
470     bool ok = false;
471
472     if (fs_type == EXT2) {
473         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
474                 perror("reading superblock");
475                 return 1;
476         }
477         if (sb.s_magic == EXT2_SUPER_MAGIC)
478                 ok = true;
479     } else if (fs_type == BTRFS) {
480         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
481                         != sizeof sb2) {
482                 perror("reading superblock");
483                 return 1;
484         }
485         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
486                 ok = true;
487     } else if (fs_type == VFAT) {
488         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
489                 perror("reading fat superblock");
490                 return 1;
491         }
492         if (sb3.bsResSectors && sb3.bsFATs &&
493             (strstr(sb3.bs16.FileSysType, "FAT") ||
494              strstr(sb3.bs32.FileSysType, "FAT")))
495                 ok = true;
496     }
497     if (!ok) {
498         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
499                         device);
500         return 1;
501     }
502     if (fs_type == VFAT) {
503         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
504         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
505             xpwrite(fd, &bs->bsCode, bsCodeLen,
506                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
507             perror("writing fat bootblock");
508             return 1;
509         }
510     } else {
511         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
512             perror("writing bootblock");
513             return 1;
514         }
515     }
516
517     return 0;
518 }
519
520 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
521 {
522     char *file;
523     int fd = -1, dirfd = -1;
524     int modbytes;
525
526     asprintf(&file, "%s%sextlinux.sys",
527              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
528     if (!file) {
529         perror(program);
530         return 1;
531     }
532
533     dirfd = open(path, O_RDONLY | O_DIRECTORY);
534     if (dirfd < 0) {
535         perror(path);
536         goto bail;
537     }
538
539     fd = open(file, O_RDONLY);
540     if (fd < 0) {
541         if (errno != ENOENT) {
542             perror(file);
543             goto bail;
544         }
545     } else {
546         clear_attributes(fd);
547     }
548     close(fd);
549
550     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
551               S_IRUSR | S_IRGRP | S_IROTH);
552     if (fd < 0) {
553         perror(file);
554         goto bail;
555     }
556
557     /* Write it the first time */
558     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
559         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
560                 boot_image_len) != 2 * ADV_SIZE) {
561         fprintf(stderr, "%s: write failure on %s\n", program, file);
562         goto bail;
563     }
564
565     /* Map the file, and patch the initial sector accordingly */
566     modbytes = patch_file_and_bootblock(fd, path, devfd);
567
568     /* Write the patch area again - this relies on the file being
569        overwritten in place! */
570     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
571         fprintf(stderr, "%s: write failure on %s\n", program, file);
572         goto bail;
573     }
574
575     /* Attempt to set immutable flag and remove all write access */
576     /* Only set immutable flag if file is owned by root */
577     set_attributes(fd);
578
579     if (fstat(fd, rst)) {
580         perror(file);
581         goto bail;
582     }
583
584     close(dirfd);
585     close(fd);
586     return 0;
587
588 bail:
589     if (dirfd >= 0)
590         close(dirfd);
591     if (fd >= 0)
592         close(fd);
593
594     return 1;
595 }
596
597 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
598    is not managered by btrfs tree, so actually this is not installed as files.
599    since the cow feature of btrfs will move the extlinux.sys every where */
600 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
601 {
602     patch_file_and_bootblock(-1, path, devfd);
603     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
604                 != boot_image_len) {
605         perror("writing bootblock");
606         return 1;
607     }
608     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
609     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
610                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
611         perror("writing adv");
612         return 1;
613     }
614     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
615     if (stat(path, rst)) {
616         perror(path);
617         return 1;
618     }
619     return 0;
620 }
621
622 int install_file(const char *path, int devfd, struct stat *rst)
623 {
624         if (fs_type == EXT2 || fs_type == VFAT)
625                 return ext2_fat_install_file(path, devfd, rst);
626         else if (fs_type == BTRFS)
627                 return btrfs_install_file(path, devfd, rst);
628         return 1;
629 }
630
631 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
632    sector; this is consistent with FAT filesystems. */
633 int already_installed(int devfd)
634 {
635     char buffer[8];
636
637     xpread(devfd, buffer, 8, 3);
638     return !memcmp(buffer, "EXTLINUX", 8);
639 }
640
641 #ifdef __KLIBC__
642 static char devname_buf[64];
643
644 static void device_cleanup(void)
645 {
646     unlink(devname_buf);
647 }
648 #endif
649
650 /* Verify that a device fd and a pathname agree.
651    Return 0 on valid, -1 on error. */
652 static int validate_device(const char *path, int devfd)
653 {
654     struct stat pst, dst;
655     struct statfs sfs;
656
657     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
658         return -1;
659     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
660     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
661         return 0;
662     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
663 }
664
665 #ifndef __KLIBC__
666 static const char *find_device(const char *mtab_file, dev_t dev)
667 {
668     struct mntent *mnt;
669     struct stat dst;
670     FILE *mtab;
671     const char *devname = NULL;
672     bool done;
673
674     mtab = setmntent(mtab_file, "r");
675     if (!mtab)
676         return NULL;
677
678     done = false;
679     while ((mnt = getmntent(mtab))) {
680         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
681         switch (fs_type) {
682         case BTRFS:
683                 if (!strcmp(mnt->mnt_type, "btrfs") &&
684                     !stat(mnt->mnt_dir, &dst) &&
685                     dst.st_dev == dev) {
686                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
687
688                     if (opt) {
689                         if (!subvol[0]) {
690                             char *tmp;
691
692                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
693                             tmp = strchr(subvol, 32);
694                             if (tmp)
695                                 *tmp = '\0';
696                         }
697                         break; /* should break and let upper layer try again */
698                     } else
699                         done = true;
700                 }
701                 break;
702         case EXT2:
703                 if ((!strcmp(mnt->mnt_type, "ext2") ||
704                      !strcmp(mnt->mnt_type, "ext3") ||
705                      !strcmp(mnt->mnt_type, "ext4")) &&
706                     !stat(mnt->mnt_fsname, &dst) &&
707                     dst.st_rdev == dev) {
708                     done = true;
709                     break;
710                 }
711         case VFAT:
712                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
713                     !stat(mnt->mnt_fsname, &dst) &&
714                     dst.st_rdev == dev) {
715                     done = true;
716                     break;
717                 }
718         case NONE:
719             break;
720         }
721         if (done) {
722                 devname = strdup(mnt->mnt_fsname);
723                 break;
724         }
725     }
726     endmntent(mtab);
727
728     return devname;
729 }
730 #endif
731
732 static const char *get_devname(const char *path)
733 {
734     const char *devname = NULL;
735     struct stat st;
736     struct statfs sfs;
737
738     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
739         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
740         return devname;
741     }
742     if (statfs(path, &sfs)) {
743         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
744         return devname;
745     }
746 #ifdef __KLIBC__
747
748     /* klibc doesn't have getmntent and friends; instead, just create
749        a new device with the appropriate device type */
750     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
751              major(st.st_dev), minor(st.st_dev));
752
753     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
754         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
755         return devname;
756     }
757
758     atexit(device_cleanup);     /* unlink the device node on exit */
759     devname = devname_buf;
760
761 #else
762
763     /* check /etc/mtab first, since btrfs subvol info is only in here */
764     devname = find_device("/etc/mtab", st.st_dev);
765     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
766         char parent[256];
767         char *tmp;
768
769         strcpy(parent, path);
770         tmp = strrchr(parent, '/');
771         if (tmp) {
772             *tmp = '\0';
773             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
774             devname = get_devname(parent);
775         } else
776             devname = NULL;
777     }
778     if (!devname) {
779         /* Didn't find it in /etc/mtab, try /proc/mounts */
780         devname = find_device("/proc/mounts", st.st_dev);
781     }
782     if (!devname) {
783         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
784         return devname;
785     }
786
787     fprintf(stderr, "%s is device %s\n", path, devname);
788 #endif
789     return devname;
790 }
791
792 static int open_device(const char *path, struct stat *st, const char **_devname)
793 {
794     int devfd;
795     const char *devname = NULL;
796     struct statfs sfs;
797
798     if (st)
799         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
800                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
801                 return -1;
802         }
803
804     if (statfs(path, &sfs)) {
805         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
806         return -1;
807     }
808     if (sfs.f_type == EXT2_SUPER_MAGIC)
809         fs_type = EXT2;
810     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
811         fs_type = BTRFS;
812     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
813         fs_type = VFAT;
814
815     if (!fs_type) {
816         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
817                 program, path);
818         return -1;
819     }
820
821     devfd = -1;
822     devname = get_devname(path);
823     if (_devname)
824         *_devname = devname;
825
826     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
827         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
828         return -1;
829     }
830
831     /* Verify that the device we opened is the device intended */
832     if (validate_device(path, devfd)) {
833         fprintf(stderr, "%s: path %s doesn't match device %s\n",
834                 program, path, devname);
835         close(devfd);
836         return -1;
837     }
838     return devfd;
839 }
840
841 static int ext_read_adv(const char *path, const char *cfg, int devfd)
842 {
843     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
844         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
845                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
846                 perror("btrfs writing adv");
847                 return 1;
848         }
849         return 0;
850     }
851     return read_adv(path, cfg);
852 }
853
854 static int ext_write_adv(const char *path, const char *cfg, int devfd)
855 {
856     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
857         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
858                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
859                 perror("writing adv");
860                 return 1;
861         }
862         return 0;
863     }
864     return write_adv(path, cfg);
865 }
866
867 int install_loader(const char *path, int update_only)
868 {
869     struct stat st, fst;
870     int devfd, rv;
871     const char *devname;
872
873     devfd = open_device(path, &st, &devname);
874     if (devfd < 0)
875         return 1;
876
877     if (update_only && !already_installed(devfd)) {
878         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
879                 program);
880         close(devfd);
881         return 1;
882     }
883
884     /* Read a pre-existing ADV, if already installed */
885     if (opt.reset_adv)
886         syslinux_reset_adv(syslinux_adv);
887     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
888         close(devfd);
889         return 1;
890     }
891     if (modify_adv() < 0) {
892         close(devfd);
893         return 1;
894     }
895
896     /* Install extlinux.sys */
897     if (install_file(path, devfd, &fst)) {
898         close(devfd);
899         return 1;
900     }
901     if (fst.st_dev != st.st_dev) {
902         fprintf(stderr, "%s: file system changed under us - aborting!\n",
903                 program);
904         close(devfd);
905         return 1;
906     }
907
908     sync();
909     rv = install_bootblock(devfd, devname);
910     close(devfd);
911     sync();
912
913     return rv;
914 }
915
916 /*
917  * Modify the ADV of an existing installation
918  */
919 int modify_existing_adv(const char *path)
920 {
921     int devfd;
922
923     devfd = open_device(path, NULL, NULL);
924     if (devfd < 0)
925         return 1;
926
927     if (opt.reset_adv)
928         syslinux_reset_adv(syslinux_adv);
929     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
930         close(devfd);
931         return 1;
932     }
933     if (modify_adv() < 0) {
934         close(devfd);
935         return 1;
936     }
937     if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
938         close(devfd);
939         return 1;
940     }
941     close(devfd);
942     return 0;
943 }
944
945 int main(int argc, char *argv[])
946 {
947     parse_options(argc, argv, MODE_EXTLINUX);
948
949     if (!opt.directory)
950         usage(EX_USAGE, 0);
951
952     if (opt.update_only == -1) {
953         if (opt.reset_adv || opt.set_once || opt.menu_save)
954             return modify_existing_adv(opt.directory);
955         else
956             usage(EX_USAGE, MODE_EXTLINUX);
957     }
958
959     return install_loader(opt.directory, opt.update_only);
960 }