diff options
Diffstat (limited to 'extlinux')
-rw-r--r-- | extlinux/Makefile | 3 | ||||
-rw-r--r-- | extlinux/btrfs.h | 76 | ||||
-rw-r--r--[-rwxr-xr-x] | extlinux/main.c | 379 | ||||
-rw-r--r-- | extlinux/mountinfo.c | 277 | ||||
-rw-r--r-- | extlinux/mountinfo.h | 35 | ||||
-rw-r--r-- | extlinux/ntfs.h | 19 |
6 files changed, 688 insertions, 101 deletions
diff --git a/extlinux/Makefile b/extlinux/Makefile index 12213d88..6cde574e 100644 --- a/extlinux/Makefile +++ b/extlinux/Makefile @@ -11,7 +11,7 @@ ## ----------------------------------------------------------------------- ## -## Linux vfat, ext2/ext3/ext4 and btrfs installer +## Linux vfat, ntfs, ext2/ext3/ext4 and btrfs installer ## topdir = .. @@ -25,6 +25,7 @@ CFLAGS = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \ LDFLAGS = SRCS = main.c \ + mountinfo.c \ ../libinstaller/syslxmod.c \ ../libinstaller/syslxopt.c \ ../libinstaller/syslxcom.c \ diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h index be0c24ef..4e2cb317 100644 --- a/extlinux/btrfs.h +++ b/extlinux/btrfs.h @@ -8,8 +8,10 @@ #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; @@ -46,17 +48,52 @@ struct btrfs_dir_item { } __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 @@ -110,6 +147,23 @@ struct btrfs_ioctl_search_header { __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 @@ -123,5 +177,9 @@ struct btrfs_ioctl_search_args { #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 e574051b..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,7 +14,7 @@ /* * 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 */ @@ -45,11 +45,14 @@ #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 @@ -73,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; @@ -207,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; @@ -271,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) { @@ -292,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); @@ -323,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) { @@ -339,7 +343,7 @@ 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) { @@ -348,20 +352,39 @@ int install_bootblock(int fd, const char *device) } 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) { @@ -513,35 +536,6 @@ static int test_issubvolume(char *path) } /* - * Get file handle for a file or dir - */ -static int open_file_or_dir(const char *fname) -{ - int ret; - struct stat st; - DIR *dirstream; - int fd; - - ret = stat(fname, &st); - if (ret < 0) { - return -1; - } - if (S_ISDIR(st.st_mode)) { - dirstream = opendir(fname); - if (!dirstream) { - return -2; - } - fd = dirfd(dirstream); - } else { - fd = open(fname, O_RDWR); - } - if (fd < 0) { - return -3; - } - return fd; -} - -/* * Get the default subvolume of a btrfs filesystem * rootdir: btrfs root dir * subvol: this function will save the default subvolume name here @@ -563,7 +557,7 @@ static char * get_default_subvol(char * rootdir, char * subvol) ret = test_issubvolume(rootdir); if (ret == 1) { - fd = open_file_or_dir(rootdir); + fd = open(rootdir, O_RDONLY); if (fd < 0) { fprintf(stderr, "ERROR: failed to open %s\n", rootdir); } @@ -752,9 +746,9 @@ static char * get_default_subvol(char * rootdir, char * subvol) return subvol; } -int install_file(const char *path, int devfd, struct stat *rst) +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); @@ -772,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__ @@ -803,37 +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) { - if (!subvol[0]) { - get_default_subvol(mnt->mnt_dir, subvol); - } - 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); @@ -842,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; @@ -856,34 +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; + if (!devname) { + devname = find_device_mountinfo(path, st.st_dev); + } -#else +#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; + } - devname = find_device("/proc/mounts", st.st_dev); +#else + if (!devname) { + 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; } @@ -910,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; } @@ -959,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; } } @@ -977,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_ */ |