1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 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 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. */
37 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/mount.h>
43 #include <linux/fd.h> /* Floppy geometry */
44 #include <linux/hdreg.h> /* Hard disk geometry */
45 #define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */
46 #include <linux/fs.h> /* FIGETBSZ, FIBMAP */
51 #include "../version.h"
55 # define dprintf printf
57 # define dprintf(...) ((void)0)
60 /* Global option handling */
61 /* Global fs_type for handling ext2/3/4 vs btrfs */
68 /* These are the options we can set and their values */
77 .sectors = 0,.heads = 0,.raid_mode = 0,.stupid_mode = 0,.reset_adv =
80 static void __attribute__ ((noreturn)) usage(int rv)
83 "Usage: %s [options] directory\n"
84 " --install -i Install over the current bootsector\n"
85 " --update -U Update a previous EXTLINUX installation\n"
86 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
87 " --sectors=# -S Force the number of sectors per track\n"
88 " --heads=# -H Force number of heads\n"
89 " --stupid -s Slow, safe and stupid mode\n"
90 " --raid -r Fall back to the next device on boot failure\n"
91 " --once=... -o Execute a command once upon boot\n"
92 " --clear-once -O Clear the boot-once command\n"
93 " --reset-adv Reset auxilliary data\n"
95 " Note: geometry is determined at boot time for devices which\n"
96 " are considered hard disks by the BIOS. Unfortunately, this is\n"
97 " not possible for devices which are considered floppy disks,\n"
98 " which includes zipdisks and LS-120 superfloppies.\n"
100 " The -z option is useful for USB devices which are considered\n"
101 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
112 static const struct option long_options[] = {
113 {"install", 0, NULL, 'i'},
114 {"update", 0, NULL, 'U'},
115 {"zipdrive", 0, NULL, 'z'},
116 {"sectors", 1, NULL, 'S'},
117 {"stupid", 0, NULL, 's'},
118 {"heads", 1, NULL, 'H'},
119 {"raid-mode", 0, NULL, 'r'},
120 {"version", 0, NULL, 'v'},
121 {"help", 0, NULL, 'h'},
122 {"once", 1, NULL, 'o'},
123 {"clear-once", 0, NULL, 'O'},
124 {"reset-adv", 0, NULL, OPT_RESET_ADV},
128 static const char short_options[] = "iUuzS:H:rvho:O";
130 #if defined(__linux__) && !defined(BLKGETSIZE64)
131 /* This takes a u64, but the size field says size_t. Someone screwed big. */
132 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
135 #ifndef EXT2_SUPER_OFFSET
136 #define EXT2_SUPER_OFFSET 1024
139 /* the btrfs partition first 64K blank area is used to store boot sector and
140 boot image, the boot sector is from 0~512, the boot image starts at 2K */
141 #define BTRFS_EXTLINUX_OFFSET (2*1024)
142 #define BTRFS_SUBVOL_OPT "subvol="
143 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
144 static char subvol[BTRFS_SUBVOL_MAX];
148 extern unsigned char extlinux_bootsect[];
149 extern unsigned int extlinux_bootsect_len;
150 #define boot_block extlinux_bootsect
151 #define boot_block_len extlinux_bootsect_len
156 extern unsigned char extlinux_image[];
157 extern unsigned int extlinux_image_len;
158 #define boot_image extlinux_image
159 #define boot_image_len extlinux_image_len
162 * Common abort function
164 void __attribute__ ((noreturn)) die(const char *msg)
171 * read/write wrapper functions
173 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
175 char *bufp = (char *)buf;
180 rv = pread(fd, bufp, count, offset);
183 } else if (rv == -1) {
184 if (errno == EINTR) {
187 die(strerror(errno));
200 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
202 const char *bufp = (const char *)buf;
207 rv = pwrite(fd, bufp, count, offset);
210 } else if (rv == -1) {
211 if (errno == EINTR) {
214 die(strerror(errno));
230 int sectmap(int fd, uint32_t * sectors, int nsectors)
232 unsigned int blksize, blk, nblk;
236 if (ioctl(fd, FIGETBSZ, &blksize))
239 /* Number of sectors per block */
240 blksize >>= SECTOR_SHIFT;
246 dprintf("querying block %u\n", blk);
247 if (ioctl(fd, FIBMAP, &blk))
251 for (i = 0; i < blksize; i++) {
255 dprintf("Sector: %10u\n", blk);
265 * Get the size of a block device
267 uint64_t get_size(int devfd)
274 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
277 if (!ioctl(devfd, BLKGETSIZE, §s))
278 return (uint64_t) sects << 9;
279 else if (!fstat(devfd, &st) && st.st_size)
286 * Get device geometry and partition offset
288 struct geometry_table {
290 struct hd_geometry g;
293 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
294 (x/64/32) is the final fallback. I don't know what LS-240 has
295 as its geometry, since I don't have one and don't know anyone that does,
296 and Google wasn't helpful... */
297 static const struct geometry_table standard_geometries[] = {
298 {360 * 1024, {2, 9, 40, 0}},
299 {720 * 1024, {2, 9, 80, 0}},
300 {1200 * 1024, {2, 15, 80, 0}},
301 {1440 * 1024, {2, 18, 80, 0}},
302 {1680 * 1024, {2, 21, 80, 0}},
303 {1722 * 1024, {2, 21, 80, 0}},
304 {2880 * 1024, {2, 36, 80, 0}},
305 {3840 * 1024, {2, 48, 80, 0}},
306 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
310 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
312 struct floppy_struct fd_str;
313 const struct geometry_table *gp;
315 memset(geo, 0, sizeof *geo);
317 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
319 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
320 geo->heads = fd_str.head;
321 geo->sectors = fd_str.sect;
322 geo->cylinders = fd_str.track;
327 /* Didn't work. Let's see if this is one of the standard geometries */
328 for (gp = standard_geometries; gp->bytes; gp++) {
329 if (gp->bytes == totalbytes) {
330 memcpy(geo, &gp->g, sizeof *geo);
335 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
336 what zipdisks use, so this would help if someone has a USB key that
337 they're booting in USB-ZIP mode. */
339 geo->heads = opt.heads ? : 64;
340 geo->sectors = opt.sectors ? : 32;
341 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
344 if (!opt.sectors && !opt.heads)
346 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
347 " (on hard disks, this is usually harmless.)\n",
348 geo->heads, geo->sectors);
354 * Query the device geometry and put it into the boot sector.
355 * Map the file and put the map in the boot sector and file.
356 * Stick the "current directory" inode number into the file.
358 * Returns the number of modified bytes in the boot file.
360 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
362 struct stat dirst, xdst;
363 struct hd_geometry geo;
365 uint64_t totalbytes, totalsectors;
368 struct boot_sector *bs;
369 struct patch_area *patcharea;
372 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
373 char *dirpath, *subpath;
375 dirpath = realpath(dir, NULL);
376 if (!dirpath || stat(dir, &dirst)) {
377 perror("accessing install directory");
378 exit(255); /* This should never happen */
381 if (lstat(dirpath, &xdst) ||
382 dirst.st_ino != xdst.st_ino ||
383 dirst.st_dev != xdst.st_dev) {
384 perror("realpath returned nonsense");
388 subpath = strchr(dirpath, '\0');
389 while (--subpath > dirpath) {
390 if (*subpath == '/') {
392 if (lstat(dirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
393 subpath = strchr(subpath+1, '/');
395 subpath = "/"; /* It's the root of the filesystem */
402 /* Now subpath should contain the path relative to the fs base */
403 dprintf("subpath = %s\n", subpath);
405 totalbytes = get_size(devfd);
406 get_geometry(devfd, totalbytes, &geo);
409 geo.heads = opt.heads;
411 geo.sectors = opt.sectors;
413 /* Patch this into a fake FAT superblock. This isn't because
414 FAT is a good format in any way, it's because it lets the
415 early bootstrap share code with the FAT version. */
416 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
418 bs = (struct boot_sector *)boot_block;
420 totalsectors = totalbytes >> SECTOR_SHIFT;
421 if (totalsectors >= 65536) {
422 set_16(&bs->bsSectors, 0);
424 set_16(&bs->bsSectors, totalsectors);
426 set_32(&bs->bsHugeSectors, totalsectors);
428 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
429 set_16(&bs->bsSecPerTrack, geo.sectors);
430 set_16(&bs->bsHeads, geo.heads);
431 set_32(&bs->bsHiddenSecs, geo.start);
433 /* If we're in RAID mode then patch the appropriate instruction;
434 either way write the proper boot signature */
435 i = get_16(&bs->bsSignature);
437 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
439 set_16(&bs->bsSignature, 0xAA55);
441 /* Construct the boot file */
443 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
444 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
445 nsect += 2; /* Two sectors for the ADV */
446 sectp = alloca(sizeof(uint32_t) * nsect);
447 if (fs_type == EXT2) {
448 if (sectmap(fd, sectp, nsect)) {
452 } else if (fs_type == BTRFS) {
455 for (i = 0; i < nsect; i++)
456 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
459 /* First sector need pointer in boot sector */
460 set_32(&bs->NextSector, *sectp++);
463 set_16(&bs->MaxTransfer, 1);
465 /* Search for LDLINUX_MAGIC to find the patch area */
466 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
467 patcharea = (struct patch_area *)wp;
469 /* Set up the totals */
470 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
471 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
472 set_16(&patcharea->adv_sectors, 2);
473 set_32(&patcharea->dwords, dw);
475 /* Set the sector pointers */
476 secptroffset = get_16(&patcharea->secptroffset);
477 wp = (uint32_t *) ((char *)boot_image + secptroffset);
478 nptrs = get_16(&patcharea->secptrcnt);
480 memset(wp, 0, nptrs * 4);
482 set_32(wp++, *sectp++);
484 /* Poke in the base directory path */
485 diroffset = get_16(&patcharea->diroffset);
486 dirlen = get_16(&patcharea->dirlen);
487 if (dirlen <= strlen(subpath)) {
488 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
491 strncpy((char *)boot_image + diroffset, subpath, dirlen);
494 /* write subvol info if we have */
495 subvoloffset = get_16(&patcharea->subvoloffset);
496 subvollen = get_16(&patcharea->subvollen);
497 if (subvollen <= strlen(subvol)) {
498 fprintf(stderr, "Subvol name too long... aborting install!\n");
501 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
503 /* Now produce a checksum */
504 set_32(&patcharea->checksum, 0);
506 csum = LDLINUX_MAGIC;
507 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
508 csum -= get_32(wp); /* Negative checksum */
510 set_32(&patcharea->checksum, csum);
513 * Assume all bytes modified. This can be optimized at the expense
514 * of keeping track of what the highest modified address ever was.
520 * Read the ADV from an existing instance, or initialize if invalid.
521 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
524 int read_adv(const char *path, int devfd)
531 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
532 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
533 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
534 perror("writing adv");
539 asprintf(&file, "%s%sextlinux.sys",
540 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
547 fd = open(file, O_RDONLY);
549 if (errno != ENOENT) {
552 syslinux_reset_adv(syslinux_adv);
554 } else if (fstat(fd, &st)) {
556 } else if (st.st_size < 2 * ADV_SIZE) {
557 /* Too small to be useful */
558 syslinux_reset_adv(syslinux_adv);
559 err = 0; /* Nothing to read... */
560 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
561 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
564 /* We got it... maybe? */
565 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
580 * Update the ADV in an existing installation.
582 int write_adv(const char *path, int devfd)
584 unsigned char advtmp[2 * ADV_SIZE];
591 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
592 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
593 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
594 perror("writing adv");
599 asprintf(&file, "%s%sextlinux.sys",
600 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
607 fd = open(file, O_RDONLY);
610 } else if (fstat(fd, &st)) {
612 } else if (st.st_size < 2 * ADV_SIZE) {
613 /* Too small to be useful */
615 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
616 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
619 /* We got it... maybe? */
620 err = syslinux_validate_adv(advtmp) ? -2 : 0;
622 /* Got a good one, write our own ADV here */
623 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
624 nflags = flags & ~EXT2_IMMUTABLE_FL;
626 ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
628 if (!(st.st_mode & S_IWUSR))
629 fchmod(fd, st.st_mode | S_IWUSR);
631 /* Need to re-open read-write */
633 fd = open(file, O_RDWR | O_SYNC);
636 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
637 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
638 fprintf(stderr, "%s: race condition on write\n", file);
641 /* Write our own version ... */
642 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
643 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
649 if (!(st.st_mode & S_IWUSR))
650 fchmod(fd, st.st_mode);
653 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
658 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
672 * Make any user-specified ADV modifications
679 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
680 fprintf(stderr, "%s: not enough space for boot-once command\n",
690 * Install the boot block on the specified device.
691 * Must be run AFTER install_file()!
693 int install_bootblock(int fd, const char *device)
695 struct ext2_super_block sb;
696 struct btrfs_super_block sb2;
699 if (fs_type == EXT2) {
700 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
701 perror("reading superblock");
704 if (sb.s_magic == EXT2_SUPER_MAGIC)
706 } else if (fs_type == BTRFS) {
707 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
709 perror("reading superblock");
712 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
716 fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
720 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
721 perror("writing bootblock");
728 int ext2_install_file(const char *path, int devfd, struct stat *rst)
731 int fd = -1, dirfd = -1, flags;
735 asprintf(&file, "%s%sextlinux.sys",
736 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
742 dirfd = open(path, O_RDONLY | O_DIRECTORY);
748 fd = open(file, O_RDONLY);
750 if (errno != ENOENT) {
755 /* If file exist, remove the immutable flag and set u+w mode */
756 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
757 flags &= ~EXT2_IMMUTABLE_FL;
758 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
760 if (!fstat(fd, &st)) {
761 fchmod(fd, st.st_mode | S_IWUSR);
766 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
767 S_IRUSR | S_IRGRP | S_IROTH);
773 /* Write it the first time */
774 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
775 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
776 boot_image_len) != 2 * ADV_SIZE) {
777 fprintf(stderr, "%s: write failure on %s\n", program, file);
781 /* Map the file, and patch the initial sector accordingly */
782 modbytes = patch_file_and_bootblock(fd, path, devfd);
784 /* Write the patch area again - this relies on the file being
785 overwritten in place! */
786 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
787 fprintf(stderr, "%s: write failure on %s\n", program, file);
791 /* Attempt to set immutable flag and remove all write access */
792 /* Only set immutable flag if file is owned by root */
793 if (!fstat(fd, &st)) {
794 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
795 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
796 flags |= EXT2_IMMUTABLE_FL;
797 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
801 if (fstat(fd, rst)) {
819 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
820 is not managered by btrfs tree, so actually this is not installed as files.
821 since the cow feature of btrfs will move the extlinux.sys every where */
822 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
824 patch_file_and_bootblock(-1, path, devfd);
825 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
827 perror("writing bootblock");
830 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
831 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
832 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
833 perror("writing adv");
836 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
837 if (stat(path, rst)) {
844 int install_file(const char *path, int devfd, struct stat *rst)
847 return ext2_install_file(path, devfd, rst);
848 else if (fs_type == BTRFS)
849 return btrfs_install_file(path, devfd, rst);
853 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
854 sector; this is consistent with FAT filesystems. */
855 int already_installed(int devfd)
859 xpread(devfd, buffer, 8, 3);
860 return !memcmp(buffer, "EXTLINUX", 8);
864 static char devname_buf[64];
866 static void device_cleanup(void)
872 /* Verify that a device fd and a pathname agree.
873 Return 0 on valid, -1 on error. */
874 static int validate_device(const char *path, int devfd)
876 struct stat pst, dst;
879 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
881 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
882 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
884 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
888 static const char *find_device(const char *mtab_file, dev_t dev)
893 const char *devname = NULL;
896 mtab = setmntent(mtab_file, "r");
901 while ((mnt = getmntent(mtab))) {
902 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
905 if (!strcmp(mnt->mnt_type, "btrfs") &&
906 !stat(mnt->mnt_dir, &dst) &&
908 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
914 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
915 tmp = strchr(subvol, 32);
919 break; /* should break and let upper layer try again */
925 if ((!strcmp(mnt->mnt_type, "ext2") ||
926 !strcmp(mnt->mnt_type, "ext3") ||
927 !strcmp(mnt->mnt_type, "ext4")) &&
928 !stat(mnt->mnt_fsname, &dst) &&
929 dst.st_rdev == dev) {
935 devname = strdup(mnt->mnt_fsname);
945 static const char *get_devname(const char *path)
947 const char *devname = NULL;
951 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
952 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
955 if (statfs(path, &sfs)) {
956 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
961 /* klibc doesn't have getmntent and friends; instead, just create
962 a new device with the appropriate device type */
963 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
964 major(st.st_dev), minor(st.st_dev));
966 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
967 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
971 atexit(device_cleanup); /* unlink the device node on exit */
972 devname = devname_buf;
976 /* check /etc/mtab first, since btrfs subvol info is only in here */
977 devname = find_device("/etc/mtab", st.st_dev);
978 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
982 strcpy(parent, path);
983 tmp = strrchr(parent, '/');
986 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
987 devname = get_devname(parent);
992 /* Didn't find it in /etc/mtab, try /proc/mounts */
993 devname = find_device("/proc/mounts", st.st_dev);
996 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1000 fprintf(stderr, "%s is device %s\n", path, devname);
1005 static int open_device(const char *path, struct stat *st, const char **_devname)
1008 const char *devname = NULL;
1012 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1013 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1017 if (statfs(path, &sfs)) {
1018 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1021 if (sfs.f_type == EXT2_SUPER_MAGIC)
1023 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1027 fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
1033 devname = get_devname(path);
1035 *_devname = devname;
1037 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1038 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1042 /* Verify that the device we opened is the device intended */
1043 if (validate_device(path, devfd)) {
1044 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1045 program, path, devname);
1052 int install_loader(const char *path, int update_only)
1054 struct stat st, fst;
1056 const char *devname;
1058 devfd = open_device(path, &st, &devname);
1062 if (update_only && !already_installed(devfd)) {
1063 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1069 /* Read a pre-existing ADV, if already installed */
1071 syslinux_reset_adv(syslinux_adv);
1072 else if (read_adv(path, devfd) < 0) {
1076 if (modify_adv() < 0) {
1081 /* Install extlinux.sys */
1082 if (install_file(path, devfd, &fst)) {
1086 if (fst.st_dev != st.st_dev) {
1087 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1094 rv = install_bootblock(devfd, devname);
1102 * Modify the ADV of an existing installation
1104 int modify_existing_adv(const char *path)
1108 devfd = open_device(path, NULL, NULL);
1113 syslinux_reset_adv(syslinux_adv);
1114 else if (read_adv(path, devfd) < 0) {
1118 if (modify_adv() < 0) {
1122 if (write_adv(path, devfd) < 0) {
1130 int main(int argc, char *argv[])
1133 const char *directory;
1134 int update_only = -1;
1138 while ((o = getopt_long(argc, argv, short_options,
1139 long_options, NULL)) != EOF) {
1146 opt.sectors = strtoul(optarg, NULL, 0);
1147 if (opt.sectors < 1 || opt.sectors > 63) {
1149 "%s: invalid number of sectors: %u (must be 1-63)\n",
1150 program, opt.sectors);
1155 opt.heads = strtoul(optarg, NULL, 0);
1156 if (opt.heads < 1 || opt.heads > 256) {
1158 "%s: invalid number of heads: %u (must be 1-256)\n",
1159 program, opt.heads);
1167 opt.stupid_mode = 1;
1180 opt.set_once = optarg;
1189 fputs("extlinux " VERSION_STR
1190 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1197 directory = argv[optind];
1202 if (update_only == -1) {
1203 if (opt.reset_adv || opt.set_once) {
1204 return modify_existing_adv(directory);
1210 return install_loader(directory, update_only);