1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Install the extlinux boot block on an fat, ext2/3/4 and btrfs filesystem
20 #define _GNU_SOURCE /* Enable everything */
22 /* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
38 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/mount.h>
44 #include <linux/fd.h> /* Floppy geometry */
45 #include <linux/hdreg.h> /* Hard disk geometry */
46 #define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */
47 #include <linux/fs.h> /* FIGETBSZ, FIBMAP */
48 #include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */
49 #ifndef FAT_IOCTL_SET_ATTRIBUTES
50 # define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
53 #undef SECTOR_SIZE /* Garbage from <linux/msdos_fs.h> */
58 #include "../version.h"
60 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
62 #include "syslxopt.h" /* unified options */
65 # define dprintf printf
67 # define dprintf(...) ((void)0)
70 #if defined(__linux__) && !defined(BLKGETSIZE64)
71 /* This takes a u64, but the size field says size_t. Someone screwed big. */
72 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
75 #ifndef EXT2_SUPER_OFFSET
76 #define EXT2_SUPER_OFFSET 1024
79 /* the btrfs partition first 64K blank area is used to store boot sector and
80 boot image, the boot sector is from 0~512, the boot image starts at 2K */
81 #define BTRFS_EXTLINUX_OFFSET (2*1024)
82 #define BTRFS_SUBVOL_OPT "subvol="
83 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
84 static char subvol[BTRFS_SUBVOL_MAX];
89 extern unsigned char extlinux_bootsect[];
90 extern unsigned int extlinux_bootsect_len;
93 #define boot_block extlinux_bootsect
94 #define boot_block_len extlinux_bootsect_len
99 extern unsigned char extlinux_image[];
100 extern unsigned int extlinux_image_len;
102 #undef boot_image_len
103 #define boot_image extlinux_image
104 #define boot_image_len extlinux_image_len
106 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
109 * Get the size of a block device
111 uint64_t get_size(int devfd)
118 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
121 if (!ioctl(devfd, BLKGETSIZE, §s))
122 return (uint64_t) sects << 9;
123 else if (!fstat(devfd, &st) && st.st_size)
130 * Get device geometry and partition offset
132 struct geometry_table {
134 struct hd_geometry g;
137 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
138 (x/64/32) is the final fallback. I don't know what LS-240 has
139 as its geometry, since I don't have one and don't know anyone that does,
140 and Google wasn't helpful... */
141 static const struct geometry_table standard_geometries[] = {
142 {360 * 1024, {2, 9, 40, 0}},
143 {720 * 1024, {2, 9, 80, 0}},
144 {1200 * 1024, {2, 15, 80, 0}},
145 {1440 * 1024, {2, 18, 80, 0}},
146 {1680 * 1024, {2, 21, 80, 0}},
147 {1722 * 1024, {2, 21, 80, 0}},
148 {2880 * 1024, {2, 36, 80, 0}},
149 {3840 * 1024, {2, 48, 80, 0}},
150 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
154 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
156 struct floppy_struct fd_str;
157 const struct geometry_table *gp;
159 memset(geo, 0, sizeof *geo);
161 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
163 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
164 geo->heads = fd_str.head;
165 geo->sectors = fd_str.sect;
166 geo->cylinders = fd_str.track;
171 /* Didn't work. Let's see if this is one of the standard geometries */
172 for (gp = standard_geometries; gp->bytes; gp++) {
173 if (gp->bytes == totalbytes) {
174 memcpy(geo, &gp->g, sizeof *geo);
179 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
180 what zipdisks use, so this would help if someone has a USB key that
181 they're booting in USB-ZIP mode. */
183 geo->heads = opt.heads ? : 64;
184 geo->sectors = opt.sectors ? : 32;
185 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
188 if (!opt.sectors && !opt.heads)
190 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
191 " (on hard disks, this is usually harmless.)\n",
192 geo->heads, geo->sectors);
198 * Generate sector extents
200 static void generate_extents(struct syslinux_extent *ex, int nptrs,
201 const sector_t *sectp, int nsect)
203 uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
208 len = lba = base = 0;
210 memset(ex, 0, nptrs * sizeof *ex);
215 if (len && sect == lba + len &&
216 ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
217 /* We can add to the current extent */
223 set_64(&ex->lba, lba);
224 set_16(&ex->len, len);
238 set_64(&ex->lba, lba);
239 set_16(&ex->len, len);
245 * Query the device geometry and put it into the boot sector.
246 * Map the file and put the map in the boot sector and file.
247 * Stick the "current directory" inode number into the file.
249 * Returns the number of modified bytes in the boot file.
251 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
253 struct stat dirst, xdst;
254 struct hd_geometry geo;
256 uint64_t totalbytes, totalsectors;
259 struct boot_sector *bs;
260 struct patch_area *patcharea;
261 struct syslinux_extent *ex;
264 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
265 char *dirpath, *subpath, *xdirpath, *xsubpath;
268 dirpath = realpath(dir, NULL);
269 if (!dirpath || stat(dir, &dirst)) {
270 perror("accessing install directory");
271 exit(255); /* This should never happen */
274 if (lstat(dirpath, &xdst) ||
275 dirst.st_ino != xdst.st_ino ||
276 dirst.st_dev != xdst.st_dev) {
277 perror("realpath returned nonsense");
281 subpath = strchr(dirpath, '\0');
283 if (*subpath == '/') {
284 if (subpath > dirpath) {
286 xsubpath = subpath+1;
292 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
293 subpath = strchr(subpath+1, '/');
295 subpath = "/"; /* It's the root of the filesystem */
301 if (subpath == dirpath)
307 /* Now subpath should contain the path relative to the fs base */
308 dprintf("subpath = %s\n", subpath);
310 totalbytes = get_size(devfd);
311 get_geometry(devfd, totalbytes, &geo);
314 geo.heads = opt.heads;
316 geo.sectors = opt.sectors;
318 /* Patch this into a fake FAT superblock. This isn't because
319 FAT is a good format in any way, it's because it lets the
320 early bootstrap share code with the FAT version. */
321 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
323 bs = (struct boot_sector *)boot_block;
325 totalsectors = totalbytes >> SECTOR_SHIFT;
326 if (totalsectors >= 65536) {
327 set_16(&bs->bsSectors, 0);
329 set_16(&bs->bsSectors, totalsectors);
331 set_32(&bs->bsHugeSectors, totalsectors);
333 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
334 set_16(&bs->bsSecPerTrack, geo.sectors);
335 set_16(&bs->bsHeads, geo.heads);
336 set_32(&bs->bsHiddenSecs, geo.start);
338 /* If we're in RAID mode then patch the appropriate instruction;
339 either way write the proper boot signature */
340 i = get_16(&bs->bsSignature);
342 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
344 set_16(&bs->bsSignature, 0xAA55);
346 /* Construct the boot file */
348 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
349 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
350 nsect += 2; /* Two sectors for the ADV */
351 sectp = alloca(sizeof(sector_t) * nsect);
352 if (fs_type == EXT2 || fs_type == VFAT) {
353 if (sectmap(fd, sectp, nsect)) {
357 } else if (fs_type == BTRFS) {
360 for (i = 0; i < nsect; i++)
361 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
364 /* First sector need pointer in boot sector */
365 set_64(&bs->NextSector, *sectp++);
367 /* Search for LDLINUX_MAGIC to find the patch area */
368 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
369 patcharea = (struct patch_area *)wp;
371 /* Set up the totals */
372 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
373 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
374 set_16(&patcharea->adv_sectors, 2);
375 set_32(&patcharea->dwords, dw);
379 set_16(&patcharea->maxtransfer, 1);
381 /* Set the sector extents */
382 secptroffset = get_16(&patcharea->secptroffset);
383 ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
384 nptrs = get_16(&patcharea->secptrcnt);
387 /* Not necessarily an error in this case, but a general problem */
388 fprintf(stderr, "Insufficient extent space, build error!\n");
392 /* -1 for the pointer in the boot sector, -2 for the two ADVs */
393 generate_extents(ex, nptrs, sectp, nsect-1-2);
396 advptrs = (uint64_t *)((char *)boot_image +
397 get_16(&patcharea->advptroffset));
398 set_64(&advptrs[0], sectp[nsect-1-2]);
399 set_64(&advptrs[1], sectp[nsect-1-1]);
401 /* Poke in the base directory path */
402 diroffset = get_16(&patcharea->diroffset);
403 dirlen = get_16(&patcharea->dirlen);
404 if (dirlen <= strlen(subpath)) {
405 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
408 strncpy((char *)boot_image + diroffset, subpath, dirlen);
411 /* write subvol info if we have */
412 subvoloffset = get_16(&patcharea->subvoloffset);
413 subvollen = get_16(&patcharea->subvollen);
414 if (subvollen <= strlen(subvol)) {
415 fprintf(stderr, "Subvol name too long... aborting install!\n");
418 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
420 /* Now produce a checksum */
421 set_32(&patcharea->checksum, 0);
423 csum = LDLINUX_MAGIC;
424 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
425 csum -= get_32(wp); /* Negative checksum */
427 set_32(&patcharea->checksum, csum);
430 * Assume all bytes modified. This can be optimized at the expense
431 * of keeping track of what the highest modified address ever was.
437 * Make any user-specified ADV modifications
444 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
445 fprintf(stderr, "%s: not enough space for boot-once command\n",
451 if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
452 fprintf(stderr, "%s: not enough space for menu-save label\n",
462 * Install the boot block on the specified device.
463 * Must be run AFTER install_file()!
465 int install_bootblock(int fd, const char *device)
467 struct ext2_super_block sb;
468 struct btrfs_super_block sb2;
469 struct boot_sector sb3;
472 if (fs_type == EXT2) {
473 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
474 perror("reading superblock");
477 if (sb.s_magic == EXT2_SUPER_MAGIC)
479 } else if (fs_type == BTRFS) {
480 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
482 perror("reading superblock");
485 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
487 } else if (fs_type == VFAT) {
488 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
489 perror("reading fat superblock");
492 if (sb3.bsResSectors && sb3.bsFATs &&
493 (strstr(sb3.bs16.FileSysType, "FAT") ||
494 strstr(sb3.bs32.FileSysType, "FAT")))
498 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
502 if (fs_type == VFAT) {
503 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
504 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
505 xpwrite(fd, &bs->bsCode, bsCodeLen,
506 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
507 perror("writing fat bootblock");
511 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
512 perror("writing bootblock");
520 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
523 int fd = -1, dirfd = -1;
526 asprintf(&file, "%s%sextlinux.sys",
527 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
533 dirfd = open(path, O_RDONLY | O_DIRECTORY);
539 fd = open(file, O_RDONLY);
541 if (errno != ENOENT) {
546 clear_attributes(fd);
550 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
551 S_IRUSR | S_IRGRP | S_IROTH);
557 /* Write it the first time */
558 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
559 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
560 boot_image_len) != 2 * ADV_SIZE) {
561 fprintf(stderr, "%s: write failure on %s\n", program, file);
565 /* Map the file, and patch the initial sector accordingly */
566 modbytes = patch_file_and_bootblock(fd, path, devfd);
568 /* Write the patch area again - this relies on the file being
569 overwritten in place! */
570 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
571 fprintf(stderr, "%s: write failure on %s\n", program, file);
575 /* Attempt to set immutable flag and remove all write access */
576 /* Only set immutable flag if file is owned by root */
579 if (fstat(fd, rst)) {
597 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
598 is not managered by btrfs tree, so actually this is not installed as files.
599 since the cow feature of btrfs will move the extlinux.sys every where */
600 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
602 patch_file_and_bootblock(-1, path, devfd);
603 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
605 perror("writing bootblock");
608 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
609 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
610 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
611 perror("writing adv");
614 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
615 if (stat(path, rst)) {
622 int install_file(const char *path, int devfd, struct stat *rst)
624 if (fs_type == EXT2 || fs_type == VFAT)
625 return ext2_fat_install_file(path, devfd, rst);
626 else if (fs_type == BTRFS)
627 return btrfs_install_file(path, devfd, rst);
631 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
632 sector; this is consistent with FAT filesystems. */
633 int already_installed(int devfd)
637 xpread(devfd, buffer, 8, 3);
638 return !memcmp(buffer, "EXTLINUX", 8);
642 static char devname_buf[64];
644 static void device_cleanup(void)
650 /* Verify that a device fd and a pathname agree.
651 Return 0 on valid, -1 on error. */
652 static int validate_device(const char *path, int devfd)
654 struct stat pst, dst;
657 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
659 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
660 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
662 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
666 static const char *find_device(const char *mtab_file, dev_t dev)
671 const char *devname = NULL;
674 mtab = setmntent(mtab_file, "r");
679 while ((mnt = getmntent(mtab))) {
680 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
683 if (!strcmp(mnt->mnt_type, "btrfs") &&
684 !stat(mnt->mnt_dir, &dst) &&
686 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
692 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
693 tmp = strchr(subvol, 32);
697 break; /* should break and let upper layer try again */
703 if ((!strcmp(mnt->mnt_type, "ext2") ||
704 !strcmp(mnt->mnt_type, "ext3") ||
705 !strcmp(mnt->mnt_type, "ext4")) &&
706 !stat(mnt->mnt_fsname, &dst) &&
707 dst.st_rdev == dev) {
712 if ((!strcmp(mnt->mnt_type, "vfat")) &&
713 !stat(mnt->mnt_fsname, &dst) &&
714 dst.st_rdev == dev) {
722 devname = strdup(mnt->mnt_fsname);
732 static const char *get_devname(const char *path)
734 const char *devname = NULL;
738 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
739 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
742 if (statfs(path, &sfs)) {
743 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
748 /* klibc doesn't have getmntent and friends; instead, just create
749 a new device with the appropriate device type */
750 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
751 major(st.st_dev), minor(st.st_dev));
753 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
754 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
758 atexit(device_cleanup); /* unlink the device node on exit */
759 devname = devname_buf;
763 /* check /etc/mtab first, since btrfs subvol info is only in here */
764 devname = find_device("/etc/mtab", st.st_dev);
765 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
769 strcpy(parent, path);
770 tmp = strrchr(parent, '/');
773 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
774 devname = get_devname(parent);
779 /* Didn't find it in /etc/mtab, try /proc/mounts */
780 devname = find_device("/proc/mounts", st.st_dev);
783 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
787 fprintf(stderr, "%s is device %s\n", path, devname);
792 static int open_device(const char *path, struct stat *st, const char **_devname)
795 const char *devname = NULL;
799 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
800 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
804 if (statfs(path, &sfs)) {
805 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
808 if (sfs.f_type == EXT2_SUPER_MAGIC)
810 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
812 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
816 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
822 devname = get_devname(path);
826 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
827 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
831 /* Verify that the device we opened is the device intended */
832 if (validate_device(path, devfd)) {
833 fprintf(stderr, "%s: path %s doesn't match device %s\n",
834 program, path, devname);
841 static int ext_read_adv(const char *path, const char *cfg, int devfd)
843 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
844 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
845 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
846 perror("btrfs writing adv");
851 return read_adv(path, cfg);
854 static int ext_write_adv(const char *path, const char *cfg, int devfd)
856 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
857 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
858 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
859 perror("writing adv");
864 return write_adv(path, cfg);
867 int install_loader(const char *path, int update_only)
873 devfd = open_device(path, &st, &devname);
877 if (update_only && !already_installed(devfd)) {
878 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
884 /* Read a pre-existing ADV, if already installed */
886 syslinux_reset_adv(syslinux_adv);
887 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
891 if (modify_adv() < 0) {
896 /* Install extlinux.sys */
897 if (install_file(path, devfd, &fst)) {
901 if (fst.st_dev != st.st_dev) {
902 fprintf(stderr, "%s: file system changed under us - aborting!\n",
909 rv = install_bootblock(devfd, devname);
917 * Modify the ADV of an existing installation
919 int modify_existing_adv(const char *path)
923 devfd = open_device(path, NULL, NULL);
928 syslinux_reset_adv(syslinux_adv);
929 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
933 if (modify_adv() < 0) {
937 if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
945 int main(int argc, char *argv[])
947 parse_options(argc, argv, MODE_EXTLINUX);
952 if (opt.update_only == -1) {
953 if (opt.reset_adv || opt.set_once || opt.menu_save)
954 return modify_existing_adv(opt.directory);
956 usage(EX_USAGE, MODE_EXTLINUX);
959 return install_loader(opt.directory, opt.update_only);