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 static char subvol[64];
147 extern unsigned char extlinux_bootsect[];
148 extern unsigned int extlinux_bootsect_len;
149 #define boot_block extlinux_bootsect
150 #define boot_block_len extlinux_bootsect_len
155 extern unsigned char extlinux_image[];
156 extern unsigned int extlinux_image_len;
157 #define boot_image extlinux_image
158 #define boot_image_len extlinux_image_len
161 * Common abort function
163 void __attribute__ ((noreturn)) die(const char *msg)
170 * read/write wrapper functions
172 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
174 char *bufp = (char *)buf;
179 rv = pread(fd, bufp, count, offset);
182 } else if (rv == -1) {
183 if (errno == EINTR) {
186 die(strerror(errno));
199 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
201 const char *bufp = (const char *)buf;
206 rv = pwrite(fd, bufp, count, offset);
209 } else if (rv == -1) {
210 if (errno == EINTR) {
213 die(strerror(errno));
229 int sectmap(int fd, uint32_t * sectors, int nsectors)
231 unsigned int blksize, blk, nblk;
235 if (ioctl(fd, FIGETBSZ, &blksize))
238 /* Number of sectors per block */
239 blksize >>= SECTOR_SHIFT;
245 dprintf("querying block %u\n", blk);
246 if (ioctl(fd, FIBMAP, &blk))
250 for (i = 0; i < blksize; i++) {
254 dprintf("Sector: %10u\n", blk);
264 * Get the size of a block device
266 uint64_t get_size(int devfd)
273 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
276 if (!ioctl(devfd, BLKGETSIZE, §s))
277 return (uint64_t) sects << 9;
278 else if (!fstat(devfd, &st) && st.st_size)
285 * Get device geometry and partition offset
287 struct geometry_table {
289 struct hd_geometry g;
292 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
293 (x/64/32) is the final fallback. I don't know what LS-240 has
294 as its geometry, since I don't have one and don't know anyone that does,
295 and Google wasn't helpful... */
296 static const struct geometry_table standard_geometries[] = {
297 {360 * 1024, {2, 9, 40, 0}},
298 {720 * 1024, {2, 9, 80, 0}},
299 {1200 * 1024, {2, 15, 80, 0}},
300 {1440 * 1024, {2, 18, 80, 0}},
301 {1680 * 1024, {2, 21, 80, 0}},
302 {1722 * 1024, {2, 21, 80, 0}},
303 {2880 * 1024, {2, 36, 80, 0}},
304 {3840 * 1024, {2, 48, 80, 0}},
305 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
309 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
311 struct floppy_struct fd_str;
312 const struct geometry_table *gp;
314 memset(geo, 0, sizeof *geo);
316 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
318 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
319 geo->heads = fd_str.head;
320 geo->sectors = fd_str.sect;
321 geo->cylinders = fd_str.track;
326 /* Didn't work. Let's see if this is one of the standard geometries */
327 for (gp = standard_geometries; gp->bytes; gp++) {
328 if (gp->bytes == totalbytes) {
329 memcpy(geo, &gp->g, sizeof *geo);
334 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
335 what zipdisks use, so this would help if someone has a USB key that
336 they're booting in USB-ZIP mode. */
338 geo->heads = opt.heads ? : 64;
339 geo->sectors = opt.sectors ? : 32;
340 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
343 if (!opt.sectors && !opt.heads)
345 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
346 " (on hard disks, this is usually harmless.)\n",
347 geo->heads, geo->sectors);
353 * Query the device geometry and put it into the boot sector.
354 * Map the file and put the map in the boot sector and file.
355 * Stick the "current directory" inode number into the file.
357 * Returns the number of modified bytes in the boot file.
359 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
361 struct stat dirst, xdst;
362 struct hd_geometry geo;
364 uint64_t totalbytes, totalsectors;
367 struct boot_sector *bs;
368 struct patch_area *patcharea;
371 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
372 char *dirpath, *subpath;
374 dirpath = realpath(dir, NULL);
375 if (!dirpath || stat(dir, &dirst)) {
376 perror("accessing install directory");
377 exit(255); /* This should never happen */
380 if (lstat(dirpath, &xdst) ||
381 dirst.st_ino != xdst.st_ino ||
382 dirst.st_dev != xdst.st_dev) {
383 perror("realpath returned nonsense");
387 subpath = strchr(dirpath, '\0');
388 while (--subpath > dirpath) {
389 if (*subpath == '/') {
391 if (lstat(dirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
392 subpath = strchr(subpath+1, '/');
394 subpath = "/"; /* It's the root of the filesystem */
401 /* Now subpath should contain the path relative to the fs base */
402 dprintf("subpath = %s\n", subpath);
404 totalbytes = get_size(devfd);
405 get_geometry(devfd, totalbytes, &geo);
408 geo.heads = opt.heads;
410 geo.sectors = opt.sectors;
412 /* Patch this into a fake FAT superblock. This isn't because
413 FAT is a good format in any way, it's because it lets the
414 early bootstrap share code with the FAT version. */
415 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
417 bs = (struct boot_sector *)boot_block;
419 totalsectors = totalbytes >> SECTOR_SHIFT;
420 if (totalsectors >= 65536) {
421 set_16(&bs->bsSectors, 0);
423 set_16(&bs->bsSectors, totalsectors);
425 set_32(&bs->bsHugeSectors, totalsectors);
427 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
428 set_16(&bs->bsSecPerTrack, geo.sectors);
429 set_16(&bs->bsHeads, geo.heads);
430 set_32(&bs->bsHiddenSecs, geo.start);
432 /* If we're in RAID mode then patch the appropriate instruction;
433 either way write the proper boot signature */
434 i = get_16(&bs->bsSignature);
436 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
438 set_16(&bs->bsSignature, 0xAA55);
440 /* Construct the boot file */
442 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
443 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
444 nsect += 2; /* Two sectors for the ADV */
445 sectp = alloca(sizeof(uint32_t) * nsect);
446 if (fs_type == EXT2) {
447 if (sectmap(fd, sectp, nsect)) {
451 } else if (fs_type == BTRFS) {
454 for (i = 0; i < nsect; i++)
455 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
458 /* First sector need pointer in boot sector */
459 set_32(&bs->NextSector, *sectp++);
462 set_16(&bs->MaxTransfer, 1);
464 /* Search for LDLINUX_MAGIC to find the patch area */
465 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
466 patcharea = (struct patch_area *)wp;
468 /* Set up the totals */
469 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
470 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
471 set_16(&patcharea->adv_sectors, 2);
472 set_32(&patcharea->dwords, dw);
474 /* Set the sector pointers */
475 secptroffset = get_16(&patcharea->secptroffset);
476 wp = (uint32_t *) ((char *)boot_image + secptroffset);
477 nptrs = get_16(&patcharea->secptrcnt);
479 memset(wp, 0, nptrs * 4);
481 set_32(wp++, *sectp++);
483 /* Poke in the base directory path */
484 diroffset = get_16(&patcharea->diroffset);
485 dirlen = get_16(&patcharea->dirlen);
486 if (dirlen <= strlen(subpath)) {
487 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
490 strncpy((char *)boot_image + diroffset, subpath, dirlen);
492 /* write subvol info if we have */
494 subvoloffset = get_16(&patcharea->subvoloffset);
495 subvollen = get_16(&patcharea->subvollen);
496 if (subvollen <= strlen(subvol)) {
497 fprintf(stderr, "Subvol name too long... aborting install!\n");
500 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);
512 return secptroffset + nptrs*4;
516 * Read the ADV from an existing instance, or initialize if invalid.
517 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
520 int read_adv(const char *path, int devfd)
527 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
528 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
529 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
530 perror("writing adv");
535 asprintf(&file, "%s%sextlinux.sys",
536 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
543 fd = open(file, O_RDONLY);
545 if (errno != ENOENT) {
548 syslinux_reset_adv(syslinux_adv);
550 } else if (fstat(fd, &st)) {
552 } else if (st.st_size < 2 * ADV_SIZE) {
553 /* Too small to be useful */
554 syslinux_reset_adv(syslinux_adv);
555 err = 0; /* Nothing to read... */
556 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
557 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
560 /* We got it... maybe? */
561 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
576 * Update the ADV in an existing installation.
578 int write_adv(const char *path, int devfd)
580 unsigned char advtmp[2 * ADV_SIZE];
587 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
588 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
589 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
590 perror("writing adv");
595 asprintf(&file, "%s%sextlinux.sys",
596 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
603 fd = open(file, O_RDONLY);
606 } else if (fstat(fd, &st)) {
608 } else if (st.st_size < 2 * ADV_SIZE) {
609 /* Too small to be useful */
611 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
612 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
615 /* We got it... maybe? */
616 err = syslinux_validate_adv(advtmp) ? -2 : 0;
618 /* Got a good one, write our own ADV here */
619 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
620 nflags = flags & ~EXT2_IMMUTABLE_FL;
622 ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
624 if (!(st.st_mode & S_IWUSR))
625 fchmod(fd, st.st_mode | S_IWUSR);
627 /* Need to re-open read-write */
629 fd = open(file, O_RDWR | O_SYNC);
632 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
633 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
634 fprintf(stderr, "%s: race condition on write\n", file);
637 /* Write our own version ... */
638 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
639 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
645 if (!(st.st_mode & S_IWUSR))
646 fchmod(fd, st.st_mode);
649 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
654 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
668 * Make any user-specified ADV modifications
675 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
676 fprintf(stderr, "%s: not enough space for boot-once command\n",
686 * Install the boot block on the specified device.
687 * Must be run AFTER install_file()!
689 int install_bootblock(int fd, const char *device)
691 struct ext2_super_block sb;
692 struct btrfs_super_block sb2;
695 if (fs_type == EXT2) {
696 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
697 perror("reading superblock");
700 if (sb.s_magic == EXT2_SUPER_MAGIC)
702 } else if (fs_type == BTRFS) {
703 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
705 perror("reading superblock");
708 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
712 fprintf(stderr, "no ext2/3/4 or btrfs superblock found on %s\n",
716 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
717 perror("writing bootblock");
724 int ext2_install_file(const char *path, int devfd, struct stat *rst)
727 int fd = -1, dirfd = -1, flags;
731 asprintf(&file, "%s%sextlinux.sys",
732 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
738 dirfd = open(path, O_RDONLY | O_DIRECTORY);
744 fd = open(file, O_RDONLY);
746 if (errno != ENOENT) {
751 /* If file exist, remove the immutable flag and set u+w mode */
752 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
753 flags &= ~EXT2_IMMUTABLE_FL;
754 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
756 if (!fstat(fd, &st)) {
757 fchmod(fd, st.st_mode | S_IWUSR);
762 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
763 S_IRUSR | S_IRGRP | S_IROTH);
769 /* Write it the first time */
770 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
771 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
772 boot_image_len) != 2 * ADV_SIZE) {
773 fprintf(stderr, "%s: write failure on %s\n", program, file);
777 /* Map the file, and patch the initial sector accordingly */
778 modbytes = patch_file_and_bootblock(fd, path, devfd);
780 /* Write the patch area again - this relies on the file being
781 overwritten in place! */
782 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
783 fprintf(stderr, "%s: write failure on %s\n", program, file);
787 /* Attempt to set immutable flag and remove all write access */
788 /* Only set immutable flag if file is owned by root */
789 if (!fstat(fd, &st)) {
790 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
791 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
792 flags |= EXT2_IMMUTABLE_FL;
793 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
797 if (fstat(fd, rst)) {
815 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
816 is not managered by btrfs tree, so actually this is not installed as files.
817 since the cow feature of btrfs will move the extlinux.sys every where */
818 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
820 patch_file_and_bootblock(-1, path, devfd);
821 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
823 perror("writing bootblock");
826 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
827 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
828 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
829 perror("writing adv");
832 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
833 if (stat(path, rst)) {
840 int install_file(const char *path, int devfd, struct stat *rst)
843 return ext2_install_file(path, devfd, rst);
844 else if (fs_type == BTRFS)
845 return btrfs_install_file(path, devfd, rst);
849 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
850 sector; this is consistent with FAT filesystems. */
851 int already_installed(int devfd)
855 xpread(devfd, buffer, 8, 3);
856 return !memcmp(buffer, "EXTLINUX", 8);
860 static char devname_buf[64];
862 static void device_cleanup(void)
868 /* Verify that a device fd and a pathname agree.
869 Return 0 on valid, -1 on error. */
870 static int validate_device(const char *path, int devfd)
872 struct stat pst, dst;
875 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
877 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
878 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
880 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
884 static const char *find_device(const char *mtab_file, dev_t dev)
889 const char *devname = NULL;
892 mtab = setmntent(mtab_file, "r");
897 while ((mnt = getmntent(mtab))) {
898 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
901 if (!strcmp(mnt->mnt_type, "btrfs") &&
902 !stat(mnt->mnt_dir, &dst) &&
904 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
910 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
911 tmp = strchr(subvol, 32);
915 break; /* should break and let upper layer try again */
921 if ((!strcmp(mnt->mnt_type, "ext2") ||
922 !strcmp(mnt->mnt_type, "ext3") ||
923 !strcmp(mnt->mnt_type, "ext4")) &&
924 !stat(mnt->mnt_fsname, &dst) &&
925 dst.st_rdev == dev) {
931 devname = strdup(mnt->mnt_fsname);
941 static const char *get_devname(const char *path)
943 const char *devname = NULL;
947 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
948 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
951 if (statfs(path, &sfs)) {
952 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
957 /* klibc doesn't have getmntent and friends; instead, just create
958 a new device with the appropriate device type */
959 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
960 major(st.st_dev), minor(st.st_dev));
962 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
963 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
967 atexit(device_cleanup); /* unlink the device node on exit */
968 devname = devname_buf;
972 /* check /etc/mtab first, since btrfs subvol info is only in here */
973 devname = find_device("/etc/mtab", st.st_dev);
974 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
978 strcpy(parent, path);
979 tmp = strrchr(parent, '/');
982 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
983 devname = get_devname(parent);
988 /* Didn't find it in /etc/mtab, try /proc/mounts */
989 devname = find_device("/proc/mounts", st.st_dev);
992 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
996 fprintf(stderr, "%s is device %s\n", path, devname);
1001 static int open_device(const char *path, struct stat *st, const char **_devname)
1004 const char *devname = NULL;
1008 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1009 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1013 if (statfs(path, &sfs)) {
1014 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1017 if (sfs.f_type == EXT2_SUPER_MAGIC)
1019 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1023 fprintf(stderr, "%s: not an ext2/3/4 or btrfs filesystem: %s\n",
1029 devname = get_devname(path);
1031 *_devname = devname;
1033 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1034 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1038 /* Verify that the device we opened is the device intended */
1039 if (validate_device(path, devfd)) {
1040 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1041 program, path, devname);
1048 int install_loader(const char *path, int update_only)
1050 struct stat st, fst;
1052 const char *devname;
1054 devfd = open_device(path, &st, &devname);
1058 if (update_only && !already_installed(devfd)) {
1059 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1065 /* Read a pre-existing ADV, if already installed */
1067 syslinux_reset_adv(syslinux_adv);
1068 else if (read_adv(path, devfd) < 0) {
1072 if (modify_adv() < 0) {
1077 /* Install extlinux.sys */
1078 if (install_file(path, devfd, &fst)) {
1082 if (fst.st_dev != st.st_dev) {
1083 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1090 rv = install_bootblock(devfd, devname);
1098 * Modify the ADV of an existing installation
1100 int modify_existing_adv(const char *path)
1104 devfd = open_device(path, NULL, NULL);
1109 syslinux_reset_adv(syslinux_adv);
1110 else if (read_adv(path, devfd) < 0) {
1114 if (modify_adv() < 0) {
1118 if (write_adv(path, devfd) < 0) {
1126 int main(int argc, char *argv[])
1129 const char *directory;
1130 int update_only = -1;
1134 while ((o = getopt_long(argc, argv, short_options,
1135 long_options, NULL)) != EOF) {
1142 opt.sectors = strtoul(optarg, NULL, 0);
1143 if (opt.sectors < 1 || opt.sectors > 63) {
1145 "%s: invalid number of sectors: %u (must be 1-63)\n",
1146 program, opt.sectors);
1151 opt.heads = strtoul(optarg, NULL, 0);
1152 if (opt.heads < 1 || opt.heads > 256) {
1154 "%s: invalid number of heads: %u (must be 1-256)\n",
1155 program, opt.heads);
1163 opt.stupid_mode = 1;
1176 opt.set_once = optarg;
1185 fputs("extlinux " VERSION_STR
1186 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1193 directory = argv[optind];
1198 if (update_only == -1) {
1199 if (opt.reset_adv || opt.set_once) {
1200 return modify_existing_adv(directory);
1206 return install_loader(directory, update_only);