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",
389 * Install the boot block on the specified device.
390 * Must be run AFTER install_file()!
392 int install_bootblock(int fd, const char *device)
394 struct ext2_super_block sb;
395 struct btrfs_super_block sb2;
396 struct boot_sector sb3;
399 if (fs_type == EXT2) {
400 if (xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb) {
401 perror("reading superblock");
404 if (sb.s_magic == EXT2_SUPER_MAGIC)
406 } else if (fs_type == BTRFS) {
407 if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET)
409 perror("reading superblock");
412 if (sb2.magic == *(u64 *)BTRFS_MAGIC)
414 } else if (fs_type == VFAT) {
415 if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
416 perror("reading fat superblock");
419 if (sb3.bsResSectors && sb3.bsFATs &&
420 (strstr(sb3.bs16.FileSysType, "FAT") ||
421 strstr(sb3.bs32.FileSysType, "FAT")))
425 fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
429 if (fs_type == VFAT) {
430 struct boot_sector *bs = (struct boot_sector *)extlinux_bootsect;
431 if (xpwrite(fd, &bs->bsHead, bsHeadLen, 0) != bsHeadLen ||
432 xpwrite(fd, &bs->bsCode, bsCodeLen,
433 offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
434 perror("writing fat bootblock");
438 if (xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len) {
439 perror("writing bootblock");
447 int ext2_fat_install_file(const char *path, int devfd, struct stat *rst)
450 int fd = -1, dirfd = -1;
453 asprintf(&file, "%s%sextlinux.sys",
454 path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/");
460 dirfd = open(path, O_RDONLY | O_DIRECTORY);
466 fd = open(file, O_RDONLY);
468 if (errno != ENOENT) {
473 clear_attributes(fd);
477 fd = open(file, O_WRONLY | O_TRUNC | O_CREAT | O_SYNC,
478 S_IRUSR | S_IRGRP | S_IROTH);
484 /* Write it the first time */
485 if (xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len ||
486 xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
487 boot_image_len) != 2 * ADV_SIZE) {
488 fprintf(stderr, "%s: write failure on %s\n", program, file);
492 /* Map the file, and patch the initial sector accordingly */
493 modbytes = patch_file_and_bootblock(fd, path, devfd);
495 /* Write the patch area again - this relies on the file being
496 overwritten in place! */
497 if (xpwrite(fd, boot_image, modbytes, 0) != modbytes) {
498 fprintf(stderr, "%s: write failure on %s\n", program, file);
502 /* Attempt to set immutable flag and remove all write access */
503 /* Only set immutable flag if file is owned by root */
506 if (fstat(fd, rst)) {
524 /* btrfs has to install the extlinux.sys in the first 64K blank area, which
525 is not managered by btrfs tree, so actually this is not installed as files.
526 since the cow feature of btrfs will move the extlinux.sys every where */
527 int btrfs_install_file(const char *path, int devfd, struct stat *rst)
529 patch_file_and_bootblock(-1, path, devfd);
530 if (xpwrite(devfd, boot_image, boot_image_len, BTRFS_EXTLINUX_OFFSET)
532 perror("writing bootblock");
535 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET);
536 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
537 BTRFS_EXTLINUX_OFFSET + boot_image_len) != 2 * ADV_SIZE) {
538 perror("writing adv");
541 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET + boot_image_len);
542 if (stat(path, rst)) {
549 int install_file(const char *path, int devfd, struct stat *rst)
551 if (fs_type == EXT2 || fs_type == VFAT)
552 return ext2_fat_install_file(path, devfd, rst);
553 else if (fs_type == BTRFS)
554 return btrfs_install_file(path, devfd, rst);
558 /* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot
559 sector; this is consistent with FAT filesystems. */
560 int already_installed(int devfd)
564 xpread(devfd, buffer, 8, 3);
565 return !memcmp(buffer, "EXTLINUX", 8);
569 static char devname_buf[64];
571 static void device_cleanup(void)
577 /* Verify that a device fd and a pathname agree.
578 Return 0 on valid, -1 on error. */
579 static int validate_device(const char *path, int devfd)
581 struct stat pst, dst;
584 if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
586 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
587 if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
589 return (pst.st_dev == dst.st_rdev) ? 0 : -1;
593 static const char *find_device(const char *mtab_file, dev_t dev)
598 const char *devname = NULL;
601 mtab = setmntent(mtab_file, "r");
606 while ((mnt = getmntent(mtab))) {
607 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
610 if (!strcmp(mnt->mnt_type, "btrfs") &&
611 !stat(mnt->mnt_dir, &dst) &&
613 char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
619 strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
620 tmp = strchr(subvol, 32);
624 break; /* should break and let upper layer try again */
630 if ((!strcmp(mnt->mnt_type, "ext2") ||
631 !strcmp(mnt->mnt_type, "ext3") ||
632 !strcmp(mnt->mnt_type, "ext4")) &&
633 !stat(mnt->mnt_fsname, &dst) &&
634 dst.st_rdev == dev) {
639 if ((!strcmp(mnt->mnt_type, "vfat")) &&
640 !stat(mnt->mnt_fsname, &dst) &&
641 dst.st_rdev == dev) {
649 devname = strdup(mnt->mnt_fsname);
659 static const char *get_devname(const char *path)
661 const char *devname = NULL;
665 if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
666 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
669 if (statfs(path, &sfs)) {
670 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
675 /* klibc doesn't have getmntent and friends; instead, just create
676 a new device with the appropriate device type */
677 snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
678 major(st.st_dev), minor(st.st_dev));
680 if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
681 fprintf(stderr, "%s: cannot create device %s\n", program, devname);
685 atexit(device_cleanup); /* unlink the device node on exit */
686 devname = devname_buf;
690 /* check /etc/mtab first, since btrfs subvol info is only in here */
691 devname = find_device("/etc/mtab", st.st_dev);
692 if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
696 strcpy(parent, path);
697 tmp = strrchr(parent, '/');
700 fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
701 devname = get_devname(parent);
706 /* Didn't find it in /etc/mtab, try /proc/mounts */
707 devname = find_device("/proc/mounts", st.st_dev);
710 fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
714 fprintf(stderr, "%s is device %s\n", path, devname);
719 static int open_device(const char *path, struct stat *st, const char **_devname)
722 const char *devname = NULL;
726 if (stat(path, st) || !S_ISDIR(st->st_mode)) {
727 fprintf(stderr, "%s: Not a directory: %s\n", program, path);
731 if (statfs(path, &sfs)) {
732 fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
735 if (sfs.f_type == EXT2_SUPER_MAGIC)
737 else if (sfs.f_type == BTRFS_SUPER_MAGIC)
739 else if (sfs.f_type == MSDOS_SUPER_MAGIC)
743 fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
749 devname = get_devname(path);
753 if ((devfd = open(devname, O_RDWR | O_SYNC)) < 0) {
754 fprintf(stderr, "%s: cannot open device %s\n", program, devname);
758 /* Verify that the device we opened is the device intended */
759 if (validate_device(path, devfd)) {
760 fprintf(stderr, "%s: path %s doesn't match device %s\n",
761 program, path, devname);
768 static int ext_read_adv(const char *path, const char *cfg, int devfd)
770 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
771 if (xpread(devfd, syslinux_adv, 2 * ADV_SIZE,
772 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
773 perror("btrfs writing adv");
778 return read_adv(path, cfg);
781 static int ext_write_adv(const char *path, const char *cfg, int devfd)
783 if (fs_type == BTRFS) { /* btrfs "extlinux.sys" is in 64k blank area */
784 if (xpwrite(devfd, syslinux_adv, 2 * ADV_SIZE,
785 BTRFS_ADV_OFFSET) != 2 * ADV_SIZE) {
786 perror("writing adv");
791 return write_adv(path, cfg);
794 int install_loader(const char *path, int update_only)
800 devfd = open_device(path, &st, &devname);
804 if (update_only && !already_installed(devfd)) {
805 fprintf(stderr, "%s: no previous extlinux boot sector found\n",
811 /* Read a pre-existing ADV, if already installed */
813 syslinux_reset_adv(syslinux_adv);
814 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
818 if (modify_adv() < 0) {
823 /* Install extlinux.sys */
824 if (install_file(path, devfd, &fst)) {
828 if (fst.st_dev != st.st_dev) {
829 fprintf(stderr, "%s: file system changed under us - aborting!\n",
836 rv = install_bootblock(devfd, devname);
844 * Modify the ADV of an existing installation
846 int modify_existing_adv(const char *path)
850 devfd = open_device(path, NULL, NULL);
855 syslinux_reset_adv(syslinux_adv);
856 else if (ext_read_adv(path, "extlinux.sys", devfd) < 0) {
860 if (modify_adv() < 0) {
864 if (ext_write_adv(path, "extlinux.sys", devfd) < 0) {
872 int main(int argc, char *argv[])
874 parse_options(argc, argv, 0);
879 if (opt.update_only == -1) {
880 if (opt.reset_adv || opt.set_once)
881 return modify_existing_adv(opt.directory);
886 return install_loader(opt.directory, opt.update_only);