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