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 syslinux 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
];
76 #define BTRFS_ADV_OFFSET (BTRFS_EXTLINUX_OFFSET + boot_image_len)
79 * Get the size of a block device
81 uint64_t get_size(int devfd
)
88 if (!ioctl(devfd
, BLKGETSIZE64
, &bytes
))
91 if (!ioctl(devfd
, BLKGETSIZE
, §s
))
92 return (uint64_t) sects
<< 9;
93 else if (!fstat(devfd
, &st
) && st
.st_size
)
100 * Get device geometry and partition offset
102 struct geometry_table
{
104 struct hd_geometry g
;
107 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
108 (x/64/32) is the final fallback. I don't know what LS-240 has
109 as its geometry, since I don't have one and don't know anyone that does,
110 and Google wasn't helpful... */
111 static const struct geometry_table standard_geometries
[] = {
112 {360 * 1024, {2, 9, 40, 0}},
113 {720 * 1024, {2, 9, 80, 0}},
114 {1200 * 1024, {2, 15, 80, 0}},
115 {1440 * 1024, {2, 18, 80, 0}},
116 {1680 * 1024, {2, 21, 80, 0}},
117 {1722 * 1024, {2, 21, 80, 0}},
118 {2880 * 1024, {2, 36, 80, 0}},
119 {3840 * 1024, {2, 48, 80, 0}},
120 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
124 int get_geometry(int devfd
, uint64_t totalbytes
, struct hd_geometry
*geo
)
126 struct floppy_struct fd_str
;
127 const struct geometry_table
*gp
;
129 memset(geo
, 0, sizeof *geo
);
131 if (!ioctl(devfd
, HDIO_GETGEO
, &geo
)) {
133 } else if (!ioctl(devfd
, FDGETPRM
, &fd_str
)) {
134 geo
->heads
= fd_str
.head
;
135 geo
->sectors
= fd_str
.sect
;
136 geo
->cylinders
= fd_str
.track
;
141 /* Didn't work. Let's see if this is one of the standard geometries */
142 for (gp
= standard_geometries
; gp
->bytes
; gp
++) {
143 if (gp
->bytes
== totalbytes
) {
144 memcpy(geo
, &gp
->g
, sizeof *geo
);
149 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
150 what zipdisks use, so this would help if someone has a USB key that
151 they're booting in USB-ZIP mode. */
153 geo
->heads
= opt
.heads
? : 64;
154 geo
->sectors
= opt
.sectors
? : 32;
155 geo
->cylinders
= totalbytes
/ (geo
->heads
* geo
->sectors
<< SECTOR_SHIFT
);
158 if (!opt
.sectors
&& !opt
.heads
)
160 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
161 " (on hard disks, this is usually harmless.)\n",
162 geo
->heads
, geo
->sectors
);
168 * Query the device geometry and put it into the boot sector.
169 * Map the file and put the map in the boot sector and file.
170 * Stick the "current directory" inode number into the file.
172 * Returns the number of modified bytes in the boot file.
174 int patch_file_and_bootblock(int fd
, const char *dir
, int devfd
)
176 struct stat dirst
, xdst
;
177 struct hd_geometry geo
;
179 uint64_t totalbytes
, totalsectors
;
181 struct boot_sector
*sbs
;
182 char *dirpath
, *subpath
, *xdirpath
, *xsubpath
;
185 dirpath
= realpath(dir
, NULL
);
186 if (!dirpath
|| stat(dir
, &dirst
)) {
187 perror("accessing install directory");
188 exit(255); /* This should never happen */
191 if (lstat(dirpath
, &xdst
) ||
192 dirst
.st_ino
!= xdst
.st_ino
||
193 dirst
.st_dev
!= xdst
.st_dev
) {
194 perror("realpath returned nonsense");
198 subpath
= strchr(dirpath
, '\0');
200 if (*subpath
== '/') {
201 if (subpath
> dirpath
) {
203 xsubpath
= subpath
+1;
209 if (lstat(xdirpath
, &xdst
) || dirst
.st_dev
!= xdst
.st_dev
) {
210 subpath
= strchr(subpath
+1, '/');
212 subpath
= "/"; /* It's the root of the filesystem */
218 if (subpath
== dirpath
)
224 /* Now subpath should contain the path relative to the fs base */
225 dprintf("subpath = %s\n", subpath
);
227 totalbytes
= get_size(devfd
);
228 get_geometry(devfd
, totalbytes
, &geo
);
231 geo
.heads
= opt
.heads
;
233 geo
.sectors
= opt
.sectors
;
235 /* Patch this into a fake FAT superblock. This isn't because
236 FAT is a good format in any way, it's because it lets the
237 early bootstrap share code with the FAT version. */
238 dprintf("heads = %u, sect = %u\n", geo
.heads
, geo
.sectors
);
240 sbs
= (struct boot_sector
*)syslinux_bootsect
;
242 totalsectors
= totalbytes
>> SECTOR_SHIFT
;
243 if (totalsectors
>= 65536) {
244 set_16(&sbs
->bsSectors
, 0);
246 set_16(&sbs
->bsSectors
, totalsectors
);
248 set_32(&sbs
->bsHugeSectors
, totalsectors
);
250 set_16(&sbs
->bsBytesPerSec
, SECTOR_SIZE
);
251 set_16(&sbs
->bsSecPerTrack
, geo
.sectors
);
252 set_16(&sbs
->bsHeads
, geo
.heads
);
253 set_32(&sbs
->bsHiddenSecs
, geo
.start
);
255 /* Construct the boot file map */
257 dprintf("directory inode = %lu\n", (unsigned long)dirst
.st_ino
);
258 nsect
= (boot_image_len
+ SECTOR_SIZE
- 1) >> SECTOR_SHIFT
;
259 nsect
+= 2; /* Two sectors for the ADV */
260 sectp
= alloca(sizeof(sector_t
) * nsect
);
261 if (fs_type
== EXT2
|| fs_type
== VFAT
) {
262 if (sectmap(fd
, sectp
, nsect
)) {
266 } else if (fs_type
== BTRFS
) {
269 for (i
= 0; i
< nsect
; i
++)
270 sectp
[i
] = BTRFS_EXTLINUX_OFFSET
/SECTOR_SIZE
+ i
;
273 /* Create the modified image in memory */
274 rv
= syslinux_patch(sectp
, nsect
, opt
.stupid_mode
,
275 opt
.raid_mode
, subpath
, subvol
);
282 * Make any user-specified ADV modifications
289 if (syslinux_setadv(ADV_BOOTONCE
, strlen(opt
.set_once
), opt
.set_once
)) {
290 fprintf(stderr
, "%s: not enough space for boot-once command\n",
296 if (syslinux_setadv(ADV_MENUSAVE
, strlen(opt
.menu_save
), opt
.menu_save
)) {
297 fprintf(stderr
, "%s: not enough space for menu-save label\n",
307 * Install the boot block on the specified device.
308 * Must be run AFTER install_file()!
310 int install_bootblock(int fd
, const char *device
)
312 struct ext2_super_block sb
;
313 struct btrfs_super_block sb2
;
314 struct boot_sector sb3
;
317 if (fs_type
== EXT2
) {
318 if (xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
319 perror("reading superblock");
322 if (sb
.s_magic
== EXT2_SUPER_MAGIC
)
324 } else if (fs_type
== BTRFS
) {
325 if (xpread(fd
, &sb2
, sizeof sb2
, BTRFS_SUPER_INFO_OFFSET
)
327 perror("reading superblock");
330 if (sb2
.magic
== *(u64
*)BTRFS_MAGIC
)
332 } else if (fs_type
== VFAT
) {
333 if (xpread(fd
, &sb3
, sizeof sb3
, 0) != sizeof sb3
) {
334 perror("reading fat superblock");
337 if (sb3
.bsResSectors
&& sb3
.bsFATs
&&
338 (strstr(sb3
.bs16
.FileSysType
, "FAT") ||
339 strstr(sb3
.bs32
.FileSysType
, "FAT")))
343 fprintf(stderr
, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
347 if (fs_type
== VFAT
) {
348 struct boot_sector
*sbs
= (struct boot_sector
*)syslinux_bootsect
;
349 if (xpwrite(fd
, &sbs
->bsHead
, bsHeadLen
, 0) != bsHeadLen
||
350 xpwrite(fd
, &sbs
->bsCode
, bsCodeLen
,
351 offsetof(struct boot_sector
, bsCode
)) != bsCodeLen
) {
352 perror("writing fat bootblock");
356 if (xpwrite(fd
, syslinux_bootsect
, syslinux_bootsect_len
, 0)
357 != syslinux_bootsect_len
) {
358 perror("writing bootblock");
366 int ext2_fat_install_file(const char *path
, int devfd
, struct stat
*rst
)
369 int fd
= -1, dirfd
= -1;
372 asprintf(&file
, "%s%sldlinux.sys",
373 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
379 dirfd
= open(path
, O_RDONLY
| O_DIRECTORY
);
385 fd
= open(file
, O_RDONLY
);
387 if (errno
!= ENOENT
) {
392 clear_attributes(fd
);
396 fd
= open(file
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_SYNC
,
397 S_IRUSR
| S_IRGRP
| S_IROTH
);
403 /* Write it the first time */
404 if (xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
||
405 xpwrite(fd
, syslinux_adv
, 2 * ADV_SIZE
,
406 boot_image_len
) != 2 * ADV_SIZE
) {
407 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
411 /* Map the file, and patch the initial sector accordingly */
412 modbytes
= patch_file_and_bootblock(fd
, path
, devfd
);
414 /* Write the patch area again - this relies on the file being
415 overwritten in place! */
416 if (xpwrite(fd
, boot_image
, modbytes
, 0) != modbytes
) {
417 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
421 /* Attempt to set immutable flag and remove all write access */
422 /* Only set immutable flag if file is owned by root */
425 if (fstat(fd
, rst
)) {
443 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
444 is not managered by btrfs tree, so actually this is not installed as files.
445 since the cow feature of btrfs will move the ldlinux.sys every where */
446 int btrfs_install_file(const char *path
, int devfd
, struct stat
*rst
)
448 patch_file_and_bootblock(-1, path
, devfd
);
449 if (xpwrite(devfd
, boot_image
, boot_image_len
, BTRFS_EXTLINUX_OFFSET
)
451 perror("writing bootblock");
454 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET
);
455 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
456 BTRFS_EXTLINUX_OFFSET
+ boot_image_len
) != 2 * ADV_SIZE
) {
457 perror("writing adv");
460 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET
+ boot_image_len
);
461 if (stat(path
, rst
)) {
468 int install_file(const char *path
, int devfd
, struct stat
*rst
)
470 if (fs_type
== EXT2
|| fs_type
== VFAT
)
471 return ext2_fat_install_file(path
, devfd
, rst
);
472 else if (fs_type
== BTRFS
)
473 return btrfs_install_file(path
, devfd
, rst
);
478 * SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
479 * sector; this is consistent with FAT filesystems. Earlier versions
480 * would install the string "EXTLINUX" instead, handle both.
482 int already_installed(int devfd
)
486 xpread(devfd
, buffer
, 8, 3);
487 return !memcmp(buffer
, "SYSLINUX", 8) || !memcmp(buffer
, "EXTLINUX", 8);
491 static char devname_buf
[64];
493 static void device_cleanup(void)
499 /* Verify that a device fd and a pathname agree.
500 Return 0 on valid, -1 on error. */
501 static int validate_device(const char *path
, int devfd
)
503 struct stat pst
, dst
;
506 if (stat(path
, &pst
) || fstat(devfd
, &dst
) || statfs(path
, &sfs
))
508 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
509 if (fs_type
== BTRFS
&& sfs
.f_type
== BTRFS_SUPER_MAGIC
)
511 return (pst
.st_dev
== dst
.st_rdev
) ? 0 : -1;
515 static const char *find_device(const char *mtab_file
, dev_t dev
)
520 const char *devname
= NULL
;
523 mtab
= setmntent(mtab_file
, "r");
528 while ((mnt
= getmntent(mtab
))) {
529 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
532 if (!strcmp(mnt
->mnt_type
, "btrfs") &&
533 !stat(mnt
->mnt_dir
, &dst
) &&
535 char *opt
= strstr(mnt
->mnt_opts
, BTRFS_SUBVOL_OPT
);
541 strcpy(subvol
, opt
+ sizeof(BTRFS_SUBVOL_OPT
) - 1);
542 tmp
= strchr(subvol
, 32);
546 break; /* should break and let upper layer try again */
552 if ((!strcmp(mnt
->mnt_type
, "ext2") ||
553 !strcmp(mnt
->mnt_type
, "ext3") ||
554 !strcmp(mnt
->mnt_type
, "ext4")) &&
555 !stat(mnt
->mnt_fsname
, &dst
) &&
556 dst
.st_rdev
== dev
) {
561 if ((!strcmp(mnt
->mnt_type
, "vfat")) &&
562 !stat(mnt
->mnt_fsname
, &dst
) &&
563 dst
.st_rdev
== dev
) {
571 devname
= strdup(mnt
->mnt_fsname
);
581 static const char *get_devname(const char *path
)
583 const char *devname
= NULL
;
587 if (stat(path
, &st
) || !S_ISDIR(st
.st_mode
)) {
588 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
591 if (statfs(path
, &sfs
)) {
592 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
597 /* klibc doesn't have getmntent and friends; instead, just create
598 a new device with the appropriate device type */
599 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
600 major(st
.st_dev
), minor(st
.st_dev
));
602 if (mknod(devname_buf
, S_IFBLK
| 0600, st
.st_dev
)) {
603 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
607 atexit(device_cleanup
); /* unlink the device node on exit */
608 devname
= devname_buf
;
612 /* check /etc/mtab first, since btrfs subvol info is only in here */
613 devname
= find_device("/etc/mtab", st
.st_dev
);
614 if (subvol
[0] && !devname
) { /* we just find it is a btrfs subvol */
618 strcpy(parent
, path
);
619 tmp
= strrchr(parent
, '/');
622 fprintf(stderr
, "%s is subvol, try its parent dir %s\n", path
, parent
);
623 devname
= get_devname(parent
);
628 /* Didn't find it in /etc/mtab, try /proc/mounts */
629 devname
= find_device("/proc/mounts", st
.st_dev
);
632 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
636 fprintf(stderr
, "%s is device %s\n", path
, devname
);
641 static int open_device(const char *path
, struct stat
*st
, const char **_devname
)
644 const char *devname
= NULL
;
648 if (stat(path
, st
) || !S_ISDIR(st
->st_mode
)) {
649 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
653 if (statfs(path
, &sfs
)) {
654 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
657 if (sfs
.f_type
== EXT2_SUPER_MAGIC
)
659 else if (sfs
.f_type
== BTRFS_SUPER_MAGIC
)
661 else if (sfs
.f_type
== MSDOS_SUPER_MAGIC
)
665 fprintf(stderr
, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
671 devname
= get_devname(path
);
675 if ((devfd
= open(devname
, O_RDWR
| O_SYNC
)) < 0) {
676 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
680 /* Verify that the device we opened is the device intended */
681 if (validate_device(path
, devfd
)) {
682 fprintf(stderr
, "%s: path %s doesn't match device %s\n",
683 program
, path
, devname
);
690 static int ext_read_adv(const char *path
, const char *cfg
, int devfd
)
692 if (fs_type
== BTRFS
) { /* btrfs "ldlinux.sys" is in 64k blank area */
693 if (xpread(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
694 BTRFS_ADV_OFFSET
) != 2 * ADV_SIZE
) {
695 perror("btrfs writing adv");
700 return read_adv(path
, cfg
);
703 static int ext_write_adv(const char *path
, const char *cfg
, int devfd
)
705 if (fs_type
== BTRFS
) { /* btrfs "ldlinux.sys" is in 64k blank area */
706 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
707 BTRFS_ADV_OFFSET
) != 2 * ADV_SIZE
) {
708 perror("writing adv");
713 return write_adv(path
, cfg
);
716 int install_loader(const char *path
, int update_only
)
722 devfd
= open_device(path
, &st
, &devname
);
726 if (update_only
&& !already_installed(devfd
)) {
727 fprintf(stderr
, "%s: no previous syslinux boot sector found\n",
733 /* Read a pre-existing ADV, if already installed */
735 syslinux_reset_adv(syslinux_adv
);
736 else if (ext_read_adv(path
, "ldlinux.sys", devfd
) < 0) {
740 if (modify_adv() < 0) {
745 /* Install ldlinux.sys */
746 if (install_file(path
, devfd
, &fst
)) {
750 if (fst
.st_dev
!= st
.st_dev
) {
751 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
758 rv
= install_bootblock(devfd
, devname
);
766 * Modify the ADV of an existing installation
768 int modify_existing_adv(const char *path
)
772 devfd
= open_device(path
, NULL
, NULL
);
777 syslinux_reset_adv(syslinux_adv
);
778 else if (ext_read_adv(path
, "ldlinux.sys", devfd
) < 0) {
782 if (modify_adv() < 0) {
786 if (ext_write_adv(path
, "ldlinux.sys", devfd
) < 0) {
794 int main(int argc
, char *argv
[])
796 parse_options(argc
, argv
, MODE_EXTLINUX
);
801 if (opt
.update_only
== -1) {
802 if (opt
.reset_adv
|| opt
.set_once
|| opt
.menu_save
)
803 return modify_existing_adv(opt
.directory
);
805 usage(EX_USAGE
, MODE_EXTLINUX
);
808 return install_loader(opt
.directory
, opt
.update_only
);