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 #ifndef EXT2_SUPER_OFFSET
61 #define EXT2_SUPER_OFFSET 1024
64 /* the btrfs partition first 64K blank area is used to store boot sector and
65 boot image, the boot sector is from 0~512, the boot image starts after */
66 #define BTRFS_BOOTSECT_AREA 65536
67 #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
68 #define BTRFS_SUBVOL_OPT "subvol="
69 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
70 static char subvol
[BTRFS_SUBVOL_MAX
];
72 #define BTRFS_ADV_OFFSET (BTRFS_BOOTSECT_AREA - 2 * ADV_SIZE)
75 * Get the size of a block device
77 uint64_t get_size(int devfd
)
84 if (!ioctl(devfd
, BLKGETSIZE64
, &bytes
))
87 if (!ioctl(devfd
, BLKGETSIZE
, §s
))
88 return (uint64_t) sects
<< 9;
89 else if (!fstat(devfd
, &st
) && st
.st_size
)
96 * Get device geometry and partition offset
98 struct geometry_table
{
100 struct hd_geometry g
;
103 static int sysfs_get_offset(int devfd
, unsigned long *start
)
106 char sysfs_name
[128];
110 if (fstat(devfd
, &st
))
113 if ((size_t)snprintf(sysfs_name
, sizeof sysfs_name
,
114 "/sys/dev/block/%u:%u/start",
115 major(st
.st_dev
), minor(st
.st_dev
))
116 >= sizeof sysfs_name
)
119 f
= fopen(sysfs_name
, "r");
123 rv
= fscanf(f
, "%lu", start
);
126 return (rv
== 1) ? 0 : -1;
129 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
130 (x/64/32) is the final fallback. I don't know what LS-240 has
131 as its geometry, since I don't have one and don't know anyone that does,
132 and Google wasn't helpful... */
133 static const struct geometry_table standard_geometries
[] = {
134 {360 * 1024, {2, 9, 40, 0}},
135 {720 * 1024, {2, 9, 80, 0}},
136 {1200 * 1024, {2, 15, 80, 0}},
137 {1440 * 1024, {2, 18, 80, 0}},
138 {1680 * 1024, {2, 21, 80, 0}},
139 {1722 * 1024, {2, 21, 80, 0}},
140 {2880 * 1024, {2, 36, 80, 0}},
141 {3840 * 1024, {2, 48, 80, 0}},
142 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
146 int get_geometry(int devfd
, uint64_t totalbytes
, struct hd_geometry
*geo
)
148 struct floppy_struct fd_str
;
150 struct loop_info64 li64
;
151 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
);
188 rv
= 1; /* Suboptimal result */
192 /* If this is a loopback device, try to set the start */
193 if (!ioctl(devfd
, LOOP_GET_STATUS64
, &li64
))
194 geo
->start
= li64
.lo_offset
>> SECTOR_SHIFT
;
195 else if (!ioctl(devfd
, LOOP_GET_STATUS
, &li
))
196 geo
->start
= (unsigned int)li
.lo_offset
>> SECTOR_SHIFT
;
197 else if (!sysfs_get_offset(devfd
, &geo
->start
)) {
205 * Query the device geometry and put it into the boot sector.
206 * Map the file and put the map in the boot sector and file.
207 * Stick the "current directory" inode number into the file.
209 * Returns the number of modified bytes in the boot file.
211 int patch_file_and_bootblock(int fd
, const char *dir
, int devfd
)
213 struct stat dirst
, xdst
;
214 struct hd_geometry geo
;
216 uint64_t totalbytes
, totalsectors
;
218 struct boot_sector
*sbs
;
219 char *dirpath
, *subpath
, *xdirpath
, *xsubpath
;
222 dirpath
= realpath(dir
, NULL
);
223 if (!dirpath
|| stat(dir
, &dirst
)) {
224 perror("accessing install directory");
225 exit(255); /* This should never happen */
228 if (lstat(dirpath
, &xdst
) ||
229 dirst
.st_ino
!= xdst
.st_ino
||
230 dirst
.st_dev
!= xdst
.st_dev
) {
231 perror("realpath returned nonsense");
235 subpath
= strchr(dirpath
, '\0');
237 if (*subpath
== '/') {
238 if (subpath
> dirpath
) {
240 xsubpath
= subpath
+1;
246 if (lstat(xdirpath
, &xdst
) || dirst
.st_dev
!= xdst
.st_dev
) {
247 subpath
= strchr(subpath
+1, '/');
249 subpath
= "/"; /* It's the root of the filesystem */
255 if (subpath
== dirpath
)
261 /* Now subpath should contain the path relative to the fs base */
262 dprintf("subpath = %s\n", subpath
);
264 totalbytes
= get_size(devfd
);
265 get_geometry(devfd
, totalbytes
, &geo
);
268 geo
.heads
= opt
.heads
;
270 geo
.sectors
= opt
.sectors
;
272 /* Patch this into a fake FAT superblock. This isn't because
273 FAT is a good format in any way, it's because it lets the
274 early bootstrap share code with the FAT version. */
275 dprintf("heads = %u, sect = %u\n", geo
.heads
, geo
.sectors
);
277 sbs
= (struct boot_sector
*)syslinux_bootsect
;
279 totalsectors
= totalbytes
>> SECTOR_SHIFT
;
280 if (totalsectors
>= 65536) {
281 set_16(&sbs
->bsSectors
, 0);
283 set_16(&sbs
->bsSectors
, totalsectors
);
285 set_32(&sbs
->bsHugeSectors
, totalsectors
);
287 set_16(&sbs
->bsBytesPerSec
, SECTOR_SIZE
);
288 set_16(&sbs
->bsSecPerTrack
, geo
.sectors
);
289 set_16(&sbs
->bsHeads
, geo
.heads
);
290 set_32(&sbs
->bsHiddenSecs
, geo
.start
);
292 /* Construct the boot file map */
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(sector_t
) * nsect
);
298 if (fs_type
== EXT2
|| fs_type
== VFAT
) {
299 if (sectmap(fd
, sectp
, nsect
)) {
303 } else if (fs_type
== BTRFS
) {
305 sector_t
*sp
= sectp
;
307 for (i
= 0; i
< nsect
- 2; i
++)
308 *sp
++ = BTRFS_EXTLINUX_OFFSET
/SECTOR_SIZE
+ i
;
309 for (i
= 0; i
< 2; i
++)
310 *sp
++ = BTRFS_ADV_OFFSET
/SECTOR_SIZE
+ i
;
313 /* Create the modified image in memory */
314 rv
= syslinux_patch(sectp
, nsect
, opt
.stupid_mode
,
315 opt
.raid_mode
, subpath
, subvol
);
322 * Install the boot block on the specified device.
323 * Must be run AFTER install_file()!
325 int install_bootblock(int fd
, const char *device
)
327 struct ext2_super_block sb
;
328 struct btrfs_super_block sb2
;
329 struct boot_sector sb3
;
332 if (fs_type
== EXT2
) {
333 if (xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
334 perror("reading superblock");
337 if (sb
.s_magic
== EXT2_SUPER_MAGIC
)
339 } else if (fs_type
== BTRFS
) {
340 if (xpread(fd
, &sb2
, sizeof sb2
, BTRFS_SUPER_INFO_OFFSET
)
342 perror("reading superblock");
345 if (sb2
.magic
== *(u64
*)BTRFS_MAGIC
)
347 } else if (fs_type
== VFAT
) {
348 if (xpread(fd
, &sb3
, sizeof sb3
, 0) != sizeof sb3
) {
349 perror("reading fat superblock");
352 if (sb3
.bsResSectors
&& sb3
.bsFATs
&&
353 (strstr(sb3
.bs16
.FileSysType
, "FAT") ||
354 strstr(sb3
.bs32
.FileSysType
, "FAT")))
358 fprintf(stderr
, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
362 if (fs_type
== VFAT
) {
363 struct boot_sector
*sbs
= (struct boot_sector
*)syslinux_bootsect
;
364 if (xpwrite(fd
, &sbs
->bsHead
, bsHeadLen
, 0) != bsHeadLen
||
365 xpwrite(fd
, &sbs
->bsCode
, bsCodeLen
,
366 offsetof(struct boot_sector
, bsCode
)) != bsCodeLen
) {
367 perror("writing fat bootblock");
371 if (xpwrite(fd
, syslinux_bootsect
, syslinux_bootsect_len
, 0)
372 != syslinux_bootsect_len
) {
373 perror("writing bootblock");
381 int ext2_fat_install_file(const char *path
, int devfd
, struct stat
*rst
)
383 char *file
, *oldfile
;
384 int fd
= -1, dirfd
= -1;
388 r1
= asprintf(&file
, "%s%sldlinux.sys",
389 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
390 r2
= asprintf(&oldfile
, "%s%sextlinux.sys",
391 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
392 if (r1
< 0 || !file
|| r2
< 0 || !oldfile
) {
397 dirfd
= open(path
, O_RDONLY
| O_DIRECTORY
);
403 fd
= open(file
, O_RDONLY
);
405 if (errno
!= ENOENT
) {
410 clear_attributes(fd
);
414 fd
= open(file
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_SYNC
,
415 S_IRUSR
| S_IRGRP
| S_IROTH
);
421 /* Write it the first time */
422 if (xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
||
423 xpwrite(fd
, syslinux_adv
, 2 * ADV_SIZE
,
424 boot_image_len
) != 2 * ADV_SIZE
) {
425 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
429 /* Map the file, and patch the initial sector accordingly */
430 modbytes
= patch_file_and_bootblock(fd
, path
, devfd
);
432 /* Write the patch area again - this relies on the file being
433 overwritten in place! */
434 if (xpwrite(fd
, boot_image
, modbytes
, 0) != modbytes
) {
435 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
439 /* Attempt to set immutable flag and remove all write access */
440 /* Only set immutable flag if file is owned by root */
443 if (fstat(fd
, rst
)) {
451 /* Look if we have the old filename */
452 fd
= open(oldfile
, O_RDONLY
);
454 clear_attributes(fd
);
474 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
475 is not managered by btrfs tree, so actually this is not installed as files.
476 since the cow feature of btrfs will move the ldlinux.sys every where */
477 int btrfs_install_file(const char *path
, int devfd
, struct stat
*rst
)
479 patch_file_and_bootblock(-1, path
, devfd
);
480 if (xpwrite(devfd
, boot_image
, boot_image_len
, BTRFS_EXTLINUX_OFFSET
)
482 perror("writing bootblock");
485 dprintf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET
);
486 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
, BTRFS_ADV_OFFSET
)
488 perror("writing adv");
491 dprintf("write adv to 0x%x\n", BTRFS_ADV_OFFSET
);
492 if (stat(path
, rst
)) {
499 int install_file(const char *path
, int devfd
, struct stat
*rst
)
501 if (fs_type
== EXT2
|| fs_type
== VFAT
)
502 return ext2_fat_install_file(path
, devfd
, rst
);
503 else if (fs_type
== BTRFS
)
504 return btrfs_install_file(path
, devfd
, rst
);
509 * SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
510 * sector; this is consistent with FAT filesystems. Earlier versions
511 * would install the string "EXTLINUX" instead, handle both.
513 int already_installed(int devfd
)
517 xpread(devfd
, buffer
, 8, 3);
518 return !memcmp(buffer
, "SYSLINUX", 8) || !memcmp(buffer
, "EXTLINUX", 8);
522 static char devname_buf
[64];
524 static void device_cleanup(void)
530 /* Verify that a device fd and a pathname agree.
531 Return 0 on valid, -1 on error. */
532 static int validate_device(const char *path
, int devfd
)
534 struct stat pst
, dst
;
537 if (stat(path
, &pst
) || fstat(devfd
, &dst
) || statfs(path
, &sfs
))
539 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
540 if (fs_type
== BTRFS
&& sfs
.f_type
== BTRFS_SUPER_MAGIC
)
542 return (pst
.st_dev
== dst
.st_rdev
) ? 0 : -1;
546 static const char *find_device(const char *mtab_file
, dev_t dev
)
551 const char *devname
= NULL
;
554 mtab
= setmntent(mtab_file
, "r");
559 while ((mnt
= getmntent(mtab
))) {
560 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
563 if (!strcmp(mnt
->mnt_type
, "btrfs") &&
564 !stat(mnt
->mnt_dir
, &dst
) &&
566 char *opt
= strstr(mnt
->mnt_opts
, BTRFS_SUBVOL_OPT
);
572 strcpy(subvol
, opt
+ sizeof(BTRFS_SUBVOL_OPT
) - 1);
573 tmp
= strchr(subvol
, 32);
577 break; /* should break and let upper layer try again */
583 if ((!strcmp(mnt
->mnt_type
, "ext2") ||
584 !strcmp(mnt
->mnt_type
, "ext3") ||
585 !strcmp(mnt
->mnt_type
, "ext4")) &&
586 !stat(mnt
->mnt_fsname
, &dst
) &&
587 dst
.st_rdev
== dev
) {
592 if ((!strcmp(mnt
->mnt_type
, "vfat")) &&
593 !stat(mnt
->mnt_fsname
, &dst
) &&
594 dst
.st_rdev
== dev
) {
602 devname
= strdup(mnt
->mnt_fsname
);
612 static const char *get_devname(const char *path
)
614 const char *devname
= NULL
;
618 if (stat(path
, &st
) || !S_ISDIR(st
.st_mode
)) {
619 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
622 if (statfs(path
, &sfs
)) {
623 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
628 /* klibc doesn't have getmntent and friends; instead, just create
629 a new device with the appropriate device type */
630 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
631 major(st
.st_dev
), minor(st
.st_dev
));
633 if (mknod(devname_buf
, S_IFBLK
| 0600, st
.st_dev
)) {
634 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
638 atexit(device_cleanup
); /* unlink the device node on exit */
639 devname
= devname_buf
;
643 /* check /etc/mtab first, since btrfs subvol info is only in here */
644 devname
= find_device("/etc/mtab", st
.st_dev
);
645 if (subvol
[0] && !devname
) { /* we just find it is a btrfs subvol */
649 strcpy(parent
, path
);
650 tmp
= strrchr(parent
, '/');
653 fprintf(stderr
, "%s is subvol, try its parent dir %s\n", path
, parent
);
654 devname
= get_devname(parent
);
659 /* Didn't find it in /etc/mtab, try /proc/mounts */
660 devname
= find_device("/proc/mounts", st
.st_dev
);
663 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
667 fprintf(stderr
, "%s is device %s\n", path
, devname
);
672 static int open_device(const char *path
, struct stat
*st
, const char **_devname
)
675 const char *devname
= NULL
;
679 if (stat(path
, st
) || !S_ISDIR(st
->st_mode
)) {
680 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
684 if (statfs(path
, &sfs
)) {
685 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
688 if (sfs
.f_type
== EXT2_SUPER_MAGIC
)
690 else if (sfs
.f_type
== BTRFS_SUPER_MAGIC
)
692 else if (sfs
.f_type
== MSDOS_SUPER_MAGIC
)
696 fprintf(stderr
, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
702 devname
= get_devname(path
);
706 if ((devfd
= open(devname
, O_RDWR
| O_SYNC
)) < 0) {
707 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
711 /* Verify that the device we opened is the device intended */
712 if (validate_device(path
, devfd
)) {
713 fprintf(stderr
, "%s: path %s doesn't match device %s\n",
714 program
, path
, devname
);
721 static int btrfs_read_adv(int devfd
)
723 if (xpread(devfd
, syslinux_adv
, 2 * ADV_SIZE
, BTRFS_ADV_OFFSET
)
727 return syslinux_validate_adv(syslinux_adv
) ? 1 : 0;
730 static int ext_read_adv(const char *path
, int devfd
, const char **namep
)
735 if (fs_type
== BTRFS
) {
736 /* btrfs "ldlinux.sys" is in 64k blank area */
737 return btrfs_read_adv(devfd
);
739 err
= read_adv(path
, name
= "ldlinux.sys");
740 if (err
== 2) /* ldlinux.sys does not exist */
741 err
= read_adv(path
, name
= "extlinux.sys");
748 static int ext_write_adv(const char *path
, const char *cfg
, int devfd
)
750 if (fs_type
== BTRFS
) { /* btrfs "ldlinux.sys" is in 64k blank area */
751 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
752 BTRFS_ADV_OFFSET
) != 2 * ADV_SIZE
) {
753 perror("writing adv");
758 return write_adv(path
, cfg
);
761 int install_loader(const char *path
, int update_only
)
767 devfd
= open_device(path
, &st
, &devname
);
771 if (update_only
&& !already_installed(devfd
)) {
772 fprintf(stderr
, "%s: no previous syslinux boot sector found\n",
778 /* Read a pre-existing ADV, if already installed */
780 syslinux_reset_adv(syslinux_adv
);
781 } else if (ext_read_adv(path
, devfd
, NULL
) < 0) {
786 if (modify_adv() < 0) {
791 /* Install ldlinux.sys */
792 if (install_file(path
, devfd
, &fst
)) {
796 if (fst
.st_dev
!= st
.st_dev
) {
797 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
804 rv
= install_bootblock(devfd
, devname
);
812 * Modify the ADV of an existing installation
814 int modify_existing_adv(const char *path
)
816 const char *filename
;
819 devfd
= open_device(path
, NULL
, NULL
);
824 syslinux_reset_adv(syslinux_adv
);
825 else if (ext_read_adv(path
, devfd
, &filename
) < 0) {
829 if (modify_adv() < 0) {
833 if (ext_write_adv(path
, filename
, devfd
) < 0) {
841 int main(int argc
, char *argv
[])
843 parse_options(argc
, argv
, MODE_EXTLINUX
);
845 if (!opt
.directory
|| opt
.install_mbr
|| opt
.activate_partition
)
848 if (opt
.update_only
== -1) {
849 if (opt
.reset_adv
|| opt
.set_once
|| opt
.menu_save
)
850 return modify_existing_adv(opt
.directory
);
852 usage(EX_USAGE
, MODE_EXTLINUX
);
855 return install_loader(opt
.directory
, opt
.update_only
);