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];
88 extern unsigned char extlinux_bootsect[];
89 extern unsigned int extlinux_bootsect_len;
90 #define boot_block extlinux_bootsect
91 #define boot_block_len extlinux_bootsect_len
96 extern unsigned char extlinux_image[];
97 extern unsigned int extlinux_image_len;
98 #define boot_image extlinux_image
99 #define boot_image_len extlinux_image_len
101 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
104 * Get the size of a block device
106 uint64_t get_size(int devfd)
113 if (!ioctl(devfd, BLKGETSIZE64, &bytes))
116 if (!ioctl(devfd, BLKGETSIZE, §s))
117 return (uint64_t) sects << 9;
118 else if (!fstat(devfd, &st) && st.st_size)
125 * Get device geometry and partition offset
127 struct geometry_table {
129 struct hd_geometry g;
132 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
133 (x/64/32) is the final fallback. I don't know what LS-240 has
134 as its geometry, since I don't have one and don't know anyone that does,
135 and Google wasn't helpful... */
136 static const struct geometry_table standard_geometries[] = {
137 {360 * 1024, {2, 9, 40, 0}},
138 {720 * 1024, {2, 9, 80, 0}},
139 {1200 * 1024, {2, 15, 80, 0}},
140 {1440 * 1024, {2, 18, 80, 0}},
141 {1680 * 1024, {2, 21, 80, 0}},
142 {1722 * 1024, {2, 21, 80, 0}},
143 {2880 * 1024, {2, 36, 80, 0}},
144 {3840 * 1024, {2, 48, 80, 0}},
145 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
149 int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
151 struct floppy_struct fd_str;
152 const struct geometry_table *gp;
154 memset(geo, 0, sizeof *geo);
156 if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
158 } else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
159 geo->heads = fd_str.head;
160 geo->sectors = fd_str.sect;
161 geo->cylinders = fd_str.track;
166 /* Didn't work. Let's see if this is one of the standard geometries */
167 for (gp = standard_geometries; gp->bytes; gp++) {
168 if (gp->bytes == totalbytes) {
169 memcpy(geo, &gp->g, sizeof *geo);
174 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
175 what zipdisks use, so this would help if someone has a USB key that
176 they're booting in USB-ZIP mode. */
178 geo->heads = opt.heads ? : 64;
179 geo->sectors = opt.sectors ? : 32;
180 geo->cylinders = totalbytes / (geo->heads * geo->sectors << SECTOR_SHIFT);
183 if (!opt.sectors && !opt.heads)
185 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
186 " (on hard disks, this is usually harmless.)\n",
187 geo->heads, geo->sectors);
193 * Query the device geometry and put it into the boot sector.
194 * Map the file and put the map in the boot sector and file.
195 * Stick the "current directory" inode number into the file.
197 * Returns the number of modified bytes in the boot file.
199 int patch_file_and_bootblock(int fd, const char *dir, int devfd)
201 struct stat dirst, xdst;
202 struct hd_geometry geo;
204 uint64_t totalbytes, totalsectors;
207 struct boot_sector *bs;
208 struct patch_area *patcharea;
211 int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
212 char *dirpath, *subpath, *xdirpath, *xsubpath;
214 dirpath = realpath(dir, NULL);
215 if (!dirpath || stat(dir, &dirst)) {
216 perror("accessing install directory");
217 exit(255); /* This should never happen */
220 if (lstat(dirpath, &xdst) ||
221 dirst.st_ino != xdst.st_ino ||
222 dirst.st_dev != xdst.st_dev) {
223 perror("realpath returned nonsense");
227 subpath = strchr(dirpath, '\0');
229 if (*subpath == '/') {
230 if (subpath > dirpath) {
232 xsubpath = subpath+1;
238 if (lstat(xdirpath, &xdst) || dirst.st_dev != xdst.st_dev) {
239 subpath = strchr(subpath+1, '/');
241 subpath = "/"; /* It's the root of the filesystem */
247 if (subpath == dirpath)
253 /* Now subpath should contain the path relative to the fs base */
254 dprintf("subpath = %s\n", subpath);
256 totalbytes = get_size(devfd);
257 get_geometry(devfd, totalbytes, &geo);
260 geo.heads = opt.heads;
262 geo.sectors = opt.sectors;
264 /* Patch this into a fake FAT superblock. This isn't because
265 FAT is a good format in any way, it's because it lets the
266 early bootstrap share code with the FAT version. */
267 dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
269 bs = (struct boot_sector *)boot_block;
271 totalsectors = totalbytes >> SECTOR_SHIFT;
272 if (totalsectors >= 65536) {
273 set_16(&bs->bsSectors, 0);
275 set_16(&bs->bsSectors, totalsectors);
277 set_32(&bs->bsHugeSectors, totalsectors);
279 set_16(&bs->bsBytesPerSec, SECTOR_SIZE);
280 set_16(&bs->bsSecPerTrack, geo.sectors);
281 set_16(&bs->bsHeads, geo.heads);
282 set_32(&bs->bsHiddenSecs, geo.start);
284 /* If we're in RAID mode then patch the appropriate instruction;
285 either way write the proper boot signature */
286 i = get_16(&bs->bsSignature);
288 set_16((uint16_t *) (boot_block + i), 0x18CD); /* INT 18h */
290 set_16(&bs->bsSignature, 0xAA55);
292 /* Construct the boot file */
294 dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
295 nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
296 nsect += 2; /* Two sectors for the ADV */
297 sectp = alloca(sizeof(uint32_t) * nsect);
298 if (fs_type == EXT2 || fs_type == VFAT) {
299 if (sectmap(fd, sectp, nsect)) {
303 } else if (fs_type == BTRFS) {
306 for (i = 0; i < nsect; i++)
307 *(sectp + i) = BTRFS_EXTLINUX_OFFSET/SECTOR_SIZE + i;
310 /* First sector need pointer in boot sector */
311 set_32(&bs->NextSector, *sectp++);
314 set_16(&bs->MaxTransfer, 1);
316 /* Search for LDLINUX_MAGIC to find the patch area */
317 for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
318 patcharea = (struct patch_area *)wp;
320 /* Set up the totals */
321 dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
322 set_16(&patcharea->data_sectors, nsect - 2); /* -2 for the ADVs */
323 set_16(&patcharea->adv_sectors, 2);
324 set_32(&patcharea->dwords, dw);
326 /* Set the sector pointers */
327 secptroffset = get_16(&patcharea->secptroffset);
328 wp = (uint32_t *) ((char *)boot_image + secptroffset);
329 nptrs = get_16(&patcharea->secptrcnt);
331 memset(wp, 0, nptrs * 4);
332 while (--nsect) /* the first sector in bs->NextSector */
333 set_32(wp++, *sectp++);
335 /* Poke in the base directory path */
336 diroffset = get_16(&patcharea->diroffset);
337 dirlen = get_16(&patcharea->dirlen);
338 if (dirlen <= strlen(subpath)) {
339 fprintf(stderr, "Subdirectory path too long... aborting install!\n");
342 strncpy((char *)boot_image + diroffset, subpath, dirlen);
345 /* write subvol info if we have */
346 subvoloffset = get_16(&patcharea->subvoloffset);
347 subvollen = get_16(&patcharea->subvollen);
348 if (subvollen <= strlen(subvol)) {
349 fprintf(stderr, "Subvol name too long... aborting install!\n");
352 strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
354 /* Now produce a checksum */
355 set_32(&patcharea->checksum, 0);
357 csum = LDLINUX_MAGIC;
358 for (i = 0, wp = (uint32_t *) boot_image; i < dw; i++, wp++)
359 csum -= get_32(wp); /* Negative checksum */
361 set_32(&patcharea->checksum, csum);
364 * Assume all bytes modified. This can be optimized at the expense
365 * of keeping track of what the highest modified address ever was.
371 * Make any user-specified ADV modifications
378 if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) {
379 fprintf(stderr, "%s: not enough space for boot-once command\n",
385 if (syslinux_setadv(ADV_MENUSAVE, strlen(opt.menu_save), opt.menu_save)) {
386 fprintf(stderr, "%s: not enough space for menu-save label\n",
396 * Install the boot block on the specified device.
397 * Must be run AFTER install_file()!
399 int install_bootblock(int fd, const char *device)
401 struct ext2_super_block sb;
402 struct btrfs_super_block sb2;
403 struct boot_sector sb3;
406 if (fs_type == EXT2) {
407 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
408 perror("reading superblock");
411 if (sb.s_magic == EXT2_SUPER_MAGIC)
413 } else if (fs_type == BTRFS) {
414 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
416 perror("reading superblock");
419 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
421 } else if (fs_type == VFAT) {
422 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
423 perror("reading fat superblock");
426 if (sb3.bsResSectors && sb3.bsFATs &&
427 (strstr(sb3.bs16.FileSysType, "FAT") ||
428 strstr(sb3.bs32.FileSysType, "FAT")))
432 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
436 if (fs_type == VFAT) {
437 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
438 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
439 xpwrite(fd, &bs->bsCode, bsCodeLen,
440 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
441 perror("writing fat bootblock");
445 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
446 perror("writing bootblock");
454 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
457 int fd = -1, dirfd = -1;
460 asprintf(&file, "%s%sextlinux.sys",
461 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
467 dirfd = open(path, O_RDONLY | O_DIRECTORY);
473 fd = open(file, O_RDONLY);
475 if (errno != ENOENT) {
480 clear_attributes(fd);
484 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
485 S_IRUSR | S_IRGRP | S_IROTH);
491 /* Write it the first time */
492 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
493 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
494 boot_image_len) != 2 * ADV_SIZE) {
495 fprintf(stderr, "%s: write failure on %s\n", program, file);
499 /* Map the file, and patch the initial sector accordingly */
500 modbytes = patch_file_and_bootblock(fd, path, devfd);
502 /* Write the patch area again - this relies on the file being
503 overwritten in place! */
504 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
505 fprintf(stderr, "%s: write failure on %s\n", program, file);
509 /* Attempt to set immutable flag and remove all write access */
510 /* Only set immutable flag if file is owned by root */
513 if (fstat(fd, rst)) {
531 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
532 is not managered by btrfs tree, so actually this is not installed as files.
533 since the cow feature of btrfs will move the extlinux.sys every where */
534 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
536 patch_file_and_bootblock(-1, path, devfd);
537 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
539 perror("writing bootblock");
542 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
543 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
544 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
545 perror("writing adv");
548 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
549 if (stat(path, rst)) {
556 int install_file(const char *path, int devfd, struct stat *rst)
558 if (fs_type == EXT2 || fs_type == VFAT)
559 return ext2_fat_install_file(path, devfd, rst);
560 else if (fs_type == BTRFS)
561 return btrfs_install_file(path, devfd, rst);
565 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
566 sector; this is consistent with FAT filesystems. */
567 int already_installed(int devfd)
571 xpread(devfd, buffer, 8, 3);
572 return !memcmp(buffer, "EXTLINUX", 8);
576 static char devname_buf[64];
578 static void device_cleanup(void)
584 /* Verify that a device fd and a pathname agree.
585 Return 0 on valid, -1 on error. */
586 static int validate_device(const char *path, int devfd)
588 struct stat pst, dst;
591 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
593 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
594 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
596 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
600 static const char *find_device(const char *mtab_file, dev_t dev)
605 const char *devname = NULL;
608 mtab = setmntent(mtab_file, "r");
613 while ((mnt = getmntent(mtab))) {
614 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
617 if (!strcmp(mnt->mnt_type, "btrfs") &&
618 !stat(mnt->mnt_dir, &dst) &&
620 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
626 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
627 tmp = strchr(subvol, 32);
631 break; /* should break and let upper layer try again */
637 if ((!strcmp(mnt->mnt_type, "ext2") ||
638 !strcmp(mnt->mnt_type, "ext3") ||
639 !strcmp(mnt->mnt_type, "ext4")) &&
640 !stat(mnt->mnt_fsname, &dst) &&
641 dst.st_rdev == dev) {
646 if ((!strcmp(mnt->mnt_type, "vfat")) &&
647 !stat(mnt->mnt_fsname, &dst) &&
648 dst.st_rdev == dev) {
656 devname = strdup(mnt->mnt_fsname);
666 static const char *get_devname(const char *path)
668 const char *devname = NULL;
672 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
673 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
676 if (statfs(path, &sfs)) {
677 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
682 /* klibc doesn't have getmntent and friends; instead, just create
683 a new device with the appropriate device type */
684 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
685 major(st.st_dev), minor(st.st_dev));
687 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
688 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
692 atexit(device_cleanup); /* unlink the device node on exit */
693 devname = devname_buf;
697 /* check /etc/mtab first, since btrfs subvol info is only in here */
698 devname = find_device("/etc/mtab", st.st_dev);
699 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
703 strcpy(parent, path);
704 tmp = strrchr(parent, '/');
707 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
708 devname = get_devname(parent);
713 /* Didn't find it in /etc/mtab, try /proc/mounts */
714 devname = find_device("/proc/mounts", st.st_dev);
717 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
721 fprintf(stderr, "%s is device %s\n", path, devname);
726 static int open_device(const char *path, struct stat *st, const char **_devname)
729 const char *devname = NULL;
733 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
734 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
738 if (statfs(path, &sfs)) {
739 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
742 if (sfs.f_type == EXT2_SUPER_MAGIC)
744 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
746 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
750 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
756 devname = get_devname(path);
760 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
761 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
765 /* Verify that the device we opened is the device intended */
766 if (validate_device(path, devfd)) {
767 fprintf(stderr, "%s: path %s doesn't match device %s\n",
768 program, path, devname);
775 static int ext_read_adv(const char *path, const char *cfg, int devfd)
777 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
778 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
779 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
780 perror("btrfs writing adv");
785 return read_adv(path, cfg);
788 static int ext_write_adv(const char *path, const char *cfg, int devfd)
790 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
791 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
792 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
793 perror("writing adv");
798 return write_adv(path, cfg);
801 int install_loader(const char *path, int update_only)
807 devfd = open_device(path, &st, &devname);
811 if (update_only && !already_installed(devfd)) {
812 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
818 /* Read a pre-existing ADV, if already installed */
820 syslinux_reset_adv(syslinux_adv);
821 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
825 if (modify_adv() < 0) {
830 /* Install extlinux.sys */
831 if (install_file(path, devfd, &fst)) {
835 if (fst.st_dev != st.st_dev) {
836 fprintf(stderr, "%s: file system changed under us - aborting!\n",
843 rv = install_bootblock(devfd, devname);
851 * Modify the ADV of an existing installation
853 int modify_existing_adv(const char *path)
857 devfd = open_device(path, NULL, NULL);
862 syslinux_reset_adv(syslinux_adv);
863 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
867 if (modify_adv() < 0) {
871 if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
879 int main(int argc, char *argv[])
881 parse_options(argc, argv, 0);
886 if (opt.update_only == -1) {
887 if (opt.reset_adv || opt.set_once || opt.menu_save)
888 return modify_existing_adv(opt.directory);
893 return install_loader(opt.directory, opt.update_only);