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");
740 } else if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
741 perror("writing bootblock");
748 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
751 int fd = -1, dirfd = -1, flags;
755 asprintf(&file, "%s%sextlinux.sys",
756 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
762 dirfd = open(path, O_RDONLY | O_DIRECTORY);
768 fd = open(file, O_RDONLY);
770 if (errno != ENOENT) {
774 } else if (fs_type == EXT2) {
775 /* If file exist, remove the immutable flag and set u+w mode */
776 if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
777 flags &= ~EXT2_IMMUTABLE_FL;
778 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
780 if (!fstat(fd, &st)) {
781 fchmod(fd, st.st_mode | S_IWUSR);
786 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
787 S_IRUSR | S_IRGRP | S_IROTH);
793 /* Write it the first time */
794 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
795 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
796 boot_image_len) != 2 * ADV_SIZE) {
797 fprintf(stderr, "%s: write failure on %s\n", program, file);
801 /* Map the file, and patch the initial sector accordingly */
802 modbytes = patch_file_and_bootblock(fd, path, devfd);
804 /* Write the patch area again - this relies on the file being
805 overwritten in place! */
806 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
807 fprintf(stderr, "%s: write failure on %s\n", program, file);
811 /* Attempt to set immutable flag and remove all write access */
812 /* Only set immutable flag if file is owned by root */
813 if (!fstat(fd, &st)) {
814 fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
815 if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
816 flags |= EXT2_IMMUTABLE_FL;
817 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
821 if (fstat(fd, rst)) {
839 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
840 is not managered by btrfs tree, so actually this is not installed as files.
841 since the cow feature of btrfs will move the extlinux.sys every where */
842 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
844 patch_file_and_bootblock(-1, path, devfd);
845 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
847 perror("writing bootblock");
850 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
851 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
852 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
853 perror("writing adv");
856 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
857 if (stat(path, rst)) {
864 int install_file(const char *path, int devfd, struct stat *rst)
866 if (fs_type == EXT2 || fs_type == VFAT)
867 return ext2_fat_install_file(path, devfd, rst);
868 else if (fs_type == BTRFS)
869 return btrfs_install_file(path, devfd, rst);
873 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
874 sector; this is consistent with FAT filesystems. */
875 int already_installed(int devfd)
879 xpread(devfd, buffer, 8, 3);
880 return !memcmp(buffer, "EXTLINUX", 8);
884 static char devname_buf[64];
886 static void device_cleanup(void)
892 /* Verify that a device fd and a pathname agree.
893 Return 0 on valid, -1 on error. */
894 static int validate_device(const char *path, int devfd)
896 struct stat pst, dst;
899 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
901 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
902 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
904 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
908 static const char *find_device(const char *mtab_file, dev_t dev)
913 const char *devname = NULL;
916 mtab = setmntent(mtab_file, "r");
921 while ((mnt = getmntent(mtab))) {
922 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
925 if (!strcmp(mnt->mnt_type, "btrfs") &&
926 !stat(mnt->mnt_dir, &dst) &&
928 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
934 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
935 tmp = strchr(subvol, 32);
939 break; /* should break and let upper layer try again */
945 if ((!strcmp(mnt->mnt_type, "ext2") ||
946 !strcmp(mnt->mnt_type, "ext3") ||
947 !strcmp(mnt->mnt_type, "ext4")) &&
948 !stat(mnt->mnt_fsname, &dst) &&
949 dst.st_rdev == dev) {
954 if ((!strcmp(mnt->mnt_type, "vfat")) &&
955 !stat(mnt->mnt_fsname, &dst) &&
956 dst.st_rdev == dev) {
962 devname = strdup(mnt->mnt_fsname);
972 static const char *get_devname(const char *path)
974 const char *devname = NULL;
978 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
979 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
982 if (statfs(path, &sfs)) {
983 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
988 /* klibc doesn't have getmntent and friends; instead, just create
989 a new device with the appropriate device type */
990 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
991 major(st.st_dev), minor(st.st_dev));
993 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
994 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
998 atexit(device_cleanup); /* unlink the device node on exit */
999 devname = devname_buf;
1003 /* check /etc/mtab first, since btrfs subvol info is only in here */
1004 devname = find_device("/etc/mtab", st.st_dev);
1005 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
1009 strcpy(parent, path);
1010 tmp = strrchr(parent, '/');
1013 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
1014 devname = get_devname(parent);
1019 /* Didn't find it in /etc/mtab, try /proc/mounts */
1020 devname = find_device("/proc/mounts", st.st_dev);
1023 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
1027 fprintf(stderr, "%s is device %s\n", path, devname);
1032 static int open_device(const char *path, struct stat *st, const char **_devname)
1035 const char *devname = NULL;
1039 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
1040 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
1044 if (statfs(path, &sfs)) {
1045 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
1048 if (sfs.f_type == EXT2_SUPER_MAGIC)
1050 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
1052 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
1056 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
1062 devname = get_devname(path);
1064 *_devname = devname;
1066 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
1067 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
1071 /* Verify that the device we opened is the device intended */
1072 if (validate_device(path, devfd)) {
1073 fprintf(stderr, "%s: path %s doesn't match device %s\n",
1074 program, path, devname);
1081 int install_loader(const char *path, int update_only)
1083 struct stat st, fst;
1085 const char *devname;
1087 devfd = open_device(path, &st, &devname);
1091 if (update_only && !already_installed(devfd)) {
1092 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
1098 /* Read a pre-existing ADV, if already installed */
1100 syslinux_reset_adv(syslinux_adv);
1101 else if (read_adv(path, devfd) < 0) {
1105 if (modify_adv() < 0) {
1110 /* Install extlinux.sys */
1111 if (install_file(path, devfd, &fst)) {
1115 if (fst.st_dev != st.st_dev) {
1116 fprintf(stderr, "%s: file system changed under us - aborting!\n",
1123 rv = install_bootblock(devfd, devname);
1131 * Modify the ADV of an existing installation
1133 int modify_existing_adv(const char *path)
1137 devfd = open_device(path, NULL, NULL);
1142 syslinux_reset_adv(syslinux_adv);
1143 else if (read_adv(path, devfd) < 0) {
1147 if (modify_adv() < 0) {
1151 if (write_adv(path, devfd) < 0) {
1159 int main(int argc, char *argv[])
1162 const char *directory;
1163 int update_only = -1;
1167 while ((o = getopt_long(argc, argv, short_options,
1168 long_options, NULL)) != EOF) {
1175 opt.sectors = strtoul(optarg, NULL, 0);
1176 if (opt.sectors < 1 || opt.sectors > 63) {
1178 "%s: invalid number of sectors: %u (must be 1-63)\n",
1179 program, opt.sectors);
1184 opt.heads = strtoul(optarg, NULL, 0);
1185 if (opt.heads < 1 || opt.heads > 256) {
1187 "%s: invalid number of heads: %u (must be 1-256)\n",
1188 program, opt.heads);
1196 opt.stupid_mode = 1;
1209 opt.set_once = optarg;
1218 fputs("extlinux " VERSION_STR
1219 " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr);
1226 directory = argv[optind];
1231 if (update_only == -1) {
1232 if (opt.reset_adv || opt.set_once) {
1233 return modify_existing_adv(directory);
1239 return install_loader(directory, update_only);