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, *xdirpath, *xsubpath;
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');
461 if (*subpath == '/') {
462 if (subpath > dirpath) {
464 xsubpath = subpath+1;
470 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
471 subpath = strchr(subpath+1, '/');
473 subpath = "/"; /* It's the root of the filesystem */
479 if (subpath == dirpath)
485 /* Now subpath should contain the path relative to the fs base */
486 dprintf("subpath = %s\n", subpath);
488 totalbytes = get_size(devfd);
489 get_geometry(devfd, totalbytes, &geo);
492 geo.heads = opt.heads;
494 geo.sectors = opt.sectors;
496 /* Patch this into a fake FAT superblock. This isn't because
497 FAT is a good format in any way, it's because it lets the
498 early bootstrap share code with the FAT version. */
499 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
501 bs = (struct boot_sector *)boot_block;
503 totalsectors = totalbytes >> SECTOR_SHIFT;
504 if (totalsectors >= 65536) {
505 set_16(&bs->bsSectors, 0);
507 set_16(&bs->bsSectors, totalsectors);
509 set_32(&bs->bsHugeSectors, totalsectors);
511 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
512 set_16(&bs->bsSecPerTrack, geo.sectors);
513 set_16(&bs->bsHeads, geo.heads);
514 set_32(&bs->bsHiddenSecs, geo.start);
516 /* If we're in RAID mode then patch the appropriate instruction;
517 either way write the proper boot signature */
518 i = get_16(&bs->bsSignature);
520 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
522 set_16(&bs->bsSignature, 0xAA55);
524 /* Construct the boot file */
526 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
527 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
528 nsect += 2; /* Two sectors for the ADV */
529 sectp = alloca(sizeof(uint32_t) * nsect);
530 if (fs_type == EXT2 || fs_type == VFAT) {
531 if (sectmap(fd, sectp, nsect)) {
535 } else if (fs_type == BTRFS) {
538 for (i = 0; i < nsect; i++)
539 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
542 /* First sector need pointer in boot sector */
543 set_32(&bs->NextSector, *sectp++);
546 set_16(&bs->MaxTransfer, 1);
548 /* Search for LDLINUX_MAGIC to find the patch area */
549 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
550 patcharea = (struct patch_area *)wp;
552 /* Set up the totals */
553 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
554 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
555 set_16(&patcharea->adv_sectors, 2);
556 set_32(&patcharea->dwords, dw);
558 /* Set the sector pointers */
559 secptroffset = get_16(&patcharea->secptroffset);
560 wp = (uint32_t *) ((char *)boot_image + secptroffset);
561 nptrs = get_16(&patcharea->secptrcnt);
563 memset(wp, 0, nptrs * 4);
565 set_32(wp++, *sectp++);
567 /* Poke in the base directory path */
568 diroffset = get_16(&patcharea->diroffset);
569 dirlen = get_16(&patcharea->dirlen);
570 if (dirlen <= strlen(subpath)) {
571 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
574 strncpy((char *)boot_image + diroffset, subpath, dirlen);
577 /* write subvol info if we have */
578 subvoloffset = get_16(&patcharea->subvoloffset);
579 subvollen = get_16(&patcharea->subvollen);
580 if (subvollen <= strlen(subvol)) {
581 fprintf(stderr, "Subvol name too long... aborting install!\n");
584 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
586 /* Now produce a checksum */
587 set_32(&patcharea->checksum, 0);
589 csum = LDLINUX_MAGIC;
590 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
591 csum -= get_32(wp); /* Negative checksum */
593 set_32(&patcharea->checksum, csum);
596 * Assume all bytes modified. This can be optimized at the expense
597 * of keeping track of what the highest modified address ever was.
603 * Read the ADV from an existing instance, or initialize if invalid.
604 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
607 int read_adv(const char *path, int devfd)
614 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
615 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
616 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
617 perror("writing adv");
622 asprintf(&file, "%s%sextlinux.sys",
623 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
630 fd = open(file, O_RDONLY);
632 if (errno != ENOENT) {
635 syslinux_reset_adv(syslinux_adv);
637 } else if (fstat(fd, &st)) {
639 } else if (st.st_size < 2 * ADV_SIZE) {
640 /* Too small to be useful */
641 syslinux_reset_adv(syslinux_adv);
642 err = 0; /* Nothing to read... */
643 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
644 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
647 /* We got it... maybe? */
648 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
663 * Update the ADV in an existing installation.
665 int write_adv(const char *path, int devfd)
667 unsigned char advtmp[2 * ADV_SIZE];
673 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
674 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
675 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
676 perror("writing adv");
681 asprintf(&file, "%s%sextlinux.sys",
682 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
689 fd = open(file, O_RDONLY);
692 } else if (fstat(fd, &st)) {
694 } else if (st.st_size < 2 * ADV_SIZE) {
695 /* Too small to be useful */
697 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
698 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
701 /* We got it... maybe? */
702 err = syslinux_validate_adv(advtmp) ? -2 : 0;
704 /* Got a good one, write our own ADV here */
705 clear_attributes(fd);
707 /* Need to re-open read-write */
709 fd = open(file, O_RDWR | O_SYNC);
712 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
713 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
714 fprintf(stderr, "%s: race condition on write\n", file);
717 /* Write our own version ... */
718 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
719 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
729 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
743 * Make any user-specified ADV modifications
750 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
751 fprintf(stderr, "%s: not enough space for boot-once command\n",
761 * Install the boot block on the specified device.
762 * Must be run AFTER install_file()!
764 int install_bootblock(int fd, const char *device)
766 struct ext2_super_block sb;
767 struct btrfs_super_block sb2;
768 struct boot_sector sb3;
771 if (fs_type == EXT2) {
772 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
773 perror("reading superblock");
776 if (sb.s_magic == EXT2_SUPER_MAGIC)
778 } else if (fs_type == BTRFS) {
779 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
781 perror("reading superblock");
784 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
786 } else if (fs_type == VFAT) {
787 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
788 perror("reading fat superblock");
791 if (sb3.bsResSectors && sb3.bsFATs &&
792 (strstr(sb3.bs16.FileSysType, "FAT") ||
793 strstr(sb3.bs32.FileSysType, "FAT")))
797 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
801 if (fs_type == VFAT) {
802 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
803 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
804 xpwrite(fd, &bs->bsCode, bsCodeLen,
805 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
806 perror("writing fat bootblock");
810 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
811 perror("writing bootblock");
819 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
822 int fd = -1, dirfd = -1;
825 asprintf(&file, "%s%sextlinux.sys",
826 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
832 dirfd = open(path, O_RDONLY | O_DIRECTORY);
838 fd = open(file, O_RDONLY);
840 if (errno != ENOENT) {
845 clear_attributes(fd);
849 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
850 S_IRUSR | S_IRGRP | S_IROTH);
856 /* Write it the first time */
857 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
858 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
859 boot_image_len) != 2 * ADV_SIZE) {
860 fprintf(stderr, "%s: write failure on %s\n", program, file);
864 /* Map the file, and patch the initial sector accordingly */
865 modbytes = patch_file_and_bootblock(fd, path, devfd);
867 /* Write the patch area again - this relies on the file being
868 overwritten in place! */
869 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
870 fprintf(stderr, "%s: write failure on %s\n", program, file);
874 /* Attempt to set immutable flag and remove all write access */
875 /* Only set immutable flag if file is owned by root */
878 if (fstat(fd, rst)) {
896 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
897 is not managered by btrfs tree, so actually this is not installed as files.
898 since the cow feature of btrfs will move the extlinux.sys every where */
899 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
901 patch_file_and_bootblock(-1, path, devfd);
902 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
904 perror("writing bootblock");
907 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
908 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
909 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
910 perror("writing adv");
913 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
914 if (stat(path, rst)) {
921 int install_file(const char *path, int devfd, struct stat *rst)
923 if (fs_type == EXT2 || fs_type == VFAT)
924 return ext2_fat_install_file(path, devfd, rst);
925 else if (fs_type == BTRFS)
926 return btrfs_install_file(path, devfd, rst);
930 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
931 sector; this is consistent with FAT filesystems. */
932 int already_installed(int devfd)
936 xpread(devfd, buffer, 8, 3);
937 return !memcmp(buffer, "EXTLINUX", 8);
941 static char devname_buf[64];
943 static void device_cleanup(void)
949 /* Verify that a device fd and a pathname agree.
950 Return 0 on valid, -1 on error. */
951 static int validate_device(const char *path, int devfd)
953 struct stat pst, dst;
956 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
958 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
959 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
961 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
965 static const char *find_device(const char *mtab_file, dev_t dev)
970 const char *devname = NULL;
973 mtab = setmntent(mtab_file, "r");
978 while ((mnt = getmntent(mtab))) {
979 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
982 if (!strcmp(mnt->mnt_type, "btrfs") &&
983 !stat(mnt->mnt_dir, &dst) &&
985 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
991 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
992 tmp = strchr(subvol, 32);
996 break; /* should break and let upper layer try again */
1002 if ((!strcmp(mnt->mnt_type, "ext2") ||
1003 !strcmp(mnt->mnt_type, "ext3") ||
1004 !strcmp(mnt->mnt_type, "ext4")) &&
1005 !stat(mnt->mnt_fsname, &dst) &&
1006 dst.st_rdev == dev) {
1011 if ((!strcmp(mnt->mnt_type, "vfat")) &&
1012 !stat(mnt->mnt_fsname, &dst) &&
1013 dst.st_rdev == dev) {
1019 devname = strdup(mnt->mnt_fsname);
1029 static const char *get_devname(const char *path)
1031 const char *devname = NULL;
1035 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
1036 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1039 if (statfs(path, &sfs)) {
1040 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1045 /* klibc doesn't have getmntent and friends; instead, just create
1046 a new device with the appropriate device type */
1047 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
1048 major(st.st_dev), minor(st.st_dev));
1050 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
1051 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1055 atexit(device_cleanup); /* unlink the device node on exit */
1056 devname = devname_buf;
1060 /* check /etc/mtab first, since btrfs subvol info is only in here */
1061 devname = find_device("/etc/mtab", st.st_dev);
1062 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1066 strcpy(parent, path);
1067 tmp = strrchr(parent, '/');
1070 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1071 devname = get_devname(parent);
1076 /* Didn't find it in /etc/mtab, try /proc/mounts */
1077 devname = find_device("/proc/mounts", st.st_dev);
1080 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1084 fprintf(stderr, "%s is device %s\n", path, devname);
1089 static int open_device(const char *path, struct stat *st, const char **_devname)
1092 const char *devname = NULL;
1096 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1097 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1101 if (statfs(path, &sfs)) {
1102 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1105 if (sfs.f_type == EXT2_SUPER_MAGIC)
1107 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1109 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1113 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1119 devname = get_devname(path);
1121 *_devname = devname;
1123 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1124 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1128 /* Verify that the device we opened is the device intended */
1129 if (validate_device(path, devfd)) {
1130 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1131 program, path, devname);
1138 int install_loader(const char *path, int update_only)
1140 struct stat st, fst;
1142 const char *devname;
1144 devfd = open_device(path, &st, &devname);
1148 if (update_only && !already_installed(devfd)) {
1149 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1155 /* Read a pre-existing ADV, if already installed */
1157 syslinux_reset_adv(syslinux_adv);
1158 else if (read_adv(path, devfd) < 0) {
1162 if (modify_adv() < 0) {
1167 /* Install extlinux.sys */
1168 if (install_file(path, devfd, &fst)) {
1172 if (fst.st_dev != st.st_dev) {
1173 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1180 rv = install_bootblock(devfd, devname);
1188 * Modify the ADV of an existing installation
1190 int modify_existing_adv(const char *path)
1194 devfd = open_device(path, NULL, NULL);
1199 syslinux_reset_adv(syslinux_adv);
1200 else if (read_adv(path, devfd) < 0) {
1204 if (modify_adv() < 0) {
1208 if (write_adv(path, devfd) < 0) {
1216 int main(int argc, char *argv[])
1219 const char *directory;
1220 int update_only = -1;
1224 while ((o = getopt_long(argc, argv, short_options,
1225 long_options, NULL)) != EOF) {
1232 opt.sectors = strtoul(optarg, NULL, 0);
1233 if (opt.sectors < 1 || opt.sectors > 63) {
1235 "%s: invalid number of sectors: %u (must be 1-63)\n",
1236 program, opt.sectors);
1241 opt.heads = strtoul(optarg, NULL, 0);
1242 if (opt.heads < 1 || opt.heads > 256) {
1244 "%s: invalid number of heads: %u (must be 1-256)\n",
1245 program, opt.heads);
1253 opt.stupid_mode = 1;
1266 opt.set_once = optarg;
1275 fputs("extlinux " VERSION_STR
1276 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1283 directory = argv[optind];
1288 if (update_only == -1) {
1289 if (opt.reset_adv || opt.set_once) {
1290 return modify_existing_adv(directory);
1296 return install_loader(directory, update_only);