extlinux: add a --menu-save option
[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     if (opt.menu_save) {
385         if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
386             fprintf(stderr, "%s: not enough space for menu-save label\n",
387                     program);
388             rv = -1;
389         }
390     }
391
392     return rv;
393 }
394
395 /*
396  * Install the boot block on the specified device.
397  * Must be run AFTER install_file()!
398  */
399 int install_bootblock(int fd, const char *device)
400 {
401     struct ext2_super_block sb;
402     struct btrfs_super_block sb2;
403     struct boot_sector sb3;
404     bool ok = false;
405
406     if (fs_type == EXT2) {
407         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
408                 perror("reading superblock");
409                 return 1;
410         }
411         if (sb.s_magic == EXT2_SUPER_MAGIC)
412                 ok = true;
413     } else if (fs_type == BTRFS) {
414         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
415                         != sizeof sb2) {
416                 perror("reading superblock");
417                 return 1;
418         }
419         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
420                 ok = true;
421     } else if (fs_type == VFAT) {
422         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
423                 perror("reading fat superblock");
424                 return 1;
425         }
426         if (sb3.bsResSectors && sb3.bsFATs &&
427             (strstr(sb3.bs16.FileSysType, "FAT") ||
428              strstr(sb3.bs32.FileSysType, "FAT")))
429                 ok = true;
430     }
431     if (!ok) {
432         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
433                         device);
434         return 1;
435     }
436     if (fs_type == VFAT) {
437         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
438         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
439             xpwrite(fd, &bs->bsCode, bsCodeLen,
440                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
441             perror("writing fat bootblock");
442             return 1;
443         }
444     } else {
445         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
446             perror("writing bootblock");
447             return 1;
448         }
449     }
450
451     return 0;
452 }
453
454 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
455 {
456     char *file;
457     int fd = -1, dirfd = -1;
458     int modbytes;
459
460     asprintf(&file, "%s%sextlinux.sys",
461              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
462     if (!file) {
463         perror(program);
464         return 1;
465     }
466
467     dirfd = open(path, O_RDONLY | O_DIRECTORY);
468     if (dirfd < 0) {
469         perror(path);
470         goto bail;
471     }
472
473     fd = open(file, O_RDONLY);
474     if (fd < 0) {
475         if (errno != ENOENT) {
476             perror(file);
477             goto bail;
478         }
479     } else {
480         clear_attributes(fd);
481     }
482     close(fd);
483
484     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
485               S_IRUSR | S_IRGRP | S_IROTH);
486     if (fd < 0) {
487         perror(file);
488         goto bail;
489     }
490
491     /* Write it the first time */
492     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
493         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
494                 boot_image_len) != 2 * ADV_SIZE) {
495         fprintf(stderr, "%s: write failure on %s\n", program, file);
496         goto bail;
497     }
498
499     /* Map the file, and patch the initial sector accordingly */
500     modbytes = patch_file_and_bootblock(fd, path, devfd);
501
502     /* Write the patch area again - this relies on the file being
503        overwritten in place! */
504     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
505         fprintf(stderr, "%s: write failure on %s\n", program, file);
506         goto bail;
507     }
508
509     /* Attempt to set immutable flag and remove all write access */
510     /* Only set immutable flag if file is owned by root */
511     set_attributes(fd);
512
513     if (fstat(fd, rst)) {
514         perror(file);
515         goto bail;
516     }
517
518     close(dirfd);
519     close(fd);
520     return 0;
521
522 bail:
523     if (dirfd >= 0)
524         close(dirfd);
525     if (fd >= 0)
526         close(fd);
527
528     return 1;
529 }
530
531 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
532    is not managered by btrfs tree, so actually this is not installed as files.
533    since the cow feature of btrfs will move the extlinux.sys every where */
534 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
535 {
536     patch_file_and_bootblock(-1, path, devfd);
537     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
538                 != boot_image_len) {
539         perror("writing bootblock");
540         return 1;
541     }
542     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
543     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
544                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
545         perror("writing adv");
546         return 1;
547     }
548     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
549     if (stat(path, rst)) {
550         perror(path);
551         return 1;
552     }
553     return 0;
554 }
555
556 int install_file(const char *path, int devfd, struct stat *rst)
557 {
558         if (fs_type == EXT2 || fs_type == VFAT)
559                 return ext2_fat_install_file(path, devfd, rst);
560         else if (fs_type == BTRFS)
561                 return btrfs_install_file(path, devfd, rst);
562         return 1;
563 }
564
565 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
566    sector; this is consistent with FAT filesystems. */
567 int already_installed(int devfd)
568 {
569     char buffer[8];
570
571     xpread(devfd, buffer, 8, 3);
572     return !memcmp(buffer, "EXTLINUX", 8);
573 }
574
575 #ifdef __KLIBC__
576 static char devname_buf[64];
577
578 static void device_cleanup(void)
579 {
580     unlink(devname_buf);
581 }
582 #endif
583
584 /* Verify that a device fd and a pathname agree.
585    Return 0 on valid, -1 on error. */
586 static int validate_device(const char *path, int devfd)
587 {
588     struct stat pst, dst;
589     struct statfs sfs;
590
591     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
592         return -1;
593     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
594     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
595         return 0;
596     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
597 }
598
599 #ifndef __KLIBC__
600 static const char *find_device(const char *mtab_file, dev_t dev)
601 {
602     struct mntent *mnt;
603     struct stat dst;
604     FILE *mtab;
605     const char *devname = NULL;
606     bool done;
607
608     mtab = setmntent(mtab_file, "r");
609     if (!mtab)
610         return NULL;
611
612     done = false;
613     while ((mnt = getmntent(mtab))) {
614         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
615         switch (fs_type) {
616         case BTRFS:
617                 if (!strcmp(mnt->mnt_type, "btrfs") &&
618                     !stat(mnt->mnt_dir, &dst) &&
619                     dst.st_dev == dev) {
620                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
621
622                     if (opt) {
623                         if (!subvol[0]) {
624                             char *tmp;
625
626                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
627                             tmp = strchr(subvol, 32);
628                             if (tmp)
629                                 *tmp = '\0';
630                         }
631                         break; /* should break and let upper layer try again */
632                     } else
633                         done = true;
634                 }
635                 break;
636         case EXT2:
637                 if ((!strcmp(mnt->mnt_type, "ext2") ||
638                      !strcmp(mnt->mnt_type, "ext3") ||
639                      !strcmp(mnt->mnt_type, "ext4")) &&
640                     !stat(mnt->mnt_fsname, &dst) &&
641                     dst.st_rdev == dev) {
642                     done = true;
643                     break;
644                 }
645         case VFAT:
646                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
647                     !stat(mnt->mnt_fsname, &dst) &&
648                     dst.st_rdev == dev) {
649                     done = true;
650                     break;
651                 }
652         case NONE:
653             break;
654         }
655         if (done) {
656                 devname = strdup(mnt->mnt_fsname);
657                 break;
658         }
659     }
660     endmntent(mtab);
661
662     return devname;
663 }
664 #endif
665
666 static const char *get_devname(const char *path)
667 {
668     const char *devname = NULL;
669     struct stat st;
670     struct statfs sfs;
671
672     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
673         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
674         return devname;
675     }
676     if (statfs(path, &sfs)) {
677         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
678         return devname;
679     }
680 #ifdef __KLIBC__
681
682     /* klibc doesn't have getmntent and friends; instead, just create
683        a new device with the appropriate device type */
684     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
685              major(st.st_dev), minor(st.st_dev));
686
687     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
688         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
689         return devname;
690     }
691
692     atexit(device_cleanup);     /* unlink the device node on exit */
693     devname = devname_buf;
694
695 #else
696
697     /* check /etc/mtab first, since btrfs subvol info is only in here */
698     devname = find_device("/etc/mtab", st.st_dev);
699     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
700         char parent[256];
701         char *tmp;
702
703         strcpy(parent, path);
704         tmp = strrchr(parent, '/');
705         if (tmp) {
706             *tmp = '\0';
707             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
708             devname = get_devname(parent);
709         } else
710             devname = NULL;
711     }
712     if (!devname) {
713         /* Didn't find it in /etc/mtab, try /proc/mounts */
714         devname = find_device("/proc/mounts", st.st_dev);
715     }
716     if (!devname) {
717         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
718         return devname;
719     }
720
721     fprintf(stderr, "%s is device %s\n", path, devname);
722 #endif
723     return devname;
724 }
725
726 static int open_device(const char *path, struct stat *st, const char **_devname)
727 {
728     int devfd;
729     const char *devname = NULL;
730     struct statfs sfs;
731
732     if (st)
733         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
734                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
735                 return -1;
736         }
737
738     if (statfs(path, &sfs)) {
739         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
740         return -1;
741     }
742     if (sfs.f_type == EXT2_SUPER_MAGIC)
743         fs_type = EXT2;
744     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
745         fs_type = BTRFS;
746     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
747         fs_type = VFAT;
748
749     if (!fs_type) {
750         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
751                 program, path);
752         return -1;
753     }
754
755     devfd = -1;
756     devname = get_devname(path);
757     if (_devname)
758         *_devname = devname;
759
760     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
761         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
762         return -1;
763     }
764
765     /* Verify that the device we opened is the device intended */
766     if (validate_device(path, devfd)) {
767         fprintf(stderr, "%s: path %s doesn't match device %s\n",
768                 program, path, devname);
769         close(devfd);
770         return -1;
771     }
772     return devfd;
773 }
774
775 static int ext_read_adv(const char *path, const char *cfg, int devfd)
776 {
777     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
778         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
779                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
780                 perror("btrfs writing adv");
781                 return 1;
782         }
783         return 0;
784     }
785     return read_adv(path, cfg);
786 }
787
788 static int ext_write_adv(const char *path, const char *cfg, int devfd)
789 {
790     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
791         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
792                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
793                 perror("writing adv");
794                 return 1;
795         }
796         return 0;
797     }
798     return write_adv(path, cfg);
799 }
800
801 int install_loader(const char *path, int update_only)
802 {
803     struct stat st, fst;
804     int devfd, rv;
805     const char *devname;
806
807     devfd = open_device(path, &st, &devname);
808     if (devfd < 0)
809         return 1;
810
811     if (update_only && !already_installed(devfd)) {
812         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
813                 program);
814         close(devfd);
815         return 1;
816     }
817
818     /* Read a pre-existing ADV, if already installed */
819     if (opt.reset_adv)
820         syslinux_reset_adv(syslinux_adv);
821     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
822         close(devfd);
823         return 1;
824     }
825     if (modify_adv() < 0) {
826         close(devfd);
827         return 1;
828     }
829
830     /* Install extlinux.sys */
831     if (install_file(path, devfd, &fst)) {
832         close(devfd);
833         return 1;
834     }
835     if (fst.st_dev != st.st_dev) {
836         fprintf(stderr, "%s: file system changed under us - aborting!\n",
837                 program);
838         close(devfd);
839         return 1;
840     }
841
842     sync();
843     rv = install_bootblock(devfd, devname);
844     close(devfd);
845     sync();
846
847     return rv;
848 }
849
850 /*
851  * Modify the ADV of an existing installation
852  */
853 int modify_existing_adv(const char *path)
854 {
855     int devfd;
856
857     devfd = open_device(path, NULL, NULL);
858     if (devfd < 0)
859         return 1;
860
861     if (opt.reset_adv)
862         syslinux_reset_adv(syslinux_adv);
863     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
864         close(devfd);
865         return 1;
866     }
867     if (modify_adv() < 0) {
868         close(devfd);
869         return 1;
870     }
871     if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
872         close(devfd);
873         return 1;
874     }
875     close(devfd);
876     return 0;
877 }
878
879 int main(int argc, char *argv[])
880 {
881     parse_options(argc, argv, 0);
882
883     if (!opt.directory)
884         usage(EX_USAGE, 0);
885
886     if (opt.update_only == -1) {
887         if (opt.reset_adv || opt.set_once || opt.menu_save)
888             return modify_existing_adv(opt.directory);
889         else
890             usage(EX_USAGE, 0);
891     }
892
893     return install_loader(opt.directory, opt.update_only);
894 }