summaryrefslogtreecommitdiff
path: root/extlinux/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'extlinux/main.c')
-rw-r--r--[-rwxr-xr-x]extlinux/main.c617
1 files changed, 524 insertions, 93 deletions
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;