Merge branch 'pathbased' of ssh://terminus.zytor.com/pub/git/syslinux/syslinux into...
[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                              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             printf("EXTENT: %11lu / %5u\n", lba, len);
226             ex++;
227         }
228
229         base = addr;
230         lba  = sect;
231         len  = 1;
232
233     next:
234         addr += SECTOR_SIZE;
235         nsect--;
236     }
237
238     if (len) {
239         set_64(&ex->lba, lba);
240         set_16(&ex->len, len);
241         printf("EXTENT: %11lu / %5u\n", lba, len);
242         ex++;
243     }
244 }
245
246 /*
247  * Query the device geometry and put it into the boot sector.
248  * Map the file and put the map in the boot sector and file.
249  * Stick the "current directory" inode number into the file.
250  *
251  * Returns the number of modified bytes in the boot file.
252  */
253 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
254 {
255     struct stat dirst, xdst;
256     struct hd_geometry geo;
257     sector_t *sectp;
258     uint64_t totalbytes, totalsectors;
259     int nsect;
260     uint32_t *wp;
261     struct boot_sector *bs;
262     struct patch_area *patcharea;
263     struct syslinux_extent *ex;
264     int i, dw, nptrs;
265     uint32_t csum;
266     int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
267     char *dirpath, *subpath, *xdirpath, *xsubpath;
268     uint64_t *advptrs;
269
270     dirpath = realpath(dir, NULL);
271     if (!dirpath || stat(dir, &dirst)) {
272         perror("accessing install directory");
273         exit(255);              /* This should never happen */
274     }
275
276     if (lstat(dirpath, &xdst) ||
277         dirst.st_ino != xdst.st_ino ||
278         dirst.st_dev != xdst.st_dev) {
279         perror("realpath returned nonsense");
280         exit(255);
281     }
282
283     subpath = strchr(dirpath, '\0');
284     for (;;) {
285         if (*subpath == '/') {
286             if (subpath > dirpath) {
287                 *subpath = '\0';
288                 xsubpath = subpath+1;
289                 xdirpath = dirpath;
290             } else {
291                 xsubpath = subpath;
292                 xdirpath = "/";
293             }
294             if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
295                 subpath = strchr(subpath+1, '/');
296                 if (!subpath)
297                     subpath = "/"; /* It's the root of the filesystem */
298                 break;
299             }
300             *subpath = '/';
301         }
302
303         if (subpath == dirpath)
304             break;
305
306         subpath--;
307     }
308
309     /* Now subpath should contain the path relative to the fs base */
310     dprintf("subpath = %s\n", subpath);
311
312     totalbytes = get_size(devfd);
313     get_geometry(devfd, totalbytes, &geo);
314
315     if (opt.heads)
316         geo.heads = opt.heads;
317     if (opt.sectors)
318         geo.sectors = opt.sectors;
319
320     /* Patch this into a fake FAT superblock.  This isn't because
321        FAT is a good format in any way, it's because it lets the
322        early bootstrap share code with the FAT version. */
323     dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
324
325     bs = (struct boot_sector *)boot_block;
326
327     totalsectors = totalbytes >> SECTOR_SHIFT;
328     if (totalsectors >= 65536) {
329         set_16(&bs->bsSectors, 0);
330     } else {
331         set_16(&bs->bsSectors, totalsectors);
332     }
333     set_32(&bs->bsHugeSectors, totalsectors);
334
335     set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
336     set_16(&bs->bsSecPerTrack, geo.sectors);
337     set_16(&bs->bsHeads, geo.heads);
338     set_32(&bs->bsHiddenSecs, geo.start);
339
340     /* If we're in RAID mode then patch the appropriate instruction;
341        either way write the proper boot signature */
342     i = get_16(&bs->bsSignature);
343     if (opt.raid_mode)
344         set_16((uint16_t *) (boot_block + i), 0x18CD);  /* INT 18h */
345
346     set_16(&bs->bsSignature, 0xAA55);
347
348     /* Construct the boot file */
349
350     dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
351     nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
352     nsect += 2;                 /* Two sectors for the ADV */
353     sectp = alloca(sizeof(sector_t) * nsect);
354     if (fs_type == EXT2 || fs_type == VFAT) {
355         if (sectmap(fd, sectp, nsect)) {
356                 perror("bmap");
357                 exit(1);
358         }
359     } else if (fs_type == BTRFS) {
360         int i;
361
362         for (i = 0; i < nsect; i++)
363                 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
364     }
365
366     /* First sector need pointer in boot sector */
367     set_64(&bs->NextSector, *sectp++);
368
369     /* Search for LDLINUX_MAGIC to find the patch area */
370     for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
371     patcharea = (struct patch_area *)wp;
372
373     /* Set up the totals */
374     dw = boot_image_len >> 2;   /* COMPLETE dwords, excluding ADV */
375     set_16(&patcharea->data_sectors, nsect - 2);        /* -2 for the ADVs */
376     set_16(&patcharea->adv_sectors, 2);
377     set_32(&patcharea->dwords, dw);
378
379     /* Stupid mode? */
380     if (opt.stupid_mode)
381         set_16(&patcharea->maxtransfer, 1);
382
383     /* Set the sector extents */
384     secptroffset = get_16(&patcharea->secptroffset);
385     ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
386     nptrs = get_16(&patcharea->secptrcnt);
387
388     if (nsect > nptrs) {
389         /* Not necessarily an error in this case, but a general problem */
390         fprintf(stderr, "Insufficient extent space, build error!\n");
391         exit(1);
392     }
393
394     /* -1 for the pointer in the boot sector, -2 for the two ADVs */
395     generate_extents(ex, nptrs, sectp, nsect-1-2);
396
397     /* ADV pointers */
398     advptrs = (uint64_t *)((char *)boot_image +
399                            get_16(&patcharea->advptroffset));
400     set_64(&advptrs[0], sectp[nsect-1-2]);
401     set_64(&advptrs[1], sectp[nsect-1-1]);
402
403     /* Poke in the base directory path */
404     diroffset = get_16(&patcharea->diroffset);
405     dirlen = get_16(&patcharea->dirlen);
406     if (dirlen <= strlen(subpath)) {
407         fprintf(stderr, "Subdirectory path too long... aborting install!\n");
408         exit(1);
409     }
410     strncpy((char *)boot_image + diroffset, subpath, dirlen);
411     free(dirpath);
412
413     /* write subvol info if we have */
414     subvoloffset = get_16(&patcharea->subvoloffset);
415     subvollen = get_16(&patcharea->subvollen);
416     if (subvollen <= strlen(subvol)) {
417         fprintf(stderr, "Subvol name too long... aborting install!\n");
418         exit(1);
419     }
420     strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
421
422     /* Now produce a checksum */
423     set_32(&patcharea->checksum, 0);
424
425     csum = LDLINUX_MAGIC;
426     for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
427         csum -= get_32(wp);     /* Negative checksum */
428
429     set_32(&patcharea->checksum, csum);
430
431     /*
432      * Assume all bytes modified.  This can be optimized at the expense
433      * of keeping track of what the highest modified address ever was.
434      */
435     return dw << 2;
436 }
437
438 /*
439  * Make any user-specified ADV modifications
440  */
441 int modify_adv(void)
442 {
443     int rv = 0;
444
445     if (opt.set_once) {
446         if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
447             fprintf(stderr, "%s: not enough space for boot-once command\n",
448                     program);
449             rv = -1;
450         }
451     }
452     if (opt.menu_save) {
453         if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
454             fprintf(stderr, "%s: not enough space for menu-save label\n",
455                     program);
456             rv = -1;
457         }
458     }
459
460     return rv;
461 }
462
463 /*
464  * Install the boot block on the specified device.
465  * Must be run AFTER install_file()!
466  */
467 int install_bootblock(int fd, const char *device)
468 {
469     struct ext2_super_block sb;
470     struct btrfs_super_block sb2;
471     struct boot_sector sb3;
472     bool ok = false;
473
474     if (fs_type == EXT2) {
475         if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
476                 perror("reading superblock");
477                 return 1;
478         }
479         if (sb.s_magic == EXT2_SUPER_MAGIC)
480                 ok = true;
481     } else if (fs_type == BTRFS) {
482         if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
483                         != sizeof sb2) {
484                 perror("reading superblock");
485                 return 1;
486         }
487         if (sb2.magic == *(u64 *)BTRFS_MAGIC)
488                 ok = true;
489     } else if (fs_type == VFAT) {
490         if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
491                 perror("reading fat superblock");
492                 return 1;
493         }
494         if (sb3.bsResSectors && sb3.bsFATs &&
495             (strstr(sb3.bs16.FileSysType, "FAT") ||
496              strstr(sb3.bs32.FileSysType, "FAT")))
497                 ok = true;
498     }
499     if (!ok) {
500         fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
501                         device);
502         return 1;
503     }
504     if (fs_type == VFAT) {
505         struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
506         if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
507             xpwrite(fd, &bs->bsCode, bsCodeLen,
508                     offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
509             perror("writing fat bootblock");
510             return 1;
511         }
512     } else {
513         if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
514             perror("writing bootblock");
515             return 1;
516         }
517     }
518
519     return 0;
520 }
521
522 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
523 {
524     char *file;
525     int fd = -1, dirfd = -1;
526     int modbytes;
527
528     asprintf(&file, "%s%sextlinux.sys",
529              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
530     if (!file) {
531         perror(program);
532         return 1;
533     }
534
535     dirfd = open(path, O_RDONLY | O_DIRECTORY);
536     if (dirfd < 0) {
537         perror(path);
538         goto bail;
539     }
540
541     fd = open(file, O_RDONLY);
542     if (fd < 0) {
543         if (errno != ENOENT) {
544             perror(file);
545             goto bail;
546         }
547     } else {
548         clear_attributes(fd);
549     }
550     close(fd);
551
552     fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
553               S_IRUSR | S_IRGRP | S_IROTH);
554     if (fd < 0) {
555         perror(file);
556         goto bail;
557     }
558
559     /* Write it the first time */
560     if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
561         xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
562                 boot_image_len) != 2 * ADV_SIZE) {
563         fprintf(stderr, "%s: write failure on %s\n", program, file);
564         goto bail;
565     }
566
567     /* Map the file, and patch the initial sector accordingly */
568     modbytes = patch_file_and_bootblock(fd, path, devfd);
569
570     /* Write the patch area again - this relies on the file being
571        overwritten in place! */
572     if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
573         fprintf(stderr, "%s: write failure on %s\n", program, file);
574         goto bail;
575     }
576
577     /* Attempt to set immutable flag and remove all write access */
578     /* Only set immutable flag if file is owned by root */
579     set_attributes(fd);
580
581     if (fstat(fd, rst)) {
582         perror(file);
583         goto bail;
584     }
585
586     close(dirfd);
587     close(fd);
588     return 0;
589
590 bail:
591     if (dirfd >= 0)
592         close(dirfd);
593     if (fd >= 0)
594         close(fd);
595
596     return 1;
597 }
598
599 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
600    is not managered by btrfs tree, so actually this is not installed as files.
601    since the cow feature of btrfs will move the extlinux.sys every where */
602 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
603 {
604     patch_file_and_bootblock(-1, path, devfd);
605     if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
606                 != boot_image_len) {
607         perror("writing bootblock");
608         return 1;
609     }
610     printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
611     if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
612                 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
613         perror("writing adv");
614         return 1;
615     }
616     printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
617     if (stat(path, rst)) {
618         perror(path);
619         return 1;
620     }
621     return 0;
622 }
623
624 int install_file(const char *path, int devfd, struct stat *rst)
625 {
626         if (fs_type == EXT2 || fs_type == VFAT)
627                 return ext2_fat_install_file(path, devfd, rst);
628         else if (fs_type == BTRFS)
629                 return btrfs_install_file(path, devfd, rst);
630         return 1;
631 }
632
633 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
634    sector; this is consistent with FAT filesystems. */
635 int already_installed(int devfd)
636 {
637     char buffer[8];
638
639     xpread(devfd, buffer, 8, 3);
640     return !memcmp(buffer, "EXTLINUX", 8);
641 }
642
643 #ifdef __KLIBC__
644 static char devname_buf[64];
645
646 static void device_cleanup(void)
647 {
648     unlink(devname_buf);
649 }
650 #endif
651
652 /* Verify that a device fd and a pathname agree.
653    Return 0 on valid, -1 on error. */
654 static int validate_device(const char *path, int devfd)
655 {
656     struct stat pst, dst;
657     struct statfs sfs;
658
659     if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
660         return -1;
661     /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
662     if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
663         return 0;
664     return (pst.st_dev == dst.st_rdev) ? 0 : -1;
665 }
666
667 #ifndef __KLIBC__
668 static const char *find_device(const char *mtab_file, dev_t dev)
669 {
670     struct mntent *mnt;
671     struct stat dst;
672     FILE *mtab;
673     const char *devname = NULL;
674     bool done;
675
676     mtab = setmntent(mtab_file, "r");
677     if (!mtab)
678         return NULL;
679
680     done = false;
681     while ((mnt = getmntent(mtab))) {
682         /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
683         switch (fs_type) {
684         case BTRFS:
685                 if (!strcmp(mnt->mnt_type, "btrfs") &&
686                     !stat(mnt->mnt_dir, &dst) &&
687                     dst.st_dev == dev) {
688                     char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
689
690                     if (opt) {
691                         if (!subvol[0]) {
692                             char *tmp;
693
694                             strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
695                             tmp = strchr(subvol, 32);
696                             if (tmp)
697                                 *tmp = '\0';
698                         }
699                         break; /* should break and let upper layer try again */
700                     } else
701                         done = true;
702                 }
703                 break;
704         case EXT2:
705                 if ((!strcmp(mnt->mnt_type, "ext2") ||
706                      !strcmp(mnt->mnt_type, "ext3") ||
707                      !strcmp(mnt->mnt_type, "ext4")) &&
708                     !stat(mnt->mnt_fsname, &dst) &&
709                     dst.st_rdev == dev) {
710                     done = true;
711                     break;
712                 }
713         case VFAT:
714                 if ((!strcmp(mnt->mnt_type, "vfat")) &&
715                     !stat(mnt->mnt_fsname, &dst) &&
716                     dst.st_rdev == dev) {
717                     done = true;
718                     break;
719                 }
720         case NONE:
721             break;
722         }
723         if (done) {
724                 devname = strdup(mnt->mnt_fsname);
725                 break;
726         }
727     }
728     endmntent(mtab);
729
730     return devname;
731 }
732 #endif
733
734 static const char *get_devname(const char *path)
735 {
736     const char *devname = NULL;
737     struct stat st;
738     struct statfs sfs;
739
740     if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
741         fprintf(stderr, "%s: Not a directory: %s\n", program, path);
742         return devname;
743     }
744     if (statfs(path, &sfs)) {
745         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
746         return devname;
747     }
748 #ifdef __KLIBC__
749
750     /* klibc doesn't have getmntent and friends; instead, just create
751        a new device with the appropriate device type */
752     snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
753              major(st.st_dev), minor(st.st_dev));
754
755     if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
756         fprintf(stderr, "%s: cannot create device %s\n", program, devname);
757         return devname;
758     }
759
760     atexit(device_cleanup);     /* unlink the device node on exit */
761     devname = devname_buf;
762
763 #else
764
765     /* check /etc/mtab first, since btrfs subvol info is only in here */
766     devname = find_device("/etc/mtab", st.st_dev);
767     if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
768         char parent[256];
769         char *tmp;
770
771         strcpy(parent, path);
772         tmp = strrchr(parent, '/');
773         if (tmp) {
774             *tmp = '\0';
775             fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
776             devname = get_devname(parent);
777         } else
778             devname = NULL;
779     }
780     if (!devname) {
781         /* Didn't find it in /etc/mtab, try /proc/mounts */
782         devname = find_device("/proc/mounts", st.st_dev);
783     }
784     if (!devname) {
785         fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
786         return devname;
787     }
788
789     fprintf(stderr, "%s is device %s\n", path, devname);
790 #endif
791     return devname;
792 }
793
794 static int open_device(const char *path, struct stat *st, const char **_devname)
795 {
796     int devfd;
797     const char *devname = NULL;
798     struct statfs sfs;
799
800     if (st)
801         if (stat(path, st) || !S_ISDIR(st->st_mode)) {
802                 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
803                 return -1;
804         }
805
806     if (statfs(path, &sfs)) {
807         fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
808         return -1;
809     }
810     if (sfs.f_type == EXT2_SUPER_MAGIC)
811         fs_type = EXT2;
812     else if (sfs.f_type == BTRFS_SUPER_MAGIC)
813         fs_type = BTRFS;
814     else if (sfs.f_type == MSDOS_SUPER_MAGIC)
815         fs_type = VFAT;
816
817     if (!fs_type) {
818         fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
819                 program, path);
820         return -1;
821     }
822
823     devfd = -1;
824     devname = get_devname(path);
825     if (_devname)
826         *_devname = devname;
827
828     if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
829         fprintf(stderr, "%s: cannot open device %s\n", program, devname);
830         return -1;
831     }
832
833     /* Verify that the device we opened is the device intended */
834     if (validate_device(path, devfd)) {
835         fprintf(stderr, "%s: path %s doesn't match device %s\n",
836                 program, path, devname);
837         close(devfd);
838         return -1;
839     }
840     return devfd;
841 }
842
843 static int ext_read_adv(const char *path, const char *cfg, int devfd)
844 {
845     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
846         if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
847                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
848                 perror("btrfs writing adv");
849                 return 1;
850         }
851         return 0;
852     }
853     return read_adv(path, cfg);
854 }
855
856 static int ext_write_adv(const char *path, const char *cfg, int devfd)
857 {
858     if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
859         if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
860                 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
861                 perror("writing adv");
862                 return 1;
863         }
864         return 0;
865     }
866     return write_adv(path, cfg);
867 }
868
869 int install_loader(const char *path, int update_only)
870 {
871     struct stat st, fst;
872     int devfd, rv;
873     const char *devname;
874
875     devfd = open_device(path, &st, &devname);
876     if (devfd < 0)
877         return 1;
878
879     if (update_only && !already_installed(devfd)) {
880         fprintf(stderr, "%s: no previous extlinux boot sector found\n",
881                 program);
882         close(devfd);
883         return 1;
884     }
885
886     /* Read a pre-existing ADV, if already installed */
887     if (opt.reset_adv)
888         syslinux_reset_adv(syslinux_adv);
889     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
890         close(devfd);
891         return 1;
892     }
893     if (modify_adv() < 0) {
894         close(devfd);
895         return 1;
896     }
897
898     /* Install extlinux.sys */
899     if (install_file(path, devfd, &fst)) {
900         close(devfd);
901         return 1;
902     }
903     if (fst.st_dev != st.st_dev) {
904         fprintf(stderr, "%s: file system changed under us - aborting!\n",
905                 program);
906         close(devfd);
907         return 1;
908     }
909
910     sync();
911     rv = install_bootblock(devfd, devname);
912     close(devfd);
913     sync();
914
915     return rv;
916 }
917
918 /*
919  * Modify the ADV of an existing installation
920  */
921 int modify_existing_adv(const char *path)
922 {
923     int devfd;
924
925     devfd = open_device(path, NULL, NULL);
926     if (devfd < 0)
927         return 1;
928
929     if (opt.reset_adv)
930         syslinux_reset_adv(syslinux_adv);
931     else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
932         close(devfd);
933         return 1;
934     }
935     if (modify_adv() < 0) {
936         close(devfd);
937         return 1;
938     }
939     if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
940         close(devfd);
941         return 1;
942     }
943     close(devfd);
944     return 0;
945 }
946
947 int main(int argc, char *argv[])
948 {
949     parse_options(argc, argv, MODE_EXTLINUX);
950
951     if (!opt.directory)
952         usage(EX_USAGE, 0);
953
954     if (opt.update_only == -1) {
955         if (opt.reset_adv || opt.set_once || opt.menu_save)
956             return modify_existing_adv(opt.directory);
957         else
958             usage(EX_USAGE, MODE_EXTLINUX);
959     }
960
961     return install_loader(opt.directory, opt.update_only);
962 }