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 * Install the boot block on the specified device.
283 * Must be run AFTER install_file()!
285 int install_bootblock(int fd
, const char *device
)
287 struct ext2_super_block sb
;
288 struct btrfs_super_block sb2
;
289 struct boot_sector sb3
;
292 if (fs_type
== EXT2
) {
293 if (xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
294 perror("reading superblock");
297 if (sb
.s_magic
== EXT2_SUPER_MAGIC
)
299 } else if (fs_type
== BTRFS
) {
300 if (xpread(fd
, &sb2
, sizeof sb2
, BTRFS_SUPER_INFO_OFFSET
)
302 perror("reading superblock");
305 if (sb2
.magic
== *(u64
*)BTRFS_MAGIC
)
307 } else if (fs_type
== VFAT
) {
308 if (xpread(fd
, &sb3
, sizeof sb3
, 0) != sizeof sb3
) {
309 perror("reading fat superblock");
312 if (sb3
.bsResSectors
&& sb3
.bsFATs
&&
313 (strstr(sb3
.bs16
.FileSysType
, "FAT") ||
314 strstr(sb3
.bs32
.FileSysType
, "FAT")))
318 fprintf(stderr
, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
322 if (fs_type
== VFAT
) {
323 struct boot_sector
*sbs
= (struct boot_sector
*)syslinux_bootsect
;
324 if (xpwrite(fd
, &sbs
->bsHead
, bsHeadLen
, 0) != bsHeadLen
||
325 xpwrite(fd
, &sbs
->bsCode
, bsCodeLen
,
326 offsetof(struct boot_sector
, bsCode
)) != bsCodeLen
) {
327 perror("writing fat bootblock");
331 if (xpwrite(fd
, syslinux_bootsect
, syslinux_bootsect_len
, 0)
332 != syslinux_bootsect_len
) {
333 perror("writing bootblock");
341 int ext2_fat_install_file(const char *path
, int devfd
, struct stat
*rst
)
343 char *file
, *oldfile
;
344 int fd
= -1, dirfd
= -1;
347 asprintf(&file
, "%s%sldlinux.sys",
348 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
349 asprintf(&oldfile
, "%s%sextlinux.sys",
350 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
351 if (!file
|| !oldfile
) {
356 dirfd
= open(path
, O_RDONLY
| O_DIRECTORY
);
362 fd
= open(file
, O_RDONLY
);
364 if (errno
!= ENOENT
) {
369 clear_attributes(fd
);
373 fd
= open(file
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_SYNC
,
374 S_IRUSR
| S_IRGRP
| S_IROTH
);
380 /* Write it the first time */
381 if (xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
||
382 xpwrite(fd
, syslinux_adv
, 2 * ADV_SIZE
,
383 boot_image_len
) != 2 * ADV_SIZE
) {
384 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
388 /* Map the file, and patch the initial sector accordingly */
389 modbytes
= patch_file_and_bootblock(fd
, path
, devfd
);
391 /* Write the patch area again - this relies on the file being
392 overwritten in place! */
393 if (xpwrite(fd
, boot_image
, modbytes
, 0) != modbytes
) {
394 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
398 /* Attempt to set immutable flag and remove all write access */
399 /* Only set immutable flag if file is owned by root */
402 if (fstat(fd
, rst
)) {
410 /* Look if we have the old filename */
411 fd
= open(oldfile
, O_RDONLY
);
413 clear_attributes(fd
);
433 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
434 is not managered by btrfs tree, so actually this is not installed as files.
435 since the cow feature of btrfs will move the ldlinux.sys every where */
436 int btrfs_install_file(const char *path
, int devfd
, struct stat
*rst
)
438 patch_file_and_bootblock(-1, path
, devfd
);
439 if (xpwrite(devfd
, boot_image
, boot_image_len
, BTRFS_EXTLINUX_OFFSET
)
441 perror("writing bootblock");
444 printf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET
);
445 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
446 BTRFS_EXTLINUX_OFFSET
+ boot_image_len
) != 2 * ADV_SIZE
) {
447 perror("writing adv");
450 printf("write adv to 0x%x\n", BTRFS_EXTLINUX_OFFSET
+ boot_image_len
);
451 if (stat(path
, rst
)) {
458 int install_file(const char *path
, int devfd
, struct stat
*rst
)
460 if (fs_type
== EXT2
|| fs_type
== VFAT
)
461 return ext2_fat_install_file(path
, devfd
, rst
);
462 else if (fs_type
== BTRFS
)
463 return btrfs_install_file(path
, devfd
, rst
);
468 * SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
469 * sector; this is consistent with FAT filesystems. Earlier versions
470 * would install the string "EXTLINUX" instead, handle both.
472 int already_installed(int devfd
)
476 xpread(devfd
, buffer
, 8, 3);
477 return !memcmp(buffer
, "SYSLINUX", 8) || !memcmp(buffer
, "EXTLINUX", 8);
481 static char devname_buf
[64];
483 static void device_cleanup(void)
489 /* Verify that a device fd and a pathname agree.
490 Return 0 on valid, -1 on error. */
491 static int validate_device(const char *path
, int devfd
)
493 struct stat pst
, dst
;
496 if (stat(path
, &pst
) || fstat(devfd
, &dst
) || statfs(path
, &sfs
))
498 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
499 if (fs_type
== BTRFS
&& sfs
.f_type
== BTRFS_SUPER_MAGIC
)
501 return (pst
.st_dev
== dst
.st_rdev
) ? 0 : -1;
505 static const char *find_device(const char *mtab_file
, dev_t dev
)
510 const char *devname
= NULL
;
513 mtab
= setmntent(mtab_file
, "r");
518 while ((mnt
= getmntent(mtab
))) {
519 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
522 if (!strcmp(mnt
->mnt_type
, "btrfs") &&
523 !stat(mnt
->mnt_dir
, &dst
) &&
525 char *opt
= strstr(mnt
->mnt_opts
, BTRFS_SUBVOL_OPT
);
531 strcpy(subvol
, opt
+ sizeof(BTRFS_SUBVOL_OPT
) - 1);
532 tmp
= strchr(subvol
, 32);
536 break; /* should break and let upper layer try again */
542 if ((!strcmp(mnt
->mnt_type
, "ext2") ||
543 !strcmp(mnt
->mnt_type
, "ext3") ||
544 !strcmp(mnt
->mnt_type
, "ext4")) &&
545 !stat(mnt
->mnt_fsname
, &dst
) &&
546 dst
.st_rdev
== dev
) {
551 if ((!strcmp(mnt
->mnt_type
, "vfat")) &&
552 !stat(mnt
->mnt_fsname
, &dst
) &&
553 dst
.st_rdev
== dev
) {
561 devname
= strdup(mnt
->mnt_fsname
);
571 static const char *get_devname(const char *path
)
573 const char *devname
= NULL
;
577 if (stat(path
, &st
) || !S_ISDIR(st
.st_mode
)) {
578 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
581 if (statfs(path
, &sfs
)) {
582 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
587 /* klibc doesn't have getmntent and friends; instead, just create
588 a new device with the appropriate device type */
589 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
590 major(st
.st_dev
), minor(st
.st_dev
));
592 if (mknod(devname_buf
, S_IFBLK
| 0600, st
.st_dev
)) {
593 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
597 atexit(device_cleanup
); /* unlink the device node on exit */
598 devname
= devname_buf
;
602 /* check /etc/mtab first, since btrfs subvol info is only in here */
603 devname
= find_device("/etc/mtab", st
.st_dev
);
604 if (subvol
[0] && !devname
) { /* we just find it is a btrfs subvol */
608 strcpy(parent
, path
);
609 tmp
= strrchr(parent
, '/');
612 fprintf(stderr
, "%s is subvol, try its parent dir %s\n", path
, parent
);
613 devname
= get_devname(parent
);
618 /* Didn't find it in /etc/mtab, try /proc/mounts */
619 devname
= find_device("/proc/mounts", st
.st_dev
);
622 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
626 fprintf(stderr
, "%s is device %s\n", path
, devname
);
631 static int open_device(const char *path
, struct stat
*st
, const char **_devname
)
634 const char *devname
= NULL
;
638 if (stat(path
, st
) || !S_ISDIR(st
->st_mode
)) {
639 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
643 if (statfs(path
, &sfs
)) {
644 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
647 if (sfs
.f_type
== EXT2_SUPER_MAGIC
)
649 else if (sfs
.f_type
== BTRFS_SUPER_MAGIC
)
651 else if (sfs
.f_type
== MSDOS_SUPER_MAGIC
)
655 fprintf(stderr
, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
661 devname
= get_devname(path
);
665 if ((devfd
= open(devname
, O_RDWR
| O_SYNC
)) < 0) {
666 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
670 /* Verify that the device we opened is the device intended */
671 if (validate_device(path
, devfd
)) {
672 fprintf(stderr
, "%s: path %s doesn't match device %s\n",
673 program
, path
, devname
);
680 static int btrfs_read_adv(int devfd
)
682 if (xpread(devfd
, syslinux_adv
, 2 * ADV_SIZE
, BTRFS_ADV_OFFSET
)
686 return syslinux_validate_adv(syslinux_adv
) ? 1 : 0;
689 static int ext_read_adv(const char *path
, int devfd
, const char **namep
)
694 if (fs_type
== BTRFS
) {
695 /* btrfs "ldlinux.sys" is in 64k blank area */
696 return btrfs_read_adv(devfd
);
698 err
= read_adv(path
, name
= "ldlinux.sys");
699 if (err
== 2) /* ldlinux.sys does not exist */
700 err
= read_adv(path
, name
= "extlinux.sys");
707 static int ext_write_adv(const char *path
, const char *cfg
, int devfd
)
709 if (fs_type
== BTRFS
) { /* btrfs "ldlinux.sys" is in 64k blank area */
710 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
711 BTRFS_ADV_OFFSET
) != 2 * ADV_SIZE
) {
712 perror("writing adv");
717 return write_adv(path
, cfg
);
720 int install_loader(const char *path
, int update_only
)
726 devfd
= open_device(path
, &st
, &devname
);
730 if (update_only
&& !already_installed(devfd
)) {
731 fprintf(stderr
, "%s: no previous syslinux boot sector found\n",
737 /* Read a pre-existing ADV, if already installed */
739 syslinux_reset_adv(syslinux_adv
);
740 } else if (ext_read_adv(path
, devfd
, NULL
) < 0) {
745 if (modify_adv() < 0) {
750 /* Install ldlinux.sys */
751 if (install_file(path
, devfd
, &fst
)) {
755 if (fst
.st_dev
!= st
.st_dev
) {
756 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
763 rv
= install_bootblock(devfd
, devname
);
771 * Modify the ADV of an existing installation
773 int modify_existing_adv(const char *path
)
775 const char *filename
;
778 devfd
= open_device(path
, NULL
, NULL
);
783 syslinux_reset_adv(syslinux_adv
);
784 else if (ext_read_adv(path
, devfd
, &filename
) < 0) {
788 if (modify_adv() < 0) {
792 if (ext_write_adv(path
, filename
, devfd
) < 0) {
800 int main(int argc
, char *argv
[])
802 parse_options(argc
, argv
, MODE_EXTLINUX
);
807 if (opt
.update_only
== -1) {
808 if (opt
.reset_adv
|| opt
.set_once
|| opt
.menu_save
)
809 return modify_existing_adv(opt
.directory
);
811 usage(EX_USAGE
, MODE_EXTLINUX
);
814 return install_loader(opt
.directory
, opt
.update_only
);