1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
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.
12 * ----------------------------------------------------------------------- */
17 * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
20 #define _GNU_SOURCE /* Enable everything */
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
38 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/mount.h>
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)
53 #undef SECTOR_SIZE /* Garbage from <linux/msdos_fs.h> */
58 #include "../version.h"
62 # define dprintf printf
64 # define dprintf(...) ((void)0)
67 /* Global option handling */
68 /* Global fs_type for handling fat, ext2/3/4 and btrfs */
69 static enum filesystem {
78 /* These are the options we can set and their values */
87 .sectors = 0,.heads = 0,.raid_mode = 0,.stupid_mode = 0,.reset_adv =
90 static void __attribute__ ((noreturn)) usage(int rv)
93 "Usage: %s [options] directory\n"
94 " --install -i Install over the current bootsector\n"
95 " --update -U Update a previous EXTLINUX installation\n"
96 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
97 " --sectors=# -S Force the number of sectors per track\n"
98 " --heads=# -H Force number of heads\n"
99 " --stupid -s Slow, safe and stupid mode\n"
100 " --raid -r Fall back to the next device on boot failure\n"
101 " --once=... -o Execute a command once upon boot\n"
102 " --clear-once -O Clear the boot-once command\n"
103 " --reset-adv Reset auxilliary data\n"
105 " Note: geometry is determined at boot time for devices which\n"
106 " are considered hard disks by the BIOS. Unfortunately, this is\n"
107 " not possible for devices which are considered floppy disks,\n"
108 " which includes zipdisks and LS-120 superfloppies.\n"
110 " The -z option is useful for USB devices which are considered\n"
111 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
122 static const struct option long_options[] = {
123 {"install", 0, NULL, 'i'},
124 {"update", 0, NULL, 'U'},
125 {"zipdrive", 0, NULL, 'z'},
126 {"sectors", 1, NULL, 'S'},
127 {"stupid", 0, NULL, 's'},
128 {"heads", 1, NULL, 'H'},
129 {"raid-mode", 0, NULL, 'r'},
130 {"version", 0, NULL, 'v'},
131 {"help", 0, NULL, 'h'},
132 {"once", 1, NULL, 'o'},
133 {"clear-once", 0, NULL, 'O'},
134 {"reset-adv", 0, NULL, OPT_RESET_ADV},
138 static const char short_options[] = "iUuzS:H:rvho:O";
140 #if defined(__linux__) && !defined(BLKGETSIZE64)
141 /* This takes a u64, but the size field says size_t. Someone screwed big. */
142 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
145 #ifndef EXT2_SUPER_OFFSET
146 #define EXT2_SUPER_OFFSET 1024
149 /* the btrfs partition first 64K blank area is used to store boot sector and
150 boot image, the boot sector is from 0~512, the boot image starts at 2K */
151 #define BTRFS_EXTLINUX_OFFSET (2*1024)
152 #define BTRFS_SUBVOL_OPT "subvol="
153 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
154 static char subvol[BTRFS_SUBVOL_MAX];
158 extern unsigned char extlinux_bootsect[];
159 extern unsigned int extlinux_bootsect_len;
160 #define boot_block extlinux_bootsect
161 #define boot_block_len extlinux_bootsect_len
166 extern unsigned char extlinux_image[];
167 extern unsigned int extlinux_image_len;
168 #define boot_image extlinux_image
169 #define boot_image_len extlinux_image_len
172 * Common abort function
174 void __attribute__ ((noreturn)) die(const char *msg)
181 * read/write wrapper functions
183 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
185 char *bufp = (char *)buf;
190 rv = pread(fd, bufp, count, offset);
193 } else if (rv == -1) {
194 if (errno == EINTR) {
197 die(strerror(errno));
210 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
212 const char *bufp = (const char *)buf;
217 rv = pwrite(fd, bufp, count, offset);
220 } else if (rv == -1) {
221 if (errno == EINTR) {
224 die(strerror(errno));
238 * Set and clear file attributes
240 static void clear_attributes(int fd)
244 if (!fstat(fd, &st)) {
250 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
251 flags &= ~EXT2_IMMUTABLE_FL;
252 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
258 uint32_t attr = 0x00; /* Clear all attributes */
259 ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
265 fchmod(fd, st.st_mode | S_IWUSR);
269 static void set_attributes(int fd)
273 if (!fstat(fd, &st)) {
274 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
280 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
281 flags |= EXT2_IMMUTABLE_FL;
282 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
288 uint32_t attr = 0x07; /* Hidden+System+Readonly */
289 ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
301 int sectmap(int fd, uint32_t * sectors, int nsectors)
303 unsigned int blksize, blk, nblk;
307 if (ioctl(fd, FIGETBSZ, &blksize))
310 /* Number of sectors per block */
311 blksize >>= SECTOR_SHIFT;
317 dprintf("querying block %u\n", blk);
318 if (ioctl(fd, FIBMAP, &blk))
322 for (i = 0; i < blksize; i++) {
326 dprintf("Sector: %10u\n", blk);
336 * Get the size of a block device
338 uint64_t get_size(int devfd)
345 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
348 if (!ioctl(devfd, BLKGETSIZE, §s))
349 return (uint64_t) sects << 9;
350 else if (!fstat(devfd, &st) && st.st_size)
357 * Get device geometry and partition offset
359 struct geometry_table {
361 struct hd_geometry g;
364 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
365 (x/64/32) is the final fallback. I don't know what LS-240 has
366 as its geometry, since I don't have one and don't know anyone that does,
367 and Google wasn't helpful... */
368 static const struct geometry_table standard_geometries[] = {
369 {360 * 1024, {2, 9, 40, 0}},
370 {720 * 1024, {2, 9, 80, 0}},
371 {1200 * 1024, {2, 15, 80, 0}},
372 {1440 * 1024, {2, 18, 80, 0}},
373 {1680 * 1024, {2, 21, 80, 0}},
374 {1722 * 1024, {2, 21, 80, 0}},
375 {2880 * 1024, {2, 36, 80, 0}},
376 {3840 * 1024, {2, 48, 80, 0}},
377 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
381 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
383 struct floppy_struct fd_str;
384 const struct geometry_table *gp;
386 memset(geo, 0, sizeof *geo);
388 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
390 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
391 geo->heads = fd_str.head;
392 geo->sectors = fd_str.sect;
393 geo->cylinders = fd_str.track;
398 /* Didn't work. Let's see if this is one of the standard geometries */
399 for (gp = standard_geometries; gp->bytes; gp++) {
400 if (gp->bytes == totalbytes) {
401 memcpy(geo, &gp->g, sizeof *geo);
406 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
407 what zipdisks use, so this would help if someone has a USB key that
408 they're booting in USB-ZIP mode. */
410 geo->heads = opt.heads ? : 64;
411 geo->sectors = opt.sectors ? : 32;
412 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
415 if (!opt.sectors && !opt.heads)
417 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
418 " (on hard disks, this is usually harmless.)\n",
419 geo->heads, geo->sectors);
425 * Query the device geometry and put it into the boot sector.
426 * Map the file and put the map in the boot sector and file.
427 * Stick the "current directory" inode number into the file.
429 * Returns the number of modified bytes in the boot file.
431 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
433 struct stat dirst, xdst;
434 struct hd_geometry geo;
436 uint64_t totalbytes, totalsectors;
439 struct boot_sector *bs;
440 struct patch_area *patcharea;
443 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
444 char *dirpath, *subpath;
446 dirpath = realpath(dir, NULL);
447 if (!dirpath || stat(dir, &dirst)) {
448 perror("accessing install directory");
449 exit(255); /* This should never happen */
452 if (lstat(dirpath, &xdst) ||
453 dirst.st_ino != xdst.st_ino ||
454 dirst.st_dev != xdst.st_dev) {
455 perror("realpath returned nonsense");
459 subpath = strchr(dirpath, '\0');
460 while (--subpath >= dirpath) {
461 if (*subpath == '/') {
463 if (lstat(dirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
464 subpath = strchr(subpath+1, '/');
466 subpath = "/"; /* It's the root of the filesystem */
473 /* Now subpath should contain the path relative to the fs base */
474 dprintf("subpath = %s\n", subpath);
476 totalbytes = get_size(devfd);
477 get_geometry(devfd, totalbytes, &geo);
480 geo.heads = opt.heads;
482 geo.sectors = opt.sectors;
484 /* Patch this into a fake FAT superblock. This isn't because
485 FAT is a good format in any way, it's because it lets the
486 early bootstrap share code with the FAT version. */
487 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
489 bs = (struct boot_sector *)boot_block;
491 totalsectors = totalbytes >> SECTOR_SHIFT;
492 if (totalsectors >= 65536) {
493 set_16(&bs->bsSectors, 0);
495 set_16(&bs->bsSectors, totalsectors);
497 set_32(&bs->bsHugeSectors, totalsectors);
499 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
500 set_16(&bs->bsSecPerTrack, geo.sectors);
501 set_16(&bs->bsHeads, geo.heads);
502 set_32(&bs->bsHiddenSecs, geo.start);
504 /* If we're in RAID mode then patch the appropriate instruction;
505 either way write the proper boot signature */
506 i = get_16(&bs->bsSignature);
508 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
510 set_16(&bs->bsSignature, 0xAA55);
512 /* Construct the boot file */
514 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
515 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
516 nsect += 2; /* Two sectors for the ADV */
517 sectp = alloca(sizeof(uint32_t) * nsect);
518 if (fs_type == EXT2 || fs_type == VFAT) {
519 if (sectmap(fd, sectp, nsect)) {
523 } else if (fs_type == BTRFS) {
526 for (i = 0; i < nsect; i++)
527 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
530 /* First sector need pointer in boot sector */
531 set_32(&bs->NextSector, *sectp++);
534 set_16(&bs->MaxTransfer, 1);
536 /* Search for LDLINUX_MAGIC to find the patch area */
537 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
538 patcharea = (struct patch_area *)wp;
540 /* Set up the totals */
541 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
542 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
543 set_16(&patcharea->adv_sectors, 2);
544 set_32(&patcharea->dwords, dw);
546 /* Set the sector pointers */
547 secptroffset = get_16(&patcharea->secptroffset);
548 wp = (uint32_t *) ((char *)boot_image + secptroffset);
549 nptrs = get_16(&patcharea->secptrcnt);
551 memset(wp, 0, nptrs * 4);
553 set_32(wp++, *sectp++);
555 /* Poke in the base directory path */
556 diroffset = get_16(&patcharea->diroffset);
557 dirlen = get_16(&patcharea->dirlen);
558 if (dirlen <= strlen(subpath)) {
559 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
562 strncpy((char *)boot_image + diroffset, subpath, dirlen);
565 /* write subvol info if we have */
566 subvoloffset = get_16(&patcharea->subvoloffset);
567 subvollen = get_16(&patcharea->subvollen);
568 if (subvollen <= strlen(subvol)) {
569 fprintf(stderr, "Subvol name too long... aborting install!\n");
572 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
574 /* Now produce a checksum */
575 set_32(&patcharea->checksum, 0);
577 csum = LDLINUX_MAGIC;
578 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
579 csum -= get_32(wp); /* Negative checksum */
581 set_32(&patcharea->checksum, csum);
584 * Assume all bytes modified. This can be optimized at the expense
585 * of keeping track of what the highest modified address ever was.
591 * Read the ADV from an existing instance, or initialize if invalid.
592 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
595 int read_adv(const char *path, int devfd)
602 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
603 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
604 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
605 perror("writing adv");
610 asprintf(&file, "%s%sextlinux.sys",
611 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
618 fd = open(file, O_RDONLY);
620 if (errno != ENOENT) {
623 syslinux_reset_adv(syslinux_adv);
625 } else if (fstat(fd, &st)) {
627 } else if (st.st_size < 2 * ADV_SIZE) {
628 /* Too small to be useful */
629 syslinux_reset_adv(syslinux_adv);
630 err = 0; /* Nothing to read... */
631 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
632 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
635 /* We got it... maybe? */
636 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
651 * Update the ADV in an existing installation.
653 int write_adv(const char *path, int devfd)
655 unsigned char advtmp[2 * ADV_SIZE];
661 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
662 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
663 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
664 perror("writing adv");
669 asprintf(&file, "%s%sextlinux.sys",
670 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
677 fd = open(file, O_RDONLY);
680 } else if (fstat(fd, &st)) {
682 } else if (st.st_size < 2 * ADV_SIZE) {
683 /* Too small to be useful */
685 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
686 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
689 /* We got it... maybe? */
690 err = syslinux_validate_adv(advtmp) ? -2 : 0;
692 /* Got a good one, write our own ADV here */
693 clear_attributes(fd);
695 /* Need to re-open read-write */
697 fd = open(file, O_RDWR | O_SYNC);
700 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
701 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
702 fprintf(stderr, "%s: race condition on write\n", file);
705 /* Write our own version ... */
706 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
707 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
717 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
731 * Make any user-specified ADV modifications
738 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
739 fprintf(stderr, "%s: not enough space for boot-once command\n",
749 * Install the boot block on the specified device.
750 * Must be run AFTER install_file()!
752 int install_bootblock(int fd, const char *device)
754 struct ext2_super_block sb;
755 struct btrfs_super_block sb2;
756 struct boot_sector sb3;
759 if (fs_type == EXT2) {
760 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
761 perror("reading superblock");
764 if (sb.s_magic == EXT2_SUPER_MAGIC)
766 } else if (fs_type == BTRFS) {
767 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
769 perror("reading superblock");
772 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
774 } else if (fs_type == VFAT) {
775 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
776 perror("reading fat superblock");
779 if (sb3.bsResSectors && sb3.bsFATs &&
780 (strstr(sb3.bs16.FileSysType, "FAT") ||
781 strstr(sb3.bs32.FileSysType, "FAT")))
785 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
789 if (fs_type == VFAT) {
790 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
791 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
792 xpwrite(fd, &bs->bsCode, bsCodeLen,
793 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
794 perror("writing fat bootblock");
798 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
799 perror("writing bootblock");
807 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
810 int fd = -1, dirfd = -1;
813 asprintf(&file, "%s%sextlinux.sys",
814 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
820 dirfd = open(path, O_RDONLY | O_DIRECTORY);
826 fd = open(file, O_RDONLY);
828 if (errno != ENOENT) {
833 clear_attributes(fd);
837 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
838 S_IRUSR | S_IRGRP | S_IROTH);
844 /* Write it the first time */
845 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
846 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
847 boot_image_len) != 2 * ADV_SIZE) {
848 fprintf(stderr, "%s: write failure on %s\n", program, file);
852 /* Map the file, and patch the initial sector accordingly */
853 modbytes = patch_file_and_bootblock(fd, path, devfd);
855 /* Write the patch area again - this relies on the file being
856 overwritten in place! */
857 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
858 fprintf(stderr, "%s: write failure on %s\n", program, file);
862 /* Attempt to set immutable flag and remove all write access */
863 /* Only set immutable flag if file is owned by root */
866 if (fstat(fd, rst)) {
884 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
885 is not managered by btrfs tree, so actually this is not installed as files.
886 since the cow feature of btrfs will move the extlinux.sys every where */
887 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
889 patch_file_and_bootblock(-1, path, devfd);
890 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
892 perror("writing bootblock");
895 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
896 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
897 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
898 perror("writing adv");
901 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
902 if (stat(path, rst)) {
909 int install_file(const char *path, int devfd, struct stat *rst)
911 if (fs_type == EXT2 || fs_type == VFAT)
912 return ext2_fat_install_file(path, devfd, rst);
913 else if (fs_type == BTRFS)
914 return btrfs_install_file(path, devfd, rst);
918 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
919 sector; this is consistent with FAT filesystems. */
920 int already_installed(int devfd)
924 xpread(devfd, buffer, 8, 3);
925 return !memcmp(buffer, "EXTLINUX", 8);
929 static char devname_buf[64];
931 static void device_cleanup(void)
937 /* Verify that a device fd and a pathname agree.
938 Return 0 on valid, -1 on error. */
939 static int validate_device(const char *path, int devfd)
941 struct stat pst, dst;
944 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
946 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
947 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
949 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
953 static const char *find_device(const char *mtab_file, dev_t dev)
958 const char *devname = NULL;
961 mtab = setmntent(mtab_file, "r");
966 while ((mnt = getmntent(mtab))) {
967 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
970 if (!strcmp(mnt->mnt_type, "btrfs") &&
971 !stat(mnt->mnt_dir, &dst) &&
973 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
979 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
980 tmp = strchr(subvol, 32);
984 break; /* should break and let upper layer try again */
990 if ((!strcmp(mnt->mnt_type, "ext2") ||
991 !strcmp(mnt->mnt_type, "ext3") ||
992 !strcmp(mnt->mnt_type, "ext4")) &&
993 !stat(mnt->mnt_fsname, &dst) &&
994 dst.st_rdev == dev) {
999 if ((!strcmp(mnt->mnt_type, "vfat")) &&
1000 !stat(mnt->mnt_fsname, &dst) &&
1001 dst.st_rdev == dev) {
1007 devname = strdup(mnt->mnt_fsname);
1017 static const char *get_devname(const char *path)
1019 const char *devname = NULL;
1023 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
1024 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1027 if (statfs(path, &sfs)) {
1028 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1033 /* klibc doesn't have getmntent and friends; instead, just create
1034 a new device with the appropriate device type */
1035 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1036 major(st.st_dev), minor(st.st_dev));
1038 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1039 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1043 atexit(device_cleanup); /* unlink the device node on exit */
1044 devname = devname_buf;
1048 /* check /etc/mtab first, since btrfs subvol info is only in here */
1049 devname = find_device("/etc/mtab", st.st_dev);
1050 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1054 strcpy(parent, path);
1055 tmp = strrchr(parent, '/');
1058 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1059 devname = get_devname(parent);
1064 /* Didn't find it in /etc/mtab, try /proc/mounts */
1065 devname = find_device("/proc/mounts", st.st_dev);
1068 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1072 fprintf(stderr, "%s is device %s\n", path, devname);
1077 static int open_device(const char *path, struct stat *st, const char **_devname)
1080 const char *devname = NULL;
1084 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1085 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1089 if (statfs(path, &sfs)) {
1090 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1093 if (sfs.f_type == EXT2_SUPER_MAGIC)
1095 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1097 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1101 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1107 devname = get_devname(path);
1109 *_devname = devname;
1111 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1112 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1116 /* Verify that the device we opened is the device intended */
1117 if (validate_device(path, devfd)) {
1118 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1119 program, path, devname);
1126 int install_loader(const char *path, int update_only)
1128 struct stat st, fst;
1130 const char *devname;
1132 devfd = open_device(path, &st, &devname);
1136 if (update_only && !already_installed(devfd)) {
1137 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1143 /* Read a pre-existing ADV, if already installed */
1145 syslinux_reset_adv(syslinux_adv);
1146 else if (read_adv(path, devfd) < 0) {
1150 if (modify_adv() < 0) {
1155 /* Install extlinux.sys */
1156 if (install_file(path, devfd, &fst)) {
1160 if (fst.st_dev != st.st_dev) {
1161 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1168 rv = install_bootblock(devfd, devname);
1176 * Modify the ADV of an existing installation
1178 int modify_existing_adv(const char *path)
1182 devfd = open_device(path, NULL, NULL);
1187 syslinux_reset_adv(syslinux_adv);
1188 else if (read_adv(path, devfd) < 0) {
1192 if (modify_adv() < 0) {
1196 if (write_adv(path, devfd) < 0) {
1204 int main(int argc, char *argv[])
1207 const char *directory;
1208 int update_only = -1;
1212 while ((o = getopt_long(argc, argv, short_options,
1213 long_options, NULL)) != EOF) {
1220 opt.sectors = strtoul(optarg, NULL, 0);
1221 if (opt.sectors < 1 || opt.sectors > 63) {
1223 "%s: invalid number of sectors: %u (must be 1-63)\n",
1224 program, opt.sectors);
1229 opt.heads = strtoul(optarg, NULL, 0);
1230 if (opt.heads < 1 || opt.heads > 256) {
1232 "%s: invalid number of heads: %u (must be 1-256)\n",
1233 program, opt.heads);
1241 opt.stupid_mode = 1;
1254 opt.set_once = optarg;
1263 fputs("extlinux " VERSION_STR
1264 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1271 directory = argv[optind];
1276 if (update_only == -1) {
1277 if (opt.reset_adv || opt.set_once) {
1278 return modify_existing_adv(directory);
1284 return install_loader(directory, update_only);