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