1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2012 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, ntfs, 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"
49 #include "../version.h"
51 #include "syslxcom.h" /* common functions shared with extlinux and syslinux */
54 #include "syslxopt.h" /* unified options */
55 #include "mountinfo.h"
58 # define dprintf printf
60 # define dprintf(...) ((void)0)
63 #ifndef EXT2_SUPER_OFFSET
64 #define EXT2_SUPER_OFFSET 1024
67 /* the btrfs partition first 64K blank area is used to store boot sector and
68 boot image, the boot sector is from 0~512, the boot image starts after */
69 #define BTRFS_BOOTSECT_AREA 65536
70 #define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
71 #define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
72 static char subvol
[BTRFS_SUBVOL_MAX
];
74 #define BTRFS_ADV_OFFSET (BTRFS_BOOTSECT_AREA - 2 * ADV_SIZE)
77 * Get the size of a block device
79 uint64_t get_size(int devfd
)
86 if (!ioctl(devfd
, BLKGETSIZE64
, &bytes
))
89 if (!ioctl(devfd
, BLKGETSIZE
, §s
))
90 return (uint64_t) sects
<< 9;
91 else if (!fstat(devfd
, &st
) && st
.st_size
)
98 * Get device geometry and partition offset
100 struct geometry_table
{
102 struct hd_geometry g
;
105 static int sysfs_get_offset(int devfd
, unsigned long *start
)
108 char sysfs_name
[128];
112 if (fstat(devfd
, &st
))
115 if ((size_t)snprintf(sysfs_name
, sizeof sysfs_name
,
116 "/sys/dev/block/%u:%u/start",
117 major(st
.st_rdev
), minor(st
.st_rdev
))
118 >= sizeof sysfs_name
)
121 f
= fopen(sysfs_name
, "r");
125 rv
= fscanf(f
, "%lu", start
);
128 return (rv
== 1) ? 0 : -1;
131 /* Standard floppy disk geometries, plus LS-120. Zipdisk geometry
132 (x/64/32) is the final fallback. I don't know what LS-240 has
133 as its geometry, since I don't have one and don't know anyone that does,
134 and Google wasn't helpful... */
135 static const struct geometry_table standard_geometries
[] = {
136 {360 * 1024, {2, 9, 40, 0}},
137 {720 * 1024, {2, 9, 80, 0}},
138 {1200 * 1024, {2, 15, 80, 0}},
139 {1440 * 1024, {2, 18, 80, 0}},
140 {1680 * 1024, {2, 21, 80, 0}},
141 {1722 * 1024, {2, 21, 80, 0}},
142 {2880 * 1024, {2, 36, 80, 0}},
143 {3840 * 1024, {2, 48, 80, 0}},
144 {123264 * 1024, {8, 32, 963, 0}}, /* LS120 */
148 int get_geometry(int devfd
, uint64_t totalbytes
, struct hd_geometry
*geo
)
150 struct floppy_struct fd_str
;
152 struct loop_info64 li64
;
153 const struct geometry_table
*gp
;
156 memset(geo
, 0, sizeof *geo
);
158 if (!ioctl(devfd
, HDIO_GETGEO
, geo
)) {
160 } else if (!ioctl(devfd
, FDGETPRM
, &fd_str
)) {
161 geo
->heads
= fd_str
.head
;
162 geo
->sectors
= fd_str
.sect
;
163 geo
->cylinders
= fd_str
.track
;
168 /* Didn't work. Let's see if this is one of the standard geometries */
169 for (gp
= standard_geometries
; gp
->bytes
; gp
++) {
170 if (gp
->bytes
== totalbytes
) {
171 memcpy(geo
, &gp
->g
, sizeof *geo
);
176 /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is
177 what zipdisks use, so this would help if someone has a USB key that
178 they're booting in USB-ZIP mode. */
180 geo
->heads
= opt
.heads
? : 64;
181 geo
->sectors
= opt
.sectors
? : 32;
182 geo
->cylinders
= totalbytes
/ (geo
->heads
* geo
->sectors
<< SECTOR_SHIFT
);
185 if (!opt
.sectors
&& !opt
.heads
) {
187 "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n"
188 " (on hard disks, this is usually harmless.)\n",
189 geo
->heads
, geo
->sectors
);
190 rv
= 1; /* Suboptimal result */
194 /* If this is a loopback device, try to set the start */
195 if (!ioctl(devfd
, LOOP_GET_STATUS64
, &li64
))
196 geo
->start
= li64
.lo_offset
>> SECTOR_SHIFT
;
197 else if (!ioctl(devfd
, LOOP_GET_STATUS
, &li
))
198 geo
->start
= (unsigned int)li
.lo_offset
>> SECTOR_SHIFT
;
199 else if (!sysfs_get_offset(devfd
, &geo
->start
)) {
207 * Query the device geometry and put it into the boot sector.
208 * Map the file and put the map in the boot sector and file.
209 * Stick the "current directory" inode number into the file.
211 * Returns the number of modified bytes in the boot file.
213 int patch_file_and_bootblock(int fd
, const char *dir
, int devfd
)
215 struct stat dirst
, xdst
;
216 struct hd_geometry geo
;
218 uint64_t totalbytes
, totalsectors
;
220 struct fat_boot_sector
*sbs
;
221 char *dirpath
, *subpath
, *xdirpath
;
224 dirpath
= realpath(dir
, NULL
);
225 if (!dirpath
|| stat(dir
, &dirst
)) {
226 perror("accessing install directory");
227 exit(255); /* This should never happen */
230 if (lstat(dirpath
, &xdst
) ||
231 dirst
.st_ino
!= xdst
.st_ino
||
232 dirst
.st_dev
!= xdst
.st_dev
) {
233 perror("realpath returned nonsense");
237 subpath
= strchr(dirpath
, '\0');
239 if (*subpath
== '/') {
240 if (subpath
> dirpath
) {
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 fat_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
|| fs_type
== NTFS
) {
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 fat_boot_sector sb3
;
330 struct ntfs_boot_sector sb4
;
333 if (fs_type
== EXT2
) {
334 if (xpread(fd
, &sb
, sizeof sb
, EXT2_SUPER_OFFSET
) != sizeof sb
) {
335 perror("reading superblock");
338 if (sb
.s_magic
== EXT2_SUPER_MAGIC
)
340 } else if (fs_type
== BTRFS
) {
341 if (xpread(fd
, &sb2
, sizeof sb2
, BTRFS_SUPER_INFO_OFFSET
)
343 perror("reading superblock");
346 if (!memcmp(sb2
.magic
, BTRFS_MAGIC
, BTRFS_MAGIC_L
))
348 } else if (fs_type
== VFAT
) {
349 if (xpread(fd
, &sb3
, sizeof sb3
, 0) != sizeof sb3
) {
350 perror("reading fat superblock");
353 if (fat_check_sb_fields(&sb3
))
355 } else if (fs_type
== NTFS
) {
356 if (xpread(fd
, &sb4
, sizeof(sb4
), 0) != sizeof(sb4
)) {
357 perror("reading ntfs superblock");
361 if (ntfs_check_sb_fields(&sb4
))
365 fprintf(stderr
, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n",
369 if (fs_type
== VFAT
) {
370 struct fat_boot_sector
*sbs
= (struct fat_boot_sector
*)syslinux_bootsect
;
371 if (xpwrite(fd
, &sbs
->FAT_bsHead
, FAT_bsHeadLen
, 0) != FAT_bsHeadLen
||
372 xpwrite(fd
, &sbs
->FAT_bsCode
, FAT_bsCodeLen
,
373 offsetof(struct fat_boot_sector
, FAT_bsCode
)) != FAT_bsCodeLen
) {
374 perror("writing fat bootblock");
377 } else if (fs_type
== NTFS
) {
378 struct ntfs_boot_sector
*sbs
=
379 (struct ntfs_boot_sector
*)syslinux_bootsect
;
380 if (xpwrite(fd
, &sbs
->NTFS_bsHead
,
381 NTFS_bsHeadLen
, 0) != NTFS_bsHeadLen
||
382 xpwrite(fd
, &sbs
->NTFS_bsCode
, NTFS_bsCodeLen
,
383 offsetof(struct ntfs_boot_sector
,
384 NTFS_bsCode
)) != NTFS_bsCodeLen
) {
385 perror("writing ntfs bootblock");
389 if (xpwrite(fd
, syslinux_bootsect
, syslinux_bootsect_len
, 0)
390 != syslinux_bootsect_len
) {
391 perror("writing bootblock");
399 int ext2_fat_install_file(const char *path
, int devfd
, struct stat
*rst
)
401 char *file
, *oldfile
;
402 int fd
= -1, dirfd
= -1;
406 r1
= asprintf(&file
, "%s%sldlinux.sys",
407 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
408 r2
= asprintf(&oldfile
, "%s%sextlinux.sys",
409 path
, path
[0] && path
[strlen(path
) - 1] == '/' ? "" : "/");
410 if (r1
< 0 || !file
|| r2
< 0 || !oldfile
) {
415 dirfd
= open(path
, O_RDONLY
| O_DIRECTORY
);
421 fd
= open(file
, O_RDONLY
);
423 if (errno
!= ENOENT
) {
428 clear_attributes(fd
);
432 fd
= open(file
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_SYNC
,
433 S_IRUSR
| S_IRGRP
| S_IROTH
);
439 /* Write it the first time */
440 if (xpwrite(fd
, boot_image
, boot_image_len
, 0) != boot_image_len
||
441 xpwrite(fd
, syslinux_adv
, 2 * ADV_SIZE
,
442 boot_image_len
) != 2 * ADV_SIZE
) {
443 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
447 /* Map the file, and patch the initial sector accordingly */
448 modbytes
= patch_file_and_bootblock(fd
, path
, devfd
);
450 /* Write the patch area again - this relies on the file being
451 overwritten in place! */
452 if (xpwrite(fd
, boot_image
, modbytes
, 0) != modbytes
) {
453 fprintf(stderr
, "%s: write failure on %s\n", program
, file
);
457 /* Attempt to set immutable flag and remove all write access */
458 /* Only set immutable flag if file is owned by root */
461 if (fstat(fd
, rst
)) {
469 /* Look if we have the old filename */
470 fd
= open(oldfile
, O_RDONLY
);
472 clear_attributes(fd
);
492 /* btrfs has to install the ldlinux.sys in the first 64K blank area, which
493 is not managered by btrfs tree, so actually this is not installed as files.
494 since the cow feature of btrfs will move the ldlinux.sys every where */
495 int btrfs_install_file(const char *path
, int devfd
, struct stat
*rst
)
497 patch_file_and_bootblock(-1, path
, devfd
);
498 if (xpwrite(devfd
, boot_image
, boot_image_len
, BTRFS_EXTLINUX_OFFSET
)
500 perror("writing bootblock");
503 dprintf("write boot_image to 0x%x\n", BTRFS_EXTLINUX_OFFSET
);
504 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
, BTRFS_ADV_OFFSET
)
506 perror("writing adv");
509 dprintf("write adv to 0x%x\n", BTRFS_ADV_OFFSET
);
510 if (stat(path
, rst
)) {
518 * * test if path is a subvolume:
519 * * this function return
520 * * 0-> path exists but it is not a subvolume
521 * * 1-> path exists and it is a subvolume
522 * * -1 -> path is unaccessible
524 static int test_issubvolume(char *path
)
530 res
= stat(path
, &st
);
534 return (st
.st_ino
== 256) && S_ISDIR(st
.st_mode
);
539 * Get the default subvolume of a btrfs filesystem
540 * rootdir: btrfs root dir
541 * subvol: this function will save the default subvolume name here
543 static char * get_default_subvol(char * rootdir
, char * subvol
)
545 struct btrfs_ioctl_search_args args
;
546 struct btrfs_ioctl_search_key
*sk
= &args
.key
;
547 struct btrfs_ioctl_search_header
*sh
;
550 struct btrfs_root_ref
*ref
;
551 struct btrfs_dir_item
*dir_item
;
552 unsigned long off
= 0;
556 u64 defaultsubvolid
= 0;
558 ret
= test_issubvolume(rootdir
);
560 fd
= open(rootdir
, O_RDONLY
);
562 fprintf(stderr
, "ERROR: failed to open %s\n", rootdir
);
571 memset(&args
, 0, sizeof(args
));
573 /* search in the tree of tree roots */
577 * set the min and max to backref keys. The search will
578 * only send back this type of key now.
580 sk
->max_type
= BTRFS_DIR_ITEM_KEY
;
581 sk
->min_type
= BTRFS_DIR_ITEM_KEY
;
584 * set all the other params to the max, we'll take any objectid
587 sk
->min_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
;
588 sk
->max_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
;
590 sk
->max_offset
= (u64
)-1;
592 sk
->max_transid
= (u64
)-1;
594 /* just a big number, doesn't matter much */
598 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
600 fprintf(stderr
, "ERROR: can't perform the search\n");
604 /* the ioctl returns the number of item it found in nr_items */
605 if (sk
->nr_items
== 0) {
612 * for each item, pull the key out of the header and then
613 * read the root_ref item it contains
615 for (i
= 0; i
< sk
->nr_items
; i
++) {
616 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
618 if (sh
->type
== BTRFS_DIR_ITEM_KEY
) {
619 dir_item
= (struct btrfs_dir_item
*)(args
.buf
+ off
);
620 name_len
= dir_item
->name_len
;
621 name
= (char *)(dir_item
+ 1);
624 /*add_root(&root_lookup, sh->objectid, sh->offset,
625 dir_id, name, name_len);*/
626 strncpy(dirname
, name
, name_len
);
627 dirname
[name_len
] = '\0';
628 if (strcmp(dirname
, "default") == 0) {
629 defaultsubvolid
= dir_item
->location
.objectid
;
636 * record the mins in sk so we can make sure the
637 * next search doesn't repeat this root
639 sk
->min_objectid
= sh
->objectid
;
640 sk
->min_type
= sh
->type
;
641 sk
->max_type
= sh
->type
;
642 sk
->min_offset
= sh
->offset
;
644 if (defaultsubvolid
!= 0)
647 /* this iteration is done, step forward one root for the next
650 if (sk
->min_objectid
< (u64
)-1) {
651 sk
->min_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
;
652 sk
->max_objectid
= BTRFS_ROOT_TREE_DIR_OBJECTID
;
653 sk
->max_type
= BTRFS_ROOT_BACKREF_KEY
;
654 sk
->min_type
= BTRFS_ROOT_BACKREF_KEY
;
660 if (defaultsubvolid
== 0) {
665 memset(&args
, 0, sizeof(args
));
667 /* search in the tree of tree roots */
671 * set the min and max to backref keys. The search will
672 * only send back this type of key now.
674 sk
->max_type
= BTRFS_ROOT_BACKREF_KEY
;
675 sk
->min_type
= BTRFS_ROOT_BACKREF_KEY
;
678 * set all the other params to the max, we'll take any objectid
681 sk
->max_objectid
= (u64
)-1;
682 sk
->max_offset
= (u64
)-1;
683 sk
->max_transid
= (u64
)-1;
685 /* just a big number, doesn't matter much */
689 ret
= ioctl(fd
, BTRFS_IOC_TREE_SEARCH
, &args
);
691 fprintf(stderr
, "ERROR: can't perform the search\n");
695 /* the ioctl returns the number of item it found in nr_items */
696 if (sk
->nr_items
== 0)
702 * for each item, pull the key out of the header and then
703 * read the root_ref item it contains
705 for (i
= 0; i
< sk
->nr_items
; i
++) {
706 sh
= (struct btrfs_ioctl_search_header
*)(args
.buf
+ off
);
708 if (sh
->type
== BTRFS_ROOT_BACKREF_KEY
) {
709 ref
= (struct btrfs_root_ref
*)(args
.buf
+ off
);
710 name_len
= ref
->name_len
;
711 name
= (char *)(ref
+ 1);
713 if (sh
->objectid
== defaultsubvolid
) {
714 strncpy(subvol
, name
, name_len
);
715 subvol
[name_len
] = '\0';
716 dprintf("The default subvolume: %s, ID: %llu\n",
717 subvol
, sh
->objectid
);
726 * record the mins in sk so we can make sure the
727 * next search doesn't repeat this root
729 sk
->min_objectid
= sh
->objectid
;
730 sk
->min_type
= sh
->type
;
731 sk
->min_offset
= sh
->offset
;
733 if (subvol
[0] != '\0')
736 /* this iteration is done, step forward one root for the next
739 if (sk
->min_objectid
< (u64
)-1) {
741 sk
->min_type
= BTRFS_ROOT_BACKREF_KEY
;
749 int install_file(const char *path
, int devfd
, struct stat
*rst
)
751 if (fs_type
== EXT2
|| fs_type
== VFAT
|| fs_type
== NTFS
)
752 return ext2_fat_install_file(path
, devfd
, rst
);
753 else if (fs_type
== BTRFS
)
754 return btrfs_install_file(path
, devfd
, rst
);
759 static char devname_buf
[64];
761 static void device_cleanup(void)
767 /* Verify that a device fd and a pathname agree.
768 Return 0 on valid, -1 on error. */
769 static int validate_device(const char *path
, int devfd
)
771 struct stat pst
, dst
;
774 if (stat(path
, &pst
) || fstat(devfd
, &dst
) || statfs(path
, &sfs
))
776 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
777 if (fs_type
== BTRFS
&& sfs
.f_type
== BTRFS_SUPER_MAGIC
)
779 return (pst
.st_dev
== dst
.st_rdev
) ? 0 : -1;
783 static const char *find_device(const char *mtab_file
, dev_t dev
)
788 const char *devname
= NULL
;
791 mtab
= setmntent(mtab_file
, "r");
796 while ((mnt
= getmntent(mtab
))) {
797 /* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
800 if (!strcmp(mnt
->mnt_type
, "btrfs") &&
801 !stat(mnt
->mnt_dir
, &dst
) &&
804 get_default_subvol(mnt
->mnt_dir
, subvol
);
809 if ((!strcmp(mnt
->mnt_type
, "ext2") ||
810 !strcmp(mnt
->mnt_type
, "ext3") ||
811 !strcmp(mnt
->mnt_type
, "ext4")) &&
812 !stat(mnt
->mnt_fsname
, &dst
) &&
813 dst
.st_rdev
== dev
) {
818 if ((!strcmp(mnt
->mnt_type
, "vfat")) &&
819 !stat(mnt
->mnt_fsname
, &dst
) &&
820 dst
.st_rdev
== dev
) {
825 if ((!strcmp(mnt
->mnt_type
, "fuseblk") /* ntfs-3g */ ||
826 !strcmp(mnt
->mnt_type
, "ntfs")) &&
827 !stat(mnt
->mnt_fsname
, &dst
) &&
828 dst
.st_rdev
== dev
) {
838 devname
= strdup(mnt
->mnt_fsname
);
849 * On newer Linux kernels we can use sysfs to get a backwards mapping
850 * from device names to standard filenames
852 static const char *find_device_sysfs(dev_t dev
)
855 char linkname
[PATH_MAX
];
861 snprintf(sysname
, sizeof sysname
, "/sys/dev/block/%u:%u",
862 major(dev
), minor(dev
));
864 llen
= readlink(sysname
, linkname
, sizeof linkname
);
865 if (llen
< 0 || llen
>= sizeof linkname
)
868 linkname
[llen
] = '\0';
870 p
= strrchr(linkname
, '/');
871 p
= p
? p
+1 : linkname
; /* Leave basename */
873 buf
= q
= malloc(strlen(p
) + 6);
877 memcpy(q
, "/dev/", 5);
881 *q
++ = (*p
== '!') ? '/' : *p
;
887 if (!stat(buf
, &st
) && st
.st_dev
== dev
)
888 return buf
; /* Found it! */
896 static const char *find_device_mountinfo(const char *path
, dev_t dev
)
898 const struct mountinfo
*m
;
901 m
= find_mount(path
, NULL
);
903 if (m
->devpath
[0] == '/' && m
->dev
== dev
&&
904 !stat(m
->devpath
, &st
) && S_ISBLK(st
.st_mode
) && st
.st_rdev
== dev
)
910 static const char *find_device_btrfs(const char *path
)
913 struct btrfs_ioctl_fs_info_args fsinfo
;
914 static struct btrfs_ioctl_dev_info_args devinfo
;
915 struct btrfs_super_block sb2
;
916 const char *rv
= NULL
;
918 fd
= open(path
, O_RDONLY
);
922 if (ioctl(fd
, BTRFS_IOC_FS_INFO
, &fsinfo
))
925 /* We do not support multi-device btrfs yet */
926 if (fsinfo
.num_devices
!= 1)
929 /* The one device will have the max devid */
930 memset(&devinfo
, 0, sizeof devinfo
);
931 devinfo
.devid
= fsinfo
.max_id
;
932 if (ioctl(fd
, BTRFS_IOC_DEV_INFO
, &devinfo
))
937 if (devinfo
.path
[0] != '/')
940 fd
= open((const char *)devinfo
.path
, O_RDONLY
);
944 if (xpread(fd
, &sb2
, sizeof sb2
, BTRFS_SUPER_INFO_OFFSET
) != sizeof sb2
)
947 if (memcmp(sb2
.magic
, BTRFS_MAGIC
, BTRFS_MAGIC_L
))
950 if (memcmp(sb2
.fsid
, fsinfo
.fsid
, sizeof fsinfo
.fsid
))
953 if (sb2
.num_devices
!= 1)
956 if (sb2
.dev_item
.devid
!= devinfo
.devid
)
959 if (memcmp(sb2
.dev_item
.uuid
, devinfo
.uuid
, sizeof devinfo
.uuid
))
962 if (memcmp(sb2
.dev_item
.fsid
, fsinfo
.fsid
, sizeof fsinfo
.fsid
))
965 rv
= (const char *)devinfo
.path
; /* It's good! */
973 static const char *get_devname(const char *path
)
975 const char *devname
= NULL
;
979 if (stat(path
, &st
) || !S_ISDIR(st
.st_mode
)) {
980 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
983 if (statfs(path
, &sfs
)) {
984 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
988 if (fs_type
== BTRFS
) {
989 /* For btrfs try to get the device name from btrfs itself */
990 devname
= find_device_btrfs(path
);
994 devname
= find_device_mountinfo(path
, st
.st_dev
);
999 devname
= find_device_sysfs(st
.st_dev
);
1002 /* klibc doesn't have getmntent and friends; instead, just create
1003 a new device with the appropriate device type */
1004 snprintf(devname_buf
, sizeof devname_buf
, "/tmp/dev-%u:%u",
1005 major(st
.st_dev
), minor(st
.st_dev
));
1007 if (mknod(devname_buf
, S_IFBLK
| 0600, st
.st_dev
)) {
1008 fprintf(stderr
, "%s: cannot create device %s\n", program
, devname
);
1012 atexit(device_cleanup
); /* unlink the device node on exit */
1013 devname
= devname_buf
;
1018 devname
= find_device("/proc/mounts", st
.st_dev
);
1021 /* Didn't find it in /proc/mounts, try /etc/mtab */
1022 devname
= find_device("/etc/mtab", st
.st_dev
);
1025 devname
= find_device_sysfs(st
.st_dev
);
1027 fprintf(stderr
, "%s: cannot find device for path %s\n", program
, path
);
1031 fprintf(stderr
, "%s is device %s\n", path
, devname
);
1037 static int open_device(const char *path
, struct stat
*st
, const char **_devname
)
1040 const char *devname
= NULL
;
1044 if (stat(path
, st
) || !S_ISDIR(st
->st_mode
)) {
1045 fprintf(stderr
, "%s: Not a directory: %s\n", program
, path
);
1049 if (statfs(path
, &sfs
)) {
1050 fprintf(stderr
, "%s: statfs %s: %s\n", program
, path
, strerror(errno
));
1053 if (sfs
.f_type
== EXT2_SUPER_MAGIC
)
1055 else if (sfs
.f_type
== BTRFS_SUPER_MAGIC
)
1057 else if (sfs
.f_type
== MSDOS_SUPER_MAGIC
)
1059 else if (sfs
.f_type
== NTFS_SB_MAGIC
||
1060 sfs
.f_type
== FUSE_SUPER_MAGIC
/* ntfs-3g */)
1064 fprintf(stderr
, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
1070 devname
= get_devname(path
);
1072 *_devname
= devname
;
1074 if ((devfd
= open(devname
, O_RDWR
| O_SYNC
)) < 0) {
1075 fprintf(stderr
, "%s: cannot open device %s\n", program
, devname
);
1079 /* Verify that the device we opened is the device intended */
1080 if (validate_device(path
, devfd
)) {
1081 fprintf(stderr
, "%s: path %s doesn't match device %s\n",
1082 program
, path
, devname
);
1089 static int btrfs_read_adv(int devfd
)
1091 if (xpread(devfd
, syslinux_adv
, 2 * ADV_SIZE
, BTRFS_ADV_OFFSET
)
1095 return syslinux_validate_adv(syslinux_adv
) ? 1 : 0;
1098 static int ext_read_adv(const char *path
, int devfd
, const char **namep
)
1103 if (fs_type
== BTRFS
) {
1104 /* btrfs "ldlinux.sys" is in 64k blank area */
1105 return btrfs_read_adv(devfd
);
1107 err
= read_adv(path
, name
= "ldlinux.sys");
1108 if (err
== 2) /* ldlinux.sys does not exist */
1109 err
= read_adv(path
, name
= "extlinux.sys");
1116 static int ext_write_adv(const char *path
, const char *cfg
, int devfd
)
1118 if (fs_type
== BTRFS
) { /* btrfs "ldlinux.sys" is in 64k blank area */
1119 if (xpwrite(devfd
, syslinux_adv
, 2 * ADV_SIZE
,
1120 BTRFS_ADV_OFFSET
) != 2 * ADV_SIZE
) {
1121 perror("writing adv");
1126 return write_adv(path
, cfg
);
1129 int install_loader(const char *path
, int update_only
)
1131 struct stat st
, fst
;
1133 const char *devname
;
1135 devfd
= open_device(path
, &st
, &devname
);
1139 if (update_only
&& !syslinux_already_installed(devfd
)) {
1140 fprintf(stderr
, "%s: no previous syslinux boot sector found\n",
1146 /* Read a pre-existing ADV, if already installed */
1147 if (opt
.reset_adv
) {
1148 syslinux_reset_adv(syslinux_adv
);
1149 } else if (ext_read_adv(path
, devfd
, NULL
) < 0) {
1154 if (modify_adv() < 0) {
1159 /* Install ldlinux.sys */
1160 if (install_file(path
, devfd
, &fst
)) {
1164 if (fst
.st_dev
!= st
.st_dev
) {
1165 fprintf(stderr
, "%s: file system changed under us - aborting!\n",
1172 rv
= install_bootblock(devfd
, devname
);
1180 * Modify the ADV of an existing installation
1182 int modify_existing_adv(const char *path
)
1184 const char *filename
;
1187 devfd
= open_device(path
, NULL
, NULL
);
1192 syslinux_reset_adv(syslinux_adv
);
1193 else if (ext_read_adv(path
, devfd
, &filename
) < 0) {
1197 if (modify_adv() < 0) {
1201 if (ext_write_adv(path
, filename
, devfd
) < 0) {
1209 int main(int argc
, char *argv
[])
1211 parse_options(argc
, argv
, MODE_EXTLINUX
);
1213 if (!opt
.directory
|| opt
.install_mbr
|| opt
.activate_partition
)
1216 if (opt
.update_only
== -1) {
1217 if (opt
.reset_adv
|| opt
.set_once
|| opt
.menu_save
)
1218 return modify_existing_adv(opt
.directory
);
1220 usage(EX_USAGE
, MODE_EXTLINUX
);
1223 return install_loader(opt
.directory
, opt
.update_only
);