diff options
Diffstat (limited to 'extlinux')
-rw-r--r-- | extlinux/Makefile | 11 | ||||
-rw-r--r-- | extlinux/btrfs.h | 179 | ||||
-rw-r--r--[-rwxr-xr-x] | extlinux/main.c | 617 | ||||
-rw-r--r-- | extlinux/mountinfo.c | 277 | ||||
-rw-r--r-- | extlinux/mountinfo.h | 35 | ||||
-rw-r--r-- | extlinux/ntfs.h | 19 |
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_ */ |