summaryrefslogtreecommitdiff
path: root/extlinux
diff options
context:
space:
mode:
Diffstat (limited to 'extlinux')
-rw-r--r--extlinux/Makefile11
-rw-r--r--extlinux/btrfs.h179
-rw-r--r--[-rwxr-xr-x]extlinux/main.c617
-rw-r--r--extlinux/mountinfo.c277
-rw-r--r--extlinux/mountinfo.h35
-rw-r--r--extlinux/ntfs.h19
6 files changed, 1034 insertions, 104 deletions
diff --git a/extlinux/Makefile b/extlinux/Makefile
index 83cf1a54..6cde574e 100644
--- a/extlinux/Makefile
+++ b/extlinux/Makefile
@@ -11,19 +11,21 @@
## -----------------------------------------------------------------------
##
-## Linux vfat, ext2/ext3/ext4 and btrfs installer
+## Linux vfat, ntfs, ext2/ext3/ext4 and btrfs installer
##
topdir = ..
-include $(topdir)/MCONFIG
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/syslinux.mk
OPTFLAGS = -g -Os
INCLUDES = -I. -I.. -I../libinstaller
CFLAGS = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \
$(OPTFLAGS) $(INCLUDES)
-LDFLAGS = # -s
+LDFLAGS =
SRCS = main.c \
+ mountinfo.c \
../libinstaller/syslxmod.c \
../libinstaller/syslxopt.c \
../libinstaller/syslxcom.c \
@@ -53,6 +55,9 @@ installer: extlinux
extlinux: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
+strip:
+ $(STRIP) extlinux
+
%.o: %.c
$(CC) $(UMAKEDEPS) $(CFLAGS) -c -o $@ $<
%.i: %.c
diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h
index 39a861a5..4e2cb317 100644
--- a/extlinux/btrfs.h
+++ b/extlinux/btrfs.h
@@ -1,22 +1,185 @@
#ifndef _BTRFS_H_
#define _BTRFS_H_
+#include <asm/types.h>
+#include <linux/ioctl.h>
+
#define BTRFS_SUPER_MAGIC 0x9123683E
#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
#define BTRFS_SUPER_INFO_SIZE 4096
#define BTRFS_MAGIC "_BHRfS_M"
+#define BTRFS_MAGIC_L 8
#define BTRFS_CSUM_SIZE 32
#define BTRFS_FSID_SIZE 16
+#define BTRFS_UUID_SIZE 16
+
+typedef __u64 u64;
+typedef __u32 u32;
+typedef __u16 u16;
+typedef __u8 u8;
+typedef u64 __le64;
+typedef u16 __le16;
+
+#define BTRFS_ROOT_BACKREF_KEY 144
+#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
+#define BTRFS_DIR_ITEM_KEY 84
+
+/*
+ * * this is used for both forward and backward root refs
+ * */
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
+struct btrfs_disk_key {
+ __le64 objectid;
+ u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __le64 transid;
+ __le16 data_len;
+ __le16 name_len;
+ u8 type;
+} __attribute__ ((__packed__));
struct btrfs_super_block {
- unsigned char csum[BTRFS_CSUM_SIZE];
- /* the first 3 fields must match struct btrfs_header */
- unsigned char fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
- u64 bytenr; /* this block number */
- u64 flags;
-
- /* allowed to be different from the btrfs_header from here own down */
- u64 magic;
+ uint8_t csum[32];
+ uint8_t fsid[16];
+ uint64_t bytenr;
+ uint64_t flags;
+ uint8_t magic[8];
+ uint64_t generation;
+ uint64_t root;
+ uint64_t chunk_root;
+ uint64_t log_root;
+ uint64_t log_root_transid;
+ uint64_t total_bytes;
+ uint64_t bytes_used;
+ uint64_t root_dir_objectid;
+ uint64_t num_devices;
+ uint32_t sectorsize;
+ uint32_t nodesize;
+ uint32_t leafsize;
+ uint32_t stripesize;
+ uint32_t sys_chunk_array_size;
+ uint64_t chunk_root_generation;
+ uint64_t compat_flags;
+ uint64_t compat_ro_flags;
+ uint64_t incompat_flags;
+ uint16_t csum_type;
+ uint8_t root_level;
+ uint8_t chunk_root_level;
+ uint8_t log_root_level;
+ struct btrfs_dev_item {
+ uint64_t devid;
+ uint64_t total_bytes;
+ uint64_t bytes_used;
+ uint32_t io_align;
+ uint32_t io_width;
+ uint32_t sector_size;
+ uint64_t type;
+ uint64_t generation;
+ uint64_t start_offset;
+ uint32_t dev_group;
+ uint8_t seek_speed;
+ uint8_t bandwidth;
+ uint8_t uuid[16];
+ uint8_t fsid[16];
+ } __attribute__ ((__packed__)) dev_item;
+ uint8_t label[256];
} __attribute__ ((__packed__));
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_VOL_NAME_MAX 255
+#define BTRFS_PATH_NAME_MAX 4087
+
+struct btrfs_ioctl_vol_args {
+ __s64 fd;
+ char name[BTRFS_PATH_NAME_MAX + 1];
+};
+
+struct btrfs_ioctl_search_key {
+ /* which root are we searching. 0 is the tree of tree roots */
+ __u64 tree_id;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_objectid;
+ __u64 max_objectid;
+
+ /* keys returned will be >= min and <= max */
+ __u64 min_offset;
+ __u64 max_offset;
+
+ /* max and min transids to search for */
+ __u64 min_transid;
+ __u64 max_transid;
+
+ /* keys returned will be >= min and <= max */
+ __u32 min_type;
+ __u32 max_type;
+
+ /*
+ * how many items did userland ask for, and how many are we
+ * returning
+ */
+ __u32 nr_items;
+
+ /* align to 64 bits */
+ __u32 unused;
+
+ /* some extra for later */
+ __u64 unused1;
+ __u64 unused2;
+ __u64 unused3;
+ __u64 unused4;
+};
+
+struct btrfs_ioctl_search_header {
+ __u64 transid;
+ __u64 objectid;
+ __u64 offset;
+ __u32 type;
+ __u32 len;
+} __attribute__((may_alias));
+
+#define BTRFS_DEVICE_PATH_NAME_MAX 1024
+struct btrfs_ioctl_dev_info_args {
+ __u64 devid; /* in/out */
+ __u8 uuid[BTRFS_UUID_SIZE]; /* in/out */
+ __u64 bytes_used; /* out */
+ __u64 total_bytes; /* out */
+ __u64 unused[379]; /* pad to 4k */
+ __u8 path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */
+};
+
+struct btrfs_ioctl_fs_info_args {
+ __u64 max_id; /* out */
+ __u64 num_devices; /* out */
+ __u8 fsid[BTRFS_FSID_SIZE]; /* out */
+ __u64 reserved[124]; /* pad to 1k */
+};
+
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
+/*
+ * the buf is an array of search headers where
+ * each header is followed by the actual item
+ * the type field is expanded to 32 bits for alignment
+ */
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
+ struct btrfs_ioctl_dev_info_args)
+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
+ struct btrfs_ioctl_fs_info_args)
+
#endif
diff --git a/extlinux/main.c b/extlinux/main.c
index e5212a95..f0d8e11b 100755..100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2012 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,18 +14,18 @@
/*
* extlinux.c
*
- * Install the syslinux boot block on an fat, ext2/3/4 and btrfs filesystem
+ * Install the syslinux boot block on an fat, ntfs, ext2/3/4 and btrfs filesystem
*/
#define _GNU_SOURCE /* Enable everything */
#include <inttypes.h>
/* This is needed to deal with the kernel headers imported into glibc 3.3.3. */
-typedef uint64_t u64;
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
+#include <dirent.h>
#ifndef __KLIBC__
#include <mntent.h>
#endif
@@ -45,11 +45,14 @@ typedef uint64_t u64;
#include "btrfs.h"
#include "fat.h"
+#include "ntfs.h"
#include "../version.h"
#include "syslxint.h"
#include "syslxcom.h" /* common functions shared with extlinux and syslinux */
+#include "syslxfs.h"
#include "setadv.h"
#include "syslxopt.h" /* unified options */
+#include "mountinfo.h"
#ifdef DEBUG
# define dprintf printf
@@ -65,7 +68,6 @@ typedef uint64_t u64;
boot image, the boot sector is from 0~512, the boot image starts after */
#define BTRFS_BOOTSECT_AREA 65536
#define BTRFS_EXTLINUX_OFFSET SECTOR_SIZE
-#define BTRFS_SUBVOL_OPT "subvol="
#define BTRFS_SUBVOL_MAX 256 /* By btrfs specification */
static char subvol[BTRFS_SUBVOL_MAX];
@@ -74,7 +76,7 @@ static char subvol[BTRFS_SUBVOL_MAX];
/*
* Get the size of a block device
*/
-uint64_t get_size(int devfd)
+static uint64_t get_size(int devfd)
{
uint64_t bytes;
uint32_t sects;
@@ -112,7 +114,7 @@ static int sysfs_get_offset(int devfd, unsigned long *start)
if ((size_t)snprintf(sysfs_name, sizeof sysfs_name,
"/sys/dev/block/%u:%u/start",
- major(st.st_dev), minor(st.st_dev))
+ major(st.st_rdev), minor(st.st_rdev))
>= sizeof sysfs_name)
return -1;
@@ -153,7 +155,7 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
memset(geo, 0, sizeof *geo);
- if (!ioctl(devfd, HDIO_GETGEO, &geo)) {
+ if (!ioctl(devfd, HDIO_GETGEO, geo)) {
goto ok;
} else if (!ioctl(devfd, FDGETPRM, &fd_str)) {
geo->heads = fd_str.head;
@@ -208,14 +210,14 @@ ok:
*
* Returns the number of modified bytes in the boot file.
*/
-int patch_file_and_bootblock(int fd, const char *dir, int devfd)
+static int patch_file_and_bootblock(int fd, const char *dir, int devfd)
{
struct stat dirst, xdst;
struct hd_geometry geo;
sector_t *sectp;
uint64_t totalbytes, totalsectors;
int nsect;
- struct boot_sector *sbs;
+ struct fat_boot_sector *sbs;
char *dirpath, *subpath, *xdirpath;
int rv;
@@ -272,7 +274,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
early bootstrap share code with the FAT version. */
dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors);
- sbs = (struct boot_sector *)syslinux_bootsect;
+ sbs = (struct fat_boot_sector *)syslinux_bootsect;
totalsectors = totalbytes >> SECTOR_SHIFT;
if (totalsectors >= 65536) {
@@ -293,7 +295,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
nsect += 2; /* Two sectors for the ADV */
sectp = alloca(sizeof(sector_t) * nsect);
- if (fs_type == EXT2 || fs_type == VFAT) {
+ if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS) {
if (sectmap(fd, sectp, nsect)) {
perror("bmap");
exit(1);
@@ -324,7 +326,8 @@ int install_bootblock(int fd, const char *device)
{
struct ext2_super_block sb;
struct btrfs_super_block sb2;
- struct boot_sector sb3;
+ struct fat_boot_sector sb3;
+ struct ntfs_boot_sector sb4;
bool ok = false;
if (fs_type == EXT2) {
@@ -340,31 +343,48 @@ int install_bootblock(int fd, const char *device)
perror("reading superblock");
return 1;
}
- if (sb2.magic == *(u64 *)BTRFS_MAGIC)
+ if (!memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
ok = true;
} else if (fs_type == VFAT) {
if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
perror("reading fat superblock");
return 1;
}
- if (sb3.bsResSectors && sb3.bsFATs &&
- (strstr(sb3.bs16.FileSysType, "FAT") ||
- strstr(sb3.bs32.FileSysType, "FAT")))
+ if (fat_check_sb_fields(&sb3))
ok = true;
+ } else if (fs_type == NTFS) {
+ if (xpread(fd, &sb4, sizeof(sb4), 0) != sizeof(sb4)) {
+ perror("reading ntfs superblock");
+ return 1;
+ }
+
+ if (ntfs_check_sb_fields(&sb4))
+ ok = true;
}
if (!ok) {
- fprintf(stderr, "no fat, ext2/3/4 or btrfs superblock found on %s\n",
+ fprintf(stderr, "no fat, ntfs, ext2/3/4 or btrfs superblock found on %s\n",
device);
return 1;
}
if (fs_type == VFAT) {
- struct boot_sector *sbs = (struct boot_sector *)syslinux_bootsect;
- if (xpwrite(fd, &sbs->bsHead, bsHeadLen, 0) != bsHeadLen ||
- xpwrite(fd, &sbs->bsCode, bsCodeLen,
- offsetof(struct boot_sector, bsCode)) != bsCodeLen) {
+ struct fat_boot_sector *sbs = (struct fat_boot_sector *)syslinux_bootsect;
+ if (xpwrite(fd, &sbs->FAT_bsHead, FAT_bsHeadLen, 0) != FAT_bsHeadLen ||
+ xpwrite(fd, &sbs->FAT_bsCode, FAT_bsCodeLen,
+ offsetof(struct fat_boot_sector, FAT_bsCode)) != FAT_bsCodeLen) {
perror("writing fat bootblock");
return 1;
}
+ } else if (fs_type == NTFS) {
+ struct ntfs_boot_sector *sbs =
+ (struct ntfs_boot_sector *)syslinux_bootsect;
+ if (xpwrite(fd, &sbs->NTFS_bsHead,
+ NTFS_bsHeadLen, 0) != NTFS_bsHeadLen ||
+ xpwrite(fd, &sbs->NTFS_bsCode, NTFS_bsCodeLen,
+ offsetof(struct ntfs_boot_sector,
+ NTFS_bsCode)) != NTFS_bsCodeLen) {
+ perror("writing ntfs bootblock");
+ return 1;
+ }
} else {
if (xpwrite(fd, syslinux_bootsect, syslinux_bootsect_len, 0)
!= syslinux_bootsect_len) {
@@ -494,9 +514,241 @@ int btrfs_install_file(const char *path, int devfd, struct stat *rst)
return 0;
}
-int install_file(const char *path, int devfd, struct stat *rst)
+/*
+ * * test if path is a subvolume:
+ * * this function return
+ * * 0-> path exists but it is not a subvolume
+ * * 1-> path exists and it is a subvolume
+ * * -1 -> path is unaccessible
+ * */
+static int test_issubvolume(char *path)
+{
+
+ struct stat st;
+ int res;
+
+ res = stat(path, &st);
+ if(res < 0 )
+ return -1;
+
+ return (st.st_ino == 256) && S_ISDIR(st.st_mode);
+
+}
+
+/*
+ * Get the default subvolume of a btrfs filesystem
+ * rootdir: btrfs root dir
+ * subvol: this function will save the default subvolume name here
+ */
+static char * get_default_subvol(char * rootdir, char * subvol)
+{
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ struct btrfs_ioctl_search_header *sh;
+ int ret, i;
+ int fd;
+ struct btrfs_root_ref *ref;
+ struct btrfs_dir_item *dir_item;
+ unsigned long off = 0;
+ int name_len;
+ char *name;
+ char dirname[4096];
+ u64 defaultsubvolid = 0;
+
+ ret = test_issubvolume(rootdir);
+ if (ret == 1) {
+ fd = open(rootdir, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
+ }
+ ret = fd;
+ }
+ if (ret <= 0) {
+ subvol[0] = '\0';
+ return NULL;
+ }
+
+ memset(&args, 0, sizeof(args));
+
+ /* search in the tree of tree roots */
+ sk->tree_id = 1;
+
+ /*
+ * set the min and max to backref keys. The search will
+ * only send back this type of key now.
+ */
+ sk->max_type = BTRFS_DIR_ITEM_KEY;
+ sk->min_type = BTRFS_DIR_ITEM_KEY;
+
+ /*
+ * set all the other params to the max, we'll take any objectid
+ * and any trans
+ */
+ sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+ sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+
+ sk->max_offset = (u64)-1;
+ sk->min_offset = 0;
+ sk->max_transid = (u64)-1;
+
+ /* just a big number, doesn't matter much */
+ sk->nr_items = 4096;
+
+ while(1) {
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: can't perform the search\n");
+ subvol[0] = '\0';
+ return NULL;
+ }
+ /* the ioctl returns the number of item it found in nr_items */
+ if (sk->nr_items == 0) {
+ break;
+ }
+
+ off = 0;
+
+ /*
+ * for each item, pull the key out of the header and then
+ * read the root_ref item it contains
+ */
+ for (i = 0; i < sk->nr_items; i++) {
+ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
+ off += sizeof(*sh);
+ if (sh->type == BTRFS_DIR_ITEM_KEY) {
+ dir_item = (struct btrfs_dir_item *)(args.buf + off);
+ name_len = dir_item->name_len;
+ name = (char *)(dir_item + 1);
+
+
+ /*add_root(&root_lookup, sh->objectid, sh->offset,
+ dir_id, name, name_len);*/
+ strncpy(dirname, name, name_len);
+ dirname[name_len] = '\0';
+ if (strcmp(dirname, "default") == 0) {
+ defaultsubvolid = dir_item->location.objectid;
+ break;
+ }
+ }
+ off += sh->len;
+
+ /*
+ * record the mins in sk so we can make sure the
+ * next search doesn't repeat this root
+ */
+ sk->min_objectid = sh->objectid;
+ sk->min_type = sh->type;
+ sk->max_type = sh->type;
+ sk->min_offset = sh->offset;
+ }
+ if (defaultsubvolid != 0)
+ break;
+ sk->nr_items = 4096;
+ /* this iteration is done, step forward one root for the next
+ * ioctl
+ */
+ if (sk->min_objectid < (u64)-1) {
+ sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+ sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->min_offset = 0;
+ } else
+ break;
+ }
+
+ if (defaultsubvolid == 0) {
+ subvol[0] = '\0';
+ return NULL;
+ }
+
+ memset(&args, 0, sizeof(args));
+
+ /* search in the tree of tree roots */
+ sk->tree_id = 1;
+
+ /*
+ * set the min and max to backref keys. The search will
+ * only send back this type of key now.
+ */
+ sk->max_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+
+ /*
+ * set all the other params to the max, we'll take any objectid
+ * and any trans
+ */
+ sk->max_objectid = (u64)-1;
+ sk->max_offset = (u64)-1;
+ sk->max_transid = (u64)-1;
+
+ /* just a big number, doesn't matter much */
+ sk->nr_items = 4096;
+
+ while(1) {
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: can't perform the search\n");
+ subvol[0] = '\0';
+ return NULL;
+ }
+ /* the ioctl returns the number of item it found in nr_items */
+ if (sk->nr_items == 0)
+ break;
+
+ off = 0;
+
+ /*
+ * for each item, pull the key out of the header and then
+ * read the root_ref item it contains
+ */
+ for (i = 0; i < sk->nr_items; i++) {
+ sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
+ off += sizeof(*sh);
+ if (sh->type == BTRFS_ROOT_BACKREF_KEY) {
+ ref = (struct btrfs_root_ref *)(args.buf + off);
+ name_len = ref->name_len;
+ name = (char *)(ref + 1);
+
+ if (sh->objectid == defaultsubvolid) {
+ strncpy(subvol, name, name_len);
+ subvol[name_len] = '\0';
+ dprintf("The default subvolume: %s, ID: %llu\n",
+ subvol, sh->objectid);
+ break;
+ }
+
+ }
+
+ off += sh->len;
+
+ /*
+ * record the mins in sk so we can make sure the
+ * next search doesn't repeat this root
+ */
+ sk->min_objectid = sh->objectid;
+ sk->min_type = sh->type;
+ sk->min_offset = sh->offset;
+ }
+ if (subvol[0] != '\0')
+ break;
+ sk->nr_items = 4096;
+ /* this iteration is done, step forward one root for the next
+ * ioctl
+ */
+ if (sk->min_objectid < (u64)-1) {
+ sk->min_objectid++;
+ sk->min_type = BTRFS_ROOT_BACKREF_KEY;
+ sk->min_offset = 0;
+ } else
+ break;
+ }
+ return subvol;
+}
+
+static int install_file(const char *path, int devfd, struct stat *rst)
{
- if (fs_type == EXT2 || fs_type == VFAT)
+ if (fs_type == EXT2 || fs_type == VFAT || fs_type == NTFS)
return ext2_fat_install_file(path, devfd, rst);
else if (fs_type == BTRFS)
return btrfs_install_file(path, devfd, rst);
@@ -514,17 +766,33 @@ static void device_cleanup(void)
/* Verify that a device fd and a pathname agree.
Return 0 on valid, -1 on error. */
+static int validate_device_btrfs(int pathfd, int devfd);
static int validate_device(const char *path, int devfd)
{
struct stat pst, dst;
struct statfs sfs;
+ int pfd;
+ int rv = -1;
+
+ pfd = open(path, O_RDONLY|O_DIRECTORY);
+ if (pfd < 0)
+ goto err;
+
+ if (fstat(pfd, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
+ goto err;
- if (stat(path, &pst) || fstat(devfd, &dst) || statfs(path, &sfs))
- return -1;
/* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
- if (fs_type == BTRFS && sfs.f_type == BTRFS_SUPER_MAGIC)
- return 0;
- return (pst.st_dev == dst.st_rdev) ? 0 : -1;
+ if (fs_type == BTRFS) {
+ if (sfs.f_type == BTRFS_SUPER_MAGIC)
+ rv = validate_device_btrfs(pfd, devfd);
+ } else {
+ rv = (pst.st_dev == dst.st_rdev) ? 0 : -1;
+ }
+
+err:
+ if (pfd >= 0)
+ close(pfd);
+ return rv;
}
#ifndef __KLIBC__
@@ -545,47 +813,46 @@ static const char *find_device(const char *mtab_file, dev_t dev)
/* btrfs st_dev is not matched with mnt st_rdev, it is a known issue */
switch (fs_type) {
case BTRFS:
- if (!strcmp(mnt->mnt_type, "btrfs") &&
- !stat(mnt->mnt_dir, &dst) &&
- dst.st_dev == dev) {
- char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
-
- if (opt) {
- if (!subvol[0]) {
- char *tmp;
-
- strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
- tmp = strchr(subvol, 32);
- if (tmp)
- *tmp = '\0';
- }
- break; /* should break and let upper layer try again */
- } else
- done = true;
- }
- break;
+ if (!strcmp(mnt->mnt_type, "btrfs") &&
+ !stat(mnt->mnt_dir, &dst) &&
+ dst.st_dev == dev) {
+ if (!subvol[0])
+ get_default_subvol(mnt->mnt_dir, subvol);
+ done = true;
+ }
+ break;
case EXT2:
- if ((!strcmp(mnt->mnt_type, "ext2") ||
- !strcmp(mnt->mnt_type, "ext3") ||
- !strcmp(mnt->mnt_type, "ext4")) &&
- !stat(mnt->mnt_fsname, &dst) &&
- dst.st_rdev == dev) {
- done = true;
- break;
- }
+ if ((!strcmp(mnt->mnt_type, "ext2") ||
+ !strcmp(mnt->mnt_type, "ext3") ||
+ !strcmp(mnt->mnt_type, "ext4")) &&
+ !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
case VFAT:
- if ((!strcmp(mnt->mnt_type, "vfat")) &&
- !stat(mnt->mnt_fsname, &dst) &&
- dst.st_rdev == dev) {
- done = true;
- break;
- }
+ if ((!strcmp(mnt->mnt_type, "vfat")) &&
+ !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
+ case NTFS:
+ if ((!strcmp(mnt->mnt_type, "fuseblk") /* ntfs-3g */ ||
+ !strcmp(mnt->mnt_type, "ntfs")) &&
+ !stat(mnt->mnt_fsname, &dst) &&
+ dst.st_rdev == dev) {
+ done = true;
+ break;
+ }
+
+ break;
case NONE:
break;
}
if (done) {
- devname = strdup(mnt->mnt_fsname);
- break;
+ devname = strdup(mnt->mnt_fsname);
+ break;
}
}
endmntent(mtab);
@@ -594,6 +861,158 @@ static const char *find_device(const char *mtab_file, dev_t dev)
}
#endif
+/*
+ * On newer Linux kernels we can use sysfs to get a backwards mapping
+ * from device names to standard filenames
+ */
+static const char *find_device_sysfs(dev_t dev)
+{
+ char sysname[64];
+ char linkname[PATH_MAX];
+ ssize_t llen;
+ char *p, *q;
+ char *buf = NULL;
+ struct stat st;
+
+ snprintf(sysname, sizeof sysname, "/sys/dev/block/%u:%u",
+ major(dev), minor(dev));
+
+ llen = readlink(sysname, linkname, sizeof linkname);
+ if (llen < 0 || llen >= sizeof linkname)
+ goto err;
+
+ linkname[llen] = '\0';
+
+ p = strrchr(linkname, '/');
+ p = p ? p+1 : linkname; /* Leave basename */
+
+ buf = q = malloc(strlen(p) + 6);
+ if (!buf)
+ goto err;
+
+ memcpy(q, "/dev/", 5);
+ q += 5;
+
+ while (*p) {
+ *q++ = (*p == '!') ? '/' : *p;
+ p++;
+ }
+
+ *q = '\0';
+
+ if (!stat(buf, &st) && st.st_dev == dev)
+ return buf; /* Found it! */
+
+err:
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+static const char *find_device_mountinfo(const char *path, dev_t dev)
+{
+ const struct mountinfo *m;
+ struct stat st;
+
+ m = find_mount(path, NULL);
+
+ if (m->devpath[0] == '/' && m->dev == dev &&
+ !stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
+ return m->devpath;
+ else
+ return NULL;
+}
+
+static int validate_device_btrfs(int pfd, int dfd)
+{
+ struct btrfs_ioctl_fs_info_args fsinfo;
+ static struct btrfs_ioctl_dev_info_args devinfo;
+ struct btrfs_super_block sb2;
+
+ if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
+ return -1;
+
+ /* We do not support multi-device btrfs yet */
+ if (fsinfo.num_devices != 1)
+ return -1;
+
+ /* The one device will have the max devid */
+ memset(&devinfo, 0, sizeof devinfo);
+ devinfo.devid = fsinfo.max_id;
+ if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
+ return -1;
+
+ if (devinfo.path[0] != '/')
+ return -1;
+
+ if (xpread(dfd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2)
+ return -1;
+
+ if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
+ return -1;
+
+ if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
+ return -1;
+
+ if (sb2.num_devices != 1)
+ return -1;
+
+ if (sb2.dev_item.devid != devinfo.devid)
+ return -1;
+
+ if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid))
+ return -1;
+
+ if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
+ return -1;
+
+ return 0; /* It's good! */
+}
+
+static const char *find_device_btrfs(const char *path)
+{
+ int pfd, dfd;
+ struct btrfs_ioctl_fs_info_args fsinfo;
+ static struct btrfs_ioctl_dev_info_args devinfo;
+ const char *rv = NULL;
+
+ pfd = dfd = -1;
+
+ pfd = open(path, O_RDONLY);
+ if (pfd < 0)
+ goto err;
+
+ if (ioctl(pfd, BTRFS_IOC_FS_INFO, &fsinfo))
+ goto err;
+
+ /* We do not support multi-device btrfs yet */
+ if (fsinfo.num_devices != 1)
+ goto err;
+
+ /* The one device will have the max devid */
+ memset(&devinfo, 0, sizeof devinfo);
+ devinfo.devid = fsinfo.max_id;
+ if (ioctl(pfd, BTRFS_IOC_DEV_INFO, &devinfo))
+ goto err;
+
+ if (devinfo.path[0] != '/')
+ goto err;
+
+ dfd = open((const char *)devinfo.path, O_RDONLY);
+ if (dfd < 0)
+ goto err;
+
+ if (!validate_device_btrfs(pfd, dfd))
+ rv = (const char *)devinfo.path; /* It's good! */
+
+err:
+ if (pfd >= 0)
+ close(pfd);
+ if (dfd >= 0)
+ close(dfd);
+ return rv;
+}
+
static const char *get_devname(const char *path)
{
const char *devname = NULL;
@@ -608,48 +1027,57 @@ static const char *get_devname(const char *path)
fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno));
return devname;
}
-#ifdef __KLIBC__
- /* klibc doesn't have getmntent and friends; instead, just create
- a new device with the appropriate device type */
- snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
- major(st.st_dev), minor(st.st_dev));
+ if (opt.device)
+ devname = opt.device;
- if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
- fprintf(stderr, "%s: cannot create device %s\n", program, devname);
- return devname;
+ if (!devname){
+ if (fs_type == BTRFS) {
+ /* For btrfs try to get the device name from btrfs itself */
+ devname = find_device_btrfs(path);
+ }
}
- atexit(device_cleanup); /* unlink the device node on exit */
- devname = devname_buf;
-
-#else
+ if (!devname) {
+ devname = find_device_mountinfo(path, st.st_dev);
+ }
- /* check /etc/mtab first, since btrfs subvol info is only in here */
- devname = find_device("/etc/mtab", st.st_dev);
- if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
- char parent[256];
- char *tmp;
-
- strcpy(parent, path);
- tmp = strrchr(parent, '/');
- if (tmp) {
- *tmp = '\0';
- fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
- devname = get_devname(parent);
- } else
- devname = NULL;
+#ifdef __KLIBC__
+ if (!devname) {
+ devname = find_device_sysfs(st.st_dev);
+ }
+ if (!devname) {
+ /* klibc doesn't have getmntent and friends; instead, just create
+ a new device with the appropriate device type */
+ snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u",
+ major(st.st_dev), minor(st.st_dev));
+
+ if (mknod(devname_buf, S_IFBLK | 0600, st.st_dev)) {
+ fprintf(stderr, "%s: cannot create device %s\n", program, devname);
+ return devname;
+ }
+
+ atexit(device_cleanup); /* unlink the device node on exit */
+ devname = devname_buf;
}
+
+#else
if (!devname) {
- /* Didn't find it in /etc/mtab, try /proc/mounts */
devname = find_device("/proc/mounts", st.st_dev);
}
if (!devname) {
+ /* Didn't find it in /proc/mounts, try /etc/mtab */
+ devname = find_device("/etc/mtab", st.st_dev);
+ }
+ if (!devname) {
+ devname = find_device_sysfs(st.st_dev);
+
fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
return devname;
}
fprintf(stderr, "%s is device %s\n", path, devname);
+
#endif
return devname;
}
@@ -676,9 +1104,12 @@ static int open_device(const char *path, struct stat *st, const char **_devname)
fs_type = BTRFS;
else if (sfs.f_type == MSDOS_SUPER_MAGIC)
fs_type = VFAT;
+ else if (sfs.f_type == NTFS_SB_MAGIC ||
+ sfs.f_type == FUSE_SUPER_MAGIC /* ntfs-3g */)
+ fs_type = NTFS;
if (!fs_type) {
- fprintf(stderr, "%s: not a fat, ext2/3/4 or btrfs filesystem: %s\n",
+ fprintf(stderr, "%s: not a fat, ntfs, ext2/3/4 or btrfs filesystem: %s\n",
program, path);
return -1;
}
@@ -725,7 +1156,7 @@ static int ext_read_adv(const char *path, int devfd, const char **namep)
if (err == 2) /* ldlinux.sys does not exist */
err = read_adv(path, name = "extlinux.sys");
if (namep)
- *namep = name;
+ *namep = name;
return err;
}
}
@@ -743,7 +1174,7 @@ static int ext_write_adv(const char *path, const char *cfg, int devfd)
return write_adv(path, cfg);
}
-int install_loader(const char *path, int update_only)
+static int install_loader(const char *path, int update_only)
{
struct stat st, fst;
int devfd, rv;
diff --git a/extlinux/mountinfo.c b/extlinux/mountinfo.c
new file mode 100644
index 00000000..2be87580
--- /dev/null
+++ b/extlinux/mountinfo.c
@@ -0,0 +1,277 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2012 Intel Corporation; All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include "mountinfo.h"
+
+/*
+ * Parse /proc/self/mountinfo
+ */
+static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec)
+{
+ int ch;
+ char *p = string_buf;
+
+ for (;;) {
+ if (!string_len)
+ return -2; /* String too long */
+
+ ch = getc(f);
+ if (ch == EOF) {
+ return -1; /* Got EOF */
+ } else if (ch == ' ' || ch == '\t' || ch == '\n') {
+ *ec = ch;
+ *p = '\0';
+ return p - string_buf;
+ } else if (ch == '\\') {
+ /* Should always be followed by 3 octal digits in 000..377 */
+ int oc = 0;
+ int i;
+ for (i = 0; i < 3; i++) {
+ ch = getc(f);
+ if (ch < '0' || ch > '7' || (i == 0 && ch > '3'))
+ return -1; /* Bad escape sequence */
+ oc = (oc << 3) + (ch - '0');
+ }
+ if (!oc)
+ return -1; /* We can't handle \000 */
+ *p++ = oc;
+ string_len--;
+ } else {
+ *p++ = ch;
+ string_len--;
+ }
+ }
+}
+
+static void free_mountinfo(struct mountinfo *m)
+{
+ struct mountinfo *nx;
+
+ while (m) {
+ free((char *)m->root);
+ free((char *)m->path);
+ free((char *)m->fstype);
+ free((char *)m->devpath);
+ free((char *)m->mountopt);
+ nx = m->next;
+ free(m);
+ m = nx;
+ }
+}
+
+static struct mountinfo *head = NULL, **tail = &head;
+
+static void parse_mountinfo(void)
+{
+ FILE *f;
+ struct mountinfo *m, *mm;
+ char string_buf[PATH_MAX*8];
+ int n;
+ char ec, *ep;
+ unsigned int ma, mi;
+
+ f = fopen("/proc/self/mountinfo", "r");
+ if (!f)
+ return;
+
+ for (;;) {
+ m = malloc(sizeof(struct mountinfo));
+ if (!m)
+ break;
+ memset(m, 0, sizeof *m);
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ break;
+
+ m->mountid = strtoul(string_buf, &ep, 10);
+ if (*ep)
+ break;
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ break;
+
+ m->parentid = strtoul(string_buf, &ep, 10);
+ if (*ep)
+ break;
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ break;
+
+ if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2)
+ break;
+
+ m->dev = makedev(ma, mi);
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 1 || ec == '\n' || string_buf[0] != '/')
+ break;
+
+ m->root = strdup(string_buf);
+ if (!m->root)
+ break;
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 1 || ec == '\n' || string_buf[0] != '/')
+ break;
+
+ m->path = strdup(string_buf);
+ m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */
+
+ /* Skip tagged attributes */
+ do {
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ goto quit;
+ } while (n != 1 || string_buf[0] != '-');
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ break;
+
+ m->fstype = strdup(string_buf);
+ if (!m->fstype)
+ break;
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0 || ec == '\n')
+ break;
+
+ m->devpath = strdup(string_buf);
+ if (!m->devpath)
+ break;
+
+ n = get_string(f, string_buf, sizeof string_buf, &ec);
+ if (n < 0)
+ break;
+
+ m->mountopt = strdup(string_buf);
+ if (!m->mountopt)
+ break;
+
+ /* Skip any previously unknown fields */
+ while (ec != '\n' && ec != EOF)
+ ec = getc(f);
+
+ *tail = m;
+ tail = &m->next;
+ }
+quit:
+ fclose(f);
+ free_mountinfo(m);
+
+ /* Create parent links */
+ for (m = head; m; m = m->next) {
+ for (mm = head; mm; mm = mm->next) {
+ if (m->parentid == mm->mountid) {
+ m->parent = mm;
+ if (!strcmp(m->path, mm->path))
+ mm->hidden = 1; /* Hidden under another mount */
+ break;
+ }
+ }
+ }
+}
+
+const struct mountinfo *find_mount(const char *path, char **subpath)
+{
+ static int done_init;
+ char *real_path;
+ const struct mountinfo *m, *best;
+ struct stat st;
+ int len, matchlen;
+
+ if (!done_init) {
+ parse_mountinfo();
+ done_init = 1;
+ }
+
+ if (stat(path, &st))
+ return NULL;
+
+ real_path = realpath(path, NULL);
+ if (!real_path)
+ return NULL;
+
+ /*
+ * Tricky business: we need the longest matching subpath
+ * which isn't a parent of the same subpath.
+ */
+ len = strlen(real_path);
+ matchlen = 0;
+ best = NULL;
+ for (m = head; m; m = m->next) {
+ if (m->hidden)
+ continue; /* Hidden underneath another mount */
+
+ if (m->pathlen > len)
+ continue; /* Cannot possibly match */
+
+ if (m->pathlen < matchlen)
+ continue; /* No point in testing this one */
+
+ if (st.st_dev == m->dev &&
+ !memcmp(m->path, real_path, m->pathlen) &&
+ (real_path[m->pathlen] == '/' || real_path[m->pathlen] == '\0')) {
+ matchlen = m->pathlen;
+ best = m;
+ }
+ }
+
+ if (best && subpath) {
+ if (real_path[best->pathlen] == '\0')
+ *subpath = strdup("/");
+ else
+ *subpath = strdup(real_path + best->pathlen);
+ }
+
+ return best;
+}
+
+#ifdef TEST
+
+int main(int argc, char *argv[])
+{
+ int i;
+ const struct mountinfo *m;
+ char *subpath;
+
+ parse_mountinfo();
+
+ for (i = 1; i < argc; i++) {
+ m = find_mount(argv[i], &subpath);
+ if (!m) {
+ printf("%s: %s\n", argv[i], strerror(errno));
+ continue;
+ }
+
+ printf("%s -> %s @ %s(%u,%u):%s %s %s\n",
+ argv[i], subpath, m->devpath, major(m->dev), minor(m->dev),
+ m->root, m->fstype, m->mountopt);
+ printf("Usable device: %s\n", find_device(m->dev, m->devpath));
+ free(subpath);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/extlinux/mountinfo.h b/extlinux/mountinfo.h
new file mode 100644
index 00000000..9cbcac12
--- /dev/null
+++ b/extlinux/mountinfo.h
@@ -0,0 +1,35 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2012 Intel Corporation; All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef SYSLINUX_MOUNTINFO_H
+#define SYSLINUX_MOUNTINFO_H
+
+#include <sys/types.h>
+
+struct mountinfo {
+ struct mountinfo *next;
+ struct mountinfo *parent;
+ const char *root;
+ const char *path;
+ const char *fstype;
+ const char *devpath;
+ const char *mountopt;
+ int mountid;
+ int parentid;
+ int pathlen;
+ int hidden;
+ dev_t dev;
+};
+
+const struct mountinfo *find_mount(const char *path, char **subpath);
+
+#endif /* SYSLINUX_MOUNTINFO_H */
diff --git a/extlinux/ntfs.h b/extlinux/ntfs.h
new file mode 100644
index 00000000..d907d452
--- /dev/null
+++ b/extlinux/ntfs.h
@@ -0,0 +1,19 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Paulo Alcantara <pcacjr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef _NTFS_H_
+#define _NTFS_H_
+
+#define NTFS_SB_MAGIC 0x5346544E
+#define FUSE_SUPER_MAGIC 0x65735546
+
+#endif /* _NTFS_H_ */