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 "linuxioctl.h"
48 #include "../version.h"
50 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
52 #include "syslxopt.h" /* unified options */
55 # define dprintf printf
57 # define dprintf(...) ((void)0)
60 #if defined(__linux__) && !defined(BLKGETSIZE64)
61 /* This takes a u64, but the size field says size_t. Someone screwed big. */
62 # define BLKGETSIZE64 _IOR(0x12,114,size_t)
65 #ifndef EXT2_SUPER_OFFSET
66 #define EXT2_SUPER_OFFSET 1024
69 /* the btrfs partition first 64K blank area is used to store boot sector and
70 boot image, the boot sector is from 0~512, the boot image starts at 2K */
71 #define BTRFS_EXTLINUX_OFFSET (2*1024)
72 #define BTRFS_SUBVOL_OPT "subvol="
73 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
74 static char subvol[BTRFS_SUBVOL_MAX];
79 extern unsigned char extlinux_bootsect[];
80 extern unsigned int extlinux_bootsect_len;
83 #define boot_block extlinux_bootsect
84 #define boot_block_len extlinux_bootsect_len
89 extern unsigned char extlinux_image[];
90 extern unsigned int extlinux_image_len;
93 #define boot_image extlinux_image
94 #define boot_image_len extlinux_image_len
96 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
99 * Get the size of a block device
101 uint64_t get_size(int devfd)
108 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
111 if (!ioctl(devfd, BLKGETSIZE, §s))
112 return (uint64_t) sects << 9;
113 else if (!fstat(devfd, &st) && st.st_size)
120 * Get device geometry and partition offset
122 struct geometry_table {
124 struct hd_geometry g;
127 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
128 (x/64/32) is the final fallback. I don't know what LS-240 has
129 as its geometry, since I don't have one and don't know anyone that does,
130 and Google wasn't helpful... */
131 static const struct geometry_table standard_geometries[] = {
132 {360 * 1024, {2, 9, 40, 0}},
133 {720 * 1024, {2, 9, 80, 0}},
134 {1200 * 1024, {2, 15, 80, 0}},
135 {1440 * 1024, {2, 18, 80, 0}},
136 {1680 * 1024, {2, 21, 80, 0}},
137 {1722 * 1024, {2, 21, 80, 0}},
138 {2880 * 1024, {2, 36, 80, 0}},
139 {3840 * 1024, {2, 48, 80, 0}},
140 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
144 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
146 struct floppy_struct fd_str;
147 const struct geometry_table *gp;
149 memset(geo, 0, sizeof *geo);
151 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
153 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
154 geo->heads = fd_str.head;
155 geo->sectors = fd_str.sect;
156 geo->cylinders = fd_str.track;
161 /* Didn't work. Let's see if this is one of the standard geometries */
162 for (gp = standard_geometries; gp->bytes; gp++) {
163 if (gp->bytes == totalbytes) {
164 memcpy(geo, &gp->g, sizeof *geo);
169 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
170 what zipdisks use, so this would help if someone has a USB key that
171 they're booting in USB-ZIP mode. */
173 geo->heads = opt.heads ? : 64;
174 geo->sectors = opt.sectors ? : 32;
175 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
178 if (!opt.sectors && !opt.heads)
180 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
181 " (on hard disks, this is usually harmless.)\n",
182 geo->heads, geo->sectors);
188 * Generate sector extents
190 static void generate_extents(struct syslinux_extent *ex, int nptrs,
191 const sector_t *sectp, int nsect)
193 uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
198 len = lba = base = 0;
200 memset(ex, 0, nptrs * sizeof *ex);
205 if (len && sect == lba + len &&
206 ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
207 /* We can add to the current extent */
213 set_64(&ex->lba, lba);
214 set_16(&ex->len, len);
228 set_64(&ex->lba, lba);
229 set_16(&ex->len, len);
235 * Query the device geometry and put it into the boot sector.
236 * Map the file and put the map in the boot sector and file.
237 * Stick the "current directory" inode number into the file.
239 * Returns the number of modified bytes in the boot file.
241 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
243 struct stat dirst, xdst;
244 struct hd_geometry geo;
246 uint64_t totalbytes, totalsectors;
249 struct boot_sector *bs;
250 struct patch_area *patcharea;
251 struct syslinux_extent *ex;
254 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
255 char *dirpath, *subpath, *xdirpath, *xsubpath;
258 dirpath = realpath(dir, NULL);
259 if (!dirpath || stat(dir, &dirst)) {
260 perror("accessing install directory");
261 exit(255); /* This should never happen */
264 if (lstat(dirpath, &xdst) ||
265 dirst.st_ino != xdst.st_ino ||
266 dirst.st_dev != xdst.st_dev) {
267 perror("realpath returned nonsense");
271 subpath = strchr(dirpath, '\0');
273 if (*subpath == '/') {
274 if (subpath > dirpath) {
276 xsubpath = subpath+1;
282 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
283 subpath = strchr(subpath+1, '/');
285 subpath = "/"; /* It's the root of the filesystem */
291 if (subpath == dirpath)
297 /* Now subpath should contain the path relative to the fs base */
298 dprintf("subpath = %s\n", subpath);
300 totalbytes = get_size(devfd);
301 get_geometry(devfd, totalbytes, &geo);
304 geo.heads = opt.heads;
306 geo.sectors = opt.sectors;
308 /* Patch this into a fake FAT superblock. This isn't because
309 FAT is a good format in any way, it's because it lets the
310 early bootstrap share code with the FAT version. */
311 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
313 bs = (struct boot_sector *)boot_block;
315 totalsectors = totalbytes >> SECTOR_SHIFT;
316 if (totalsectors >= 65536) {
317 set_16(&bs->bsSectors, 0);
319 set_16(&bs->bsSectors, totalsectors);
321 set_32(&bs->bsHugeSectors, totalsectors);
323 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
324 set_16(&bs->bsSecPerTrack, geo.sectors);
325 set_16(&bs->bsHeads, geo.heads);
326 set_32(&bs->bsHiddenSecs, geo.start);
328 /* If we're in RAID mode then patch the appropriate instruction;
329 either way write the proper boot signature */
330 i = get_16(&bs->bsSignature);
332 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
334 set_16(&bs->bsSignature, 0xAA55);
336 /* Construct the boot file */
338 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
339 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
340 nsect += 2; /* Two sectors for the ADV */
341 sectp = alloca(sizeof(sector_t) * nsect);
342 if (fs_type == EXT2 || fs_type == VFAT) {
343 if (sectmap(fd, sectp, nsect)) {
347 } else if (fs_type == BTRFS) {
350 for (i = 0; i < nsect; i++)
351 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
354 /* First sector need pointer in boot sector */
355 set_64(&bs->NextSector, *sectp++);
357 /* Search for LDLINUX_MAGIC to find the patch area */
358 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
359 patcharea = (struct patch_area *)wp;
361 /* Set up the totals */
362 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
363 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
364 set_16(&patcharea->adv_sectors, 2);
365 set_32(&patcharea->dwords, dw);
369 set_16(&patcharea->maxtransfer, 1);
371 /* Set the sector extents */
372 secptroffset = get_16(&patcharea->secptroffset);
373 ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
374 nptrs = get_16(&patcharea->secptrcnt);
377 /* Not necessarily an error in this case, but a general problem */
378 fprintf(stderr, "Insufficient extent space, build error!\n");
382 /* -1 for the pointer in the boot sector, -2 for the two ADVs */
383 generate_extents(ex, nptrs, sectp, nsect-1-2);
386 advptrs = (uint64_t *)((char *)boot_image +
387 get_16(&patcharea->advptroffset));
388 set_64(&advptrs[0], sectp[nsect-1-2]);
389 set_64(&advptrs[1], sectp[nsect-1-1]);
391 /* Poke in the base directory path */
392 diroffset = get_16(&patcharea->diroffset);
393 dirlen = get_16(&patcharea->dirlen);
394 if (dirlen <= strlen(subpath)) {
395 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
398 strncpy((char *)boot_image + diroffset, subpath, dirlen);
401 /* write subvol info if we have */
402 subvoloffset = get_16(&patcharea->subvoloffset);
403 subvollen = get_16(&patcharea->subvollen);
404 if (subvollen <= strlen(subvol)) {
405 fprintf(stderr, "Subvol name too long... aborting install!\n");
408 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
410 /* Now produce a checksum */
411 set_32(&patcharea->checksum, 0);
413 csum = LDLINUX_MAGIC;
414 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
415 csum -= get_32(wp); /* Negative checksum */
417 set_32(&patcharea->checksum, csum);
420 * Assume all bytes modified. This can be optimized at the expense
421 * of keeping track of what the highest modified address ever was.
427 * Make any user-specified ADV modifications
434 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
435 fprintf(stderr, "%s: not enough space for boot-once command\n",
441 if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
442 fprintf(stderr, "%s: not enough space for menu-save label\n",
452 * Install the boot block on the specified device.
453 * Must be run AFTER install_file()!
455 int install_bootblock(int fd, const char *device)
457 struct ext2_super_block sb;
458 struct btrfs_super_block sb2;
459 struct boot_sector sb3;
462 if (fs_type == EXT2) {
463 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
464 perror("reading superblock");
467 if (sb.s_magic == EXT2_SUPER_MAGIC)
469 } else if (fs_type == BTRFS) {
470 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
472 perror("reading superblock");
475 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
477 } else if (fs_type == VFAT) {
478 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
479 perror("reading fat superblock");
482 if (sb3.bsResSectors && sb3.bsFATs &&
483 (strstr(sb3.bs16.FileSysType, "FAT") ||
484 strstr(sb3.bs32.FileSysType, "FAT")))
488 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
492 if (fs_type == VFAT) {
493 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
494 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
495 xpwrite(fd, &bs->bsCode, bsCodeLen,
496 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
497 perror("writing fat bootblock");
501 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
502 perror("writing bootblock");
510 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
513 int fd = -1, dirfd = -1;
516 asprintf(&file, "%s%sextlinux.sys",
517 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
523 dirfd = open(path, O_RDONLY | O_DIRECTORY);
529 fd = open(file, O_RDONLY);
531 if (errno != ENOENT) {
536 clear_attributes(fd);
540 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
541 S_IRUSR | S_IRGRP | S_IROTH);
547 /* Write it the first time */
548 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
549 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
550 boot_image_len) != 2 * ADV_SIZE) {
551 fprintf(stderr, "%s: write failure on %s\n", program, file);
555 /* Map the file, and patch the initial sector accordingly */
556 modbytes = patch_file_and_bootblock(fd, path, devfd);
558 /* Write the patch area again - this relies on the file being
559 overwritten in place! */
560 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
561 fprintf(stderr, "%s: write failure on %s\n", program, file);
565 /* Attempt to set immutable flag and remove all write access */
566 /* Only set immutable flag if file is owned by root */
569 if (fstat(fd, rst)) {
587 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
588 is not managered by btrfs tree, so actually this is not installed as files.
589 since the cow feature of btrfs will move the extlinux.sys every where */
590 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
592 patch_file_and_bootblock(-1, path, devfd);
593 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
595 perror("writing bootblock");
598 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
599 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
600 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
601 perror("writing adv");
604 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
605 if (stat(path, rst)) {
612 int install_file(const char *path, int devfd, struct stat *rst)
614 if (fs_type == EXT2 || fs_type == VFAT)
615 return ext2_fat_install_file(path, devfd, rst);
616 else if (fs_type == BTRFS)
617 return btrfs_install_file(path, devfd, rst);
621 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
622 sector; this is consistent with FAT filesystems. */
623 int already_installed(int devfd)
627 xpread(devfd, buffer, 8, 3);
628 return !memcmp(buffer, "EXTLINUX", 8);
632 static char devname_buf[64];
634 static void device_cleanup(void)
640 /* Verify that a device fd and a pathname agree.
641 Return 0 on valid, -1 on error. */
642 static int validate_device(const char *path, int devfd)
644 struct stat pst, dst;
647 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
649 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
650 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
652 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
656 static const char *find_device(const char *mtab_file, dev_t dev)
661 const char *devname = NULL;
664 mtab = setmntent(mtab_file, "r");
669 while ((mnt = getmntent(mtab))) {
670 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
673 if (!strcmp(mnt->mnt_type, "btrfs") &&
674 !stat(mnt->mnt_dir, &dst) &&
676 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
682 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
683 tmp = strchr(subvol, 32);
687 break; /* should break and let upper layer try again */
693 if ((!strcmp(mnt->mnt_type, "ext2") ||
694 !strcmp(mnt->mnt_type, "ext3") ||
695 !strcmp(mnt->mnt_type, "ext4")) &&
696 !stat(mnt->mnt_fsname, &dst) &&
697 dst.st_rdev == dev) {
702 if ((!strcmp(mnt->mnt_type, "vfat")) &&
703 !stat(mnt->mnt_fsname, &dst) &&
704 dst.st_rdev == dev) {
712 devname = strdup(mnt->mnt_fsname);
722 static const char *get_devname(const char *path)
724 const char *devname = NULL;
728 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
729 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
732 if (statfs(path, &sfs)) {
733 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
738 /* klibc doesn't have getmntent and friends; instead, just create
739 a new device with the appropriate device type */
740 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
741 major(st.st_dev), minor(st.st_dev));
743 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
744 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
748 atexit(device_cleanup); /* unlink the device node on exit */
749 devname = devname_buf;
753 /* check /etc/mtab first, since btrfs subvol info is only in here */
754 devname = find_device("/etc/mtab", st.st_dev);
755 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
759 strcpy(parent, path);
760 tmp = strrchr(parent, '/');
763 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
764 devname = get_devname(parent);
769 /* Didn't find it in /etc/mtab, try /proc/mounts */
770 devname = find_device("/proc/mounts", st.st_dev);
773 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
777 fprintf(stderr, "%s is device %s\n", path, devname);
782 static int open_device(const char *path, struct stat *st, const char **_devname)
785 const char *devname = NULL;
789 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
790 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
794 if (statfs(path, &sfs)) {
795 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
798 if (sfs.f_type == EXT2_SUPER_MAGIC)
800 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
802 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
806 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
812 devname = get_devname(path);
816 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
817 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
821 /* Verify that the device we opened is the device intended */
822 if (validate_device(path, devfd)) {
823 fprintf(stderr, "%s: path %s doesn't match device %s\n",
824 program, path, devname);
831 static int ext_read_adv(const char *path, const char *cfg, int devfd)
833 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
834 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
835 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
836 perror("btrfs writing adv");
841 return read_adv(path, cfg);
844 static int ext_write_adv(const char *path, const char *cfg, int devfd)
846 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
847 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
848 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
849 perror("writing adv");
854 return write_adv(path, cfg);
857 int install_loader(const char *path, int update_only)
863 devfd = open_device(path, &st, &devname);
867 if (update_only && !already_installed(devfd)) {
868 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
874 /* Read a pre-existing ADV, if already installed */
876 syslinux_reset_adv(syslinux_adv);
877 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
881 if (modify_adv() < 0) {
886 /* Install extlinux.sys */
887 if (install_file(path, devfd, &fst)) {
891 if (fst.st_dev != st.st_dev) {
892 fprintf(stderr, "%s: file system changed under us - aborting!\n",
899 rv = install_bootblock(devfd, devname);
907 * Modify the ADV of an existing installation
909 int modify_existing_adv(const char *path)
913 devfd = open_device(path, NULL, NULL);
918 syslinux_reset_adv(syslinux_adv);
919 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
923 if (modify_adv() < 0) {
927 if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
935 int main(int argc, char *argv[])
937 parse_options(argc, argv, MODE_EXTLINUX);
942 if (opt.update_only == -1) {
943 if (opt.reset_adv || opt.set_once || opt.menu_save)
944 return modify_existing_adv(opt.directory);
946 usage(EX_USAGE, MODE_EXTLINUX);
949 return install_loader(opt.directory, opt.update_only);