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 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 */
53 #include "../version.h"
57 # define dprintf printf
59 # define dprintf(...) ((void)0)
62 /* Global option handling */
63 /* Global fs_type for handling fat, ext2/3/4 and btrfs */
71 /* These are the options we can set and their values */
80 .sectors = 0,.heads = 0,.raid_mode = 0,.stupid_mode = 0,.reset_adv =
83 static void __attribute__ ((noreturn)) usage(int rv)
86 "Usage: %s [options] directory\n"
87 " --install -i Install over the current bootsector\n"
88 " --update -U Update a previous EXTLINUX installation\n"
89 " --zip -z Force zipdrive geometry (-H 64 -S 32)\n"
90 " --sectors=# -S Force the number of sectors per track\n"
91 " --heads=# -H Force number of heads\n"
92 " --stupid -s Slow, safe and stupid mode\n"
93 " --raid -r Fall back to the next device on boot failure\n"
94 " --once=... -o Execute a command once upon boot\n"
95 " --clear-once -O Clear the boot-once command\n"
96 " --reset-adv Reset auxilliary data\n"
98 " Note: geometry is determined at boot time for devices which\n"
99 " are considered hard disks by the BIOS. Unfortunately, this is\n"
100 " not possible for devices which are considered floppy disks,\n"
101 " which includes zipdisks and LS-120 superfloppies.\n"
103 " The -z option is useful for USB devices which are considered\n"
104 " hard disks by some BIOSes and zipdrives by other BIOSes.\n",
115 static const struct option long_options[] = {
116 {"install", 0, NULL, 'i'},
117 {"update", 0, NULL, 'U'},
118 {"zipdrive", 0, NULL, 'z'},
119 {"sectors", 1, NULL, 'S'},
120 {"stupid", 0, NULL, 's'},
121 {"heads", 1, NULL, 'H'},
122 {"raid-mode", 0, NULL, 'r'},
123 {"version", 0, NULL, 'v'},
124 {"help", 0, NULL, 'h'},
125 {"once", 1, NULL, 'o'},
126 {"clear-once", 0, NULL, 'O'},
127 {"reset-adv", 0, NULL, OPT_RESET_ADV},
131 static const char short_options[] = "iUuzS:H:rvho:O";
133 #if defined(__linux__) && !defined(BLKGETSIZE64)
134 /* This takes a u64, but the size field says size_t. Someone screwed big. */
135 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
138 #ifndef EXT2_SUPER_OFFSET
139 #define EXT2_SUPER_OFFSET 1024
142 /* the btrfs partition first 64K blank area is used to store boot sector and
143 boot image, the boot sector is from 0~512, the boot image starts at 2K */
144 #define BTRFS_EXTLINUX_OFFSET (2*1024)
145 #define BTRFS_SUBVOL_OPT "subvol="
146 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
147 static char subvol[BTRFS_SUBVOL_MAX];
151 extern unsigned char extlinux_bootsect[];
152 extern unsigned int extlinux_bootsect_len;
153 #define boot_block extlinux_bootsect
154 #define boot_block_len extlinux_bootsect_len
159 extern unsigned char extlinux_image[];
160 extern unsigned int extlinux_image_len;
161 #define boot_image extlinux_image
162 #define boot_image_len extlinux_image_len
165 * Common abort function
167 void __attribute__ ((noreturn)) die(const char *msg)
174 * read/write wrapper functions
176 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
178 char *bufp = (char *)buf;
183 rv = pread(fd, bufp, count, offset);
186 } else if (rv == -1) {
187 if (errno == EINTR) {
190 die(strerror(errno));
203 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
205 const char *bufp = (const char *)buf;
210 rv = pwrite(fd, bufp, count, offset);
213 } else if (rv == -1) {
214 if (errno == EINTR) {
217 die(strerror(errno));
233 int sectmap(int fd, uint32_t * sectors, int nsectors)
235 unsigned int blksize, blk, nblk;
239 if (ioctl(fd, FIGETBSZ, &blksize))
242 /* Number of sectors per block */
243 blksize >>= SECTOR_SHIFT;
249 dprintf("querying block %u\n", blk);
250 if (ioctl(fd, FIBMAP, &blk))
254 for (i = 0; i < blksize; i++) {
258 dprintf("Sector: %10u\n", blk);
268 * Get the size of a block device
270 uint64_t get_size(int devfd)
277 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
280 if (!ioctl(devfd, BLKGETSIZE, §s))
281 return (uint64_t) sects << 9;
282 else if (!fstat(devfd, &st) && st.st_size)
289 * Get device geometry and partition offset
291 struct geometry_table {
293 struct hd_geometry g;
296 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
297 (x/64/32) is the final fallback. I don't know what LS-240 has
298 as its geometry, since I don't have one and don't know anyone that does,
299 and Google wasn't helpful... */
300 static const struct geometry_table standard_geometries[] = {
301 {360 * 1024, {2, 9, 40, 0}},
302 {720 * 1024, {2, 9, 80, 0}},
303 {1200 * 1024, {2, 15, 80, 0}},
304 {1440 * 1024, {2, 18, 80, 0}},
305 {1680 * 1024, {2, 21, 80, 0}},
306 {1722 * 1024, {2, 21, 80, 0}},
307 {2880 * 1024, {2, 36, 80, 0}},
308 {3840 * 1024, {2, 48, 80, 0}},
309 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
313 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
315 struct floppy_struct fd_str;
316 const struct geometry_table *gp;
318 memset(geo, 0, sizeof *geo);
320 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
322 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
323 geo->heads = fd_str.head;
324 geo->sectors = fd_str.sect;
325 geo->cylinders = fd_str.track;
330 /* Didn't work. Let's see if this is one of the standard geometries */
331 for (gp = standard_geometries; gp->bytes; gp++) {
332 if (gp->bytes == totalbytes) {
333 memcpy(geo, &gp->g, sizeof *geo);
338 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
339 what zipdisks use, so this would help if someone has a USB key that
340 they're booting in USB-ZIP mode. */
342 geo->heads = opt.heads ? : 64;
343 geo->sectors = opt.sectors ? : 32;
344 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
347 if (!opt.sectors && !opt.heads)
349 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
350 " (on hard disks, this is usually harmless.)\n",
351 geo->heads, geo->sectors);
357 * Query the device geometry and put it into the boot sector.
358 * Map the file and put the map in the boot sector and file.
359 * Stick the "current directory" inode number into the file.
361 * Returns the number of modified bytes in the boot file.
363 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
365 struct stat dirst, xdst;
366 struct hd_geometry geo;
368 uint64_t totalbytes, totalsectors;
371 struct boot_sector *bs;
372 struct patch_area *patcharea;
375 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
376 char *dirpath, *subpath;
378 dirpath = realpath(dir, NULL);
379 if (!dirpath || stat(dir, &dirst)) {
380 perror("accessing install directory");
381 exit(255); /* This should never happen */
384 if (lstat(dirpath, &xdst) ||
385 dirst.st_ino != xdst.st_ino ||
386 dirst.st_dev != xdst.st_dev) {
387 perror("realpath returned nonsense");
391 subpath = strchr(dirpath, '\0');
392 while (--subpath >= dirpath) {
393 if (*subpath == '/') {
395 if (lstat(dirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
396 subpath = strchr(subpath+1, '/');
398 subpath = "/"; /* It's the root of the filesystem */
405 /* Now subpath should contain the path relative to the fs base */
406 dprintf("subpath = %s\n", subpath);
408 totalbytes = get_size(devfd);
409 get_geometry(devfd, totalbytes, &geo);
412 geo.heads = opt.heads;
414 geo.sectors = opt.sectors;
416 /* Patch this into a fake FAT superblock. This isn't because
417 FAT is a good format in any way, it's because it lets the
418 early bootstrap share code with the FAT version. */
419 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
421 bs = (struct boot_sector *)boot_block;
423 totalsectors = totalbytes >> SECTOR_SHIFT;
424 if (totalsectors >= 65536) {
425 set_16(&bs->bsSectors, 0);
427 set_16(&bs->bsSectors, totalsectors);
429 set_32(&bs->bsHugeSectors, totalsectors);
431 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
432 set_16(&bs->bsSecPerTrack, geo.sectors);
433 set_16(&bs->bsHeads, geo.heads);
434 set_32(&bs->bsHiddenSecs, geo.start);
436 /* If we're in RAID mode then patch the appropriate instruction;
437 either way write the proper boot signature */
438 i = get_16(&bs->bsSignature);
440 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
442 set_16(&bs->bsSignature, 0xAA55);
444 /* Construct the boot file */
446 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
447 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
448 nsect += 2; /* Two sectors for the ADV */
449 sectp = alloca(sizeof(uint32_t) * nsect);
450 if (fs_type == EXT2 || fs_type == VFAT) {
451 if (sectmap(fd, sectp, nsect)) {
455 } else if (fs_type == BTRFS) {
458 for (i = 0; i < nsect; i++)
459 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
462 /* First sector need pointer in boot sector */
463 set_32(&bs->NextSector, *sectp++);
466 set_16(&bs->MaxTransfer, 1);
468 /* Search for LDLINUX_MAGIC to find the patch area */
469 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
470 patcharea = (struct patch_area *)wp;
472 /* Set up the totals */
473 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
474 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
475 set_16(&patcharea->adv_sectors, 2);
476 set_32(&patcharea->dwords, dw);
478 /* Set the sector pointers */
479 secptroffset = get_16(&patcharea->secptroffset);
480 wp = (uint32_t *) ((char *)boot_image + secptroffset);
481 nptrs = get_16(&patcharea->secptrcnt);
483 memset(wp, 0, nptrs * 4);
485 set_32(wp++, *sectp++);
487 /* Poke in the base directory path */
488 diroffset = get_16(&patcharea->diroffset);
489 dirlen = get_16(&patcharea->dirlen);
490 if (dirlen <= strlen(subpath)) {
491 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
494 strncpy((char *)boot_image + diroffset, subpath, dirlen);
497 /* write subvol info if we have */
498 subvoloffset = get_16(&patcharea->subvoloffset);
499 subvollen = get_16(&patcharea->subvollen);
500 if (subvollen <= strlen(subvol)) {
501 fprintf(stderr, "Subvol name too long... aborting install!\n");
504 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
506 /* Now produce a checksum */
507 set_32(&patcharea->checksum, 0);
509 csum = LDLINUX_MAGIC;
510 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
511 csum -= get_32(wp); /* Negative checksum */
513 set_32(&patcharea->checksum, csum);
516 * Assume all bytes modified. This can be optimized at the expense
517 * of keeping track of what the highest modified address ever was.
523 * Read the ADV from an existing instance, or initialize if invalid.
524 * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
527 int read_adv(const char *path, int devfd)
534 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
535 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
536 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
537 perror("writing adv");
542 asprintf(&file, "%s%sextlinux.sys",
543 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
550 fd = open(file, O_RDONLY);
552 if (errno != ENOENT) {
555 syslinux_reset_adv(syslinux_adv);
557 } else if (fstat(fd, &st)) {
559 } else if (st.st_size < 2 * ADV_SIZE) {
560 /* Too small to be useful */
561 syslinux_reset_adv(syslinux_adv);
562 err = 0; /* Nothing to read... */
563 } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
564 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
567 /* We got it... maybe? */
568 err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
583 * Update the ADV in an existing installation.
585 int write_adv(const char *path, int devfd)
587 unsigned char advtmp[2 * ADV_SIZE];
594 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
595 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
596 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
597 perror("writing adv");
602 asprintf(&file, "%s%sextlinux.sys",
603 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
610 fd = open(file, O_RDONLY);
613 } else if (fstat(fd, &st)) {
615 } else if (st.st_size < 2 * ADV_SIZE) {
616 /* Too small to be useful */
618 } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
619 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
622 /* We got it... maybe? */
623 err = syslinux_validate_adv(advtmp) ? -2 : 0;
625 /* Got a good one, write our own ADV here */
626 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
627 nflags = flags & ~EXT2_IMMUTABLE_FL;
629 ioctl(fd, EXT2_IOC_SETFLAGS, &nflags);
631 if (!(st.st_mode & S_IWUSR))
632 fchmod(fd, st.st_mode | S_IWUSR);
634 /* Need to re-open read-write */
636 fd = open(file, O_RDWR | O_SYNC);
639 } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
640 xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
641 fprintf(stderr, "%s: race condition on write\n", file);
644 /* Write our own version ... */
645 if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
646 st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
652 if (!(st.st_mode & S_IWUSR))
653 fchmod(fd, st.st_mode);
656 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
661 fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
675 * Make any user-specified ADV modifications
682 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
683 fprintf(stderr, "%s: not enough space for boot-once command\n",
693 * Install the boot block on the specified device.
694 * Must be run AFTER install_file()!
696 int install_bootblock(int fd, const char *device)
698 struct ext2_super_block sb;
699 struct btrfs_super_block sb2;
700 struct boot_sector sb3;
703 if (fs_type == EXT2) {
704 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
705 perror("reading superblock");
708 if (sb.s_magic == EXT2_SUPER_MAGIC)
710 } else if (fs_type == BTRFS) {
711 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
713 perror("reading superblock");
716 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
718 } else if (fs_type == VFAT) {
719 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
720 perror("reading fat superblock");
723 if (sb3.bsResSectors && sb3.bsFATs &&
724 (strstr(sb3.bs16.FileSysType, "FAT") ||
725 strstr(sb3.bs32.FileSysType, "FAT")))
729 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
733 if (fs_type == VFAT) {
734 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
735 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
736 xpwrite(fd, &bs->bsCode, bsCodeLen,
737 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
738 perror("writing fat bootblock");
742 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
743 perror("writing bootblock");
751 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
754 int fd = -1, dirfd = -1, flags;
758 asprintf(&file, "%s%sextlinux.sys",
759 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
765 dirfd = open(path, O_RDONLY | O_DIRECTORY);
771 fd = open(file, O_RDONLY);
773 if (errno != ENOENT) {
777 } else if (fs_type == EXT2) {
778 /* If file exist, remove the immutable flag and set u+w mode */
779 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
780 flags &= ~EXT2_IMMUTABLE_FL;
781 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
783 if (!fstat(fd, &st)) {
784 fchmod(fd, st.st_mode | S_IWUSR);
789 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
790 S_IRUSR | S_IRGRP | S_IROTH);
796 /* Write it the first time */
797 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
798 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
799 boot_image_len) != 2 * ADV_SIZE) {
800 fprintf(stderr, "%s: write failure on %s\n", program, file);
804 /* Map the file, and patch the initial sector accordingly */
805 modbytes = patch_file_and_bootblock(fd, path, devfd);
807 /* Write the patch area again - this relies on the file being
808 overwritten in place! */
809 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
810 fprintf(stderr, "%s: write failure on %s\n", program, file);
814 /* Attempt to set immutable flag and remove all write access */
815 /* Only set immutable flag if file is owned by root */
816 if (!fstat(fd, &st)) {
817 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
818 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
819 flags |= EXT2_IMMUTABLE_FL;
820 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
824 if (fstat(fd, rst)) {
842 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
843 is not managered by btrfs tree, so actually this is not installed as files.
844 since the cow feature of btrfs will move the extlinux.sys every where */
845 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
847 patch_file_and_bootblock(-1, path, devfd);
848 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
850 perror("writing bootblock");
853 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
854 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
855 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
856 perror("writing adv");
859 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
860 if (stat(path, rst)) {
867 int install_file(const char *path, int devfd, struct stat *rst)
869 if (fs_type == EXT2 || fs_type == VFAT)
870 return ext2_fat_install_file(path, devfd, rst);
871 else if (fs_type == BTRFS)
872 return btrfs_install_file(path, devfd, rst);
876 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
877 sector; this is consistent with FAT filesystems. */
878 int already_installed(int devfd)
882 xpread(devfd, buffer, 8, 3);
883 return !memcmp(buffer, "EXTLINUX", 8);
887 static char devname_buf[64];
889 static void device_cleanup(void)
895 /* Verify that a device fd and a pathname agree.
896 Return 0 on valid, -1 on error. */
897 static int validate_device(const char *path, int devfd)
899 struct stat pst, dst;
902 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
904 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
905 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
907 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
911 static const char *find_device(const char *mtab_file, dev_t dev)
916 const char *devname = NULL;
919 mtab = setmntent(mtab_file, "r");
924 while ((mnt = getmntent(mtab))) {
925 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
928 if (!strcmp(mnt->mnt_type, "btrfs") &&
929 !stat(mnt->mnt_dir, &dst) &&
931 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
937 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
938 tmp = strchr(subvol, 32);
942 break; /* should break and let upper layer try again */
948 if ((!strcmp(mnt->mnt_type, "ext2") ||
949 !strcmp(mnt->mnt_type, "ext3") ||
950 !strcmp(mnt->mnt_type, "ext4")) &&
951 !stat(mnt->mnt_fsname, &dst) &&
952 dst.st_rdev == dev) {
957 if ((!strcmp(mnt->mnt_type, "vfat")) &&
958 !stat(mnt->mnt_fsname, &dst) &&
959 dst.st_rdev == dev) {
965 devname = strdup(mnt->mnt_fsname);
975 static const char *get_devname(const char *path)
977 const char *devname = NULL;
981 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
982 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
985 if (statfs(path, &sfs)) {
986 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
991 /* klibc doesn't have getmntent and friends; instead, just create
992 a new device with the appropriate device type */
993 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
994 major(st.st_dev), minor(st.st_dev));
996 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
997 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
1001 atexit(device_cleanup); /* unlink the device node on exit */
1002 devname = devname_buf;
1006 /* check /etc/mtab first, since btrfs subvol info is only in here */
1007 devname = find_device("/etc/mtab", st.st_dev);
1008 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1012 strcpy(parent, path);
1013 tmp = strrchr(parent, '/');
1016 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1017 devname = get_devname(parent);
1022 /* Didn't find it in /etc/mtab, try /proc/mounts */
1023 devname = find_device("/proc/mounts", st.st_dev);
1026 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1030 fprintf(stderr, "%s is device %s\n", path, devname);
1035 static int open_device(const char *path, struct stat *st, const char **_devname)
1038 const char *devname = NULL;
1042 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1043 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1047 if (statfs(path, &sfs)) {
1048 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1051 if (sfs.f_type == EXT2_SUPER_MAGIC)
1053 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1055 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1059 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1065 devname = get_devname(path);
1067 *_devname = devname;
1069 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1070 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1074 /* Verify that the device we opened is the device intended */
1075 if (validate_device(path, devfd)) {
1076 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1077 program, path, devname);
1084 int install_loader(const char *path, int update_only)
1086 struct stat st, fst;
1088 const char *devname;
1090 devfd = open_device(path, &st, &devname);
1094 if (update_only && !already_installed(devfd)) {
1095 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1101 /* Read a pre-existing ADV, if already installed */
1103 syslinux_reset_adv(syslinux_adv);
1104 else if (read_adv(path, devfd) < 0) {
1108 if (modify_adv() < 0) {
1113 /* Install extlinux.sys */
1114 if (install_file(path, devfd, &fst)) {
1118 if (fst.st_dev != st.st_dev) {
1119 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1126 rv = install_bootblock(devfd, devname);
1134 * Modify the ADV of an existing installation
1136 int modify_existing_adv(const char *path)
1140 devfd = open_device(path, NULL, NULL);
1145 syslinux_reset_adv(syslinux_adv);
1146 else if (read_adv(path, devfd) < 0) {
1150 if (modify_adv() < 0) {
1154 if (write_adv(path, devfd) < 0) {
1162 int main(int argc, char *argv[])
1165 const char *directory;
1166 int update_only = -1;
1170 while ((o = getopt_long(argc, argv, short_options,
1171 long_options, NULL)) != EOF) {
1178 opt.sectors = strtoul(optarg, NULL, 0);
1179 if (opt.sectors < 1 || opt.sectors > 63) {
1181 "%s: invalid number of sectors: %u (must be 1-63)\n",
1182 program, opt.sectors);
1187 opt.heads = strtoul(optarg, NULL, 0);
1188 if (opt.heads < 1 || opt.heads > 256) {
1190 "%s: invalid number of heads: %u (must be 1-256)\n",
1191 program, opt.heads);
1199 opt.stupid_mode = 1;
1212 opt.set_once = optarg;
1221 fputs("extlinux " VERSION_STR
1222 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1229 directory = argv[optind];
1234 if (update_only == -1) {
1235 if (opt.reset_adv || opt.set_once) {
1236 return modify_existing_adv(directory);
1242 return install_loader(directory, update_only);