diff options
author | Stanislav Brabec | 2016-01-21 22:58:31 +0100 |
---|---|---|
committer | Karel Zak | 2016-01-26 11:14:25 +0100 |
commit | 2cd28fc82d0c947472a4700d5e764265916fba1e (patch) | |
tree | 6ed461930795e9ae8ab84a6193d1248c40f9f3bf /libmount/src/btrfs.c | |
parent | doap: fix download URL (diff) | |
download | kernel-qcow2-util-linux-2cd28fc82d0c947472a4700d5e764265916fba1e.tar.gz kernel-qcow2-util-linux-2cd28fc82d0c947472a4700d5e764265916fba1e.tar.xz kernel-qcow2-util-linux-2cd28fc82d0c947472a4700d5e764265916fba1e.zip |
libmount: handle btrfs default subvolume mount
When mounting btrfs volume without subvol= and subvolid=, and the
btrfs volume has default subvolume defined, mount() mounts the default
subvolume and not the volume root as other filesystems do.
To handle this situation correctly (for example for "mount -a"),
libmount has to be capable to detect default subvolume.
Add btrfs.c and btrfs.h that implement needed functions.
This patch adds mnt_table_find_target_with_option() to the library API.
Known problems not covered by this patch:
- Use of subvolid= in fstab is not yet handled.
- Use of type auto in combination with subvol= in fstab is not yet
handled.
- Use of btrfs in loop devices, where image file is specified in fstab is
not yet handled (use of /dev/loop0 in fstab works).
- If fstab uses subvol=, and subvol path changes since last "mount -a",
subsequent "mount -a" will not recognize that it is already mounted,
and it will attempt to mount it second time. To fix it, libmount should
remember subvolid in time of mount (subvolid is unique for the
subvolume, subvol is not).
- mountinfo contains subvol and subvolid since kernel 4.2. Before kernel
4.2, there is no reasonable way to solve this situation. (One would
create temporary mount point, mount the default, call needed ioctl() to
determine what was mounted, deduce the default subvolume, compare it
with subvolume of mounted volume, unmount and return result.)
How to reproduce:
truncate -s1G btrfs_test.img
mkdir -p btrfs_mnt
/sbin/mkfs.btrfs -f -d single -m single ./btrfs_test.img
mount -o loop btrfs_test.img btrfs_mnt
pushd .
cd btrfs_mnt
mkdir -p d0/dd0/ddd0
cd d0/dd0/ddd0
touch file{1..5}
btrfs subvol create s1
cd s1
touch file{1..5}
mkdir -p d1/dd1/ddd1
cd d1/dd1/ddd1
btrfs subvol create s2
rid=$(btrfs inspect rootid s2)
echo new default $rid
btrfs subvol get-default .
btrfs subvol set-default $rid .
popd
umount btrfs_mnt
losetup /dev/loop0 $PWD/btrfs_test.img
echo "/dev/loop0 $PWD/btrfs_mnt btrfs defaults 0 0" >>/etc/fstab
mount -a
mount -a
umount btrfs_mnt
sed -i "/\/dev\/loop0/d" /etc/fstab
losetup -d /dev/loop0
rm btrfs_test.img
rmdir btrfs_mnt
Current behavior:
mount: /dev/loop0 is already mounted or /root/btrfs_mnt busy
/dev/loop0 is already mounted on /root/btrfs_mnt
Expected behavior is to ignore already mounted FS.
[kzak@redhat.com: - make 'var' optional for mnt_table_find_target_with_option(),
- add mnt_table_find_target_with_option() to symbols table and docs
- add "btrfs" string between supported debug modes
- minor coding style changes]
Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
Cc: David Štěrba <dsterba@suse.cz>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libmount/src/btrfs.c')
-rw-r--r-- | libmount/src/btrfs.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/libmount/src/btrfs.c b/libmount/src/btrfs.c new file mode 100644 index 000000000..3c61a2a03 --- /dev/null +++ b/libmount/src/btrfs.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 David Sterba <dsterba@suse.cz> + * Copyright (C) 2016 Stanislav Brabec <sbrabec@suse.cz> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/* + * SECTION: btrfs + * @title: btrfs + * @short_description: special function for btrfs + * + * btrfs contains function needed for manipulation with btrfs. + */ +#include <dirent.h> +#include <sys/ioctl.h> +#include <linux/magic.h> +#include "btrfs.h" + +/* + * btrfs_get_default_subvol_id: + * @path: Path to mounted btrfs volume + * + * Searches for the btrfs default subvolume id. + * + * Returns: default subvolume id or UINT64_MAX (-1) in case of no + * default subvolume or error. In case of error, errno is set + * properly. + */ +uint64_t btrfs_get_default_subvol_id(const char *path) +{ + int iocret; + int fd; + DIR *dirstream = NULL; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + uint64_t found = UINT64_MAX; + + dirstream = opendir(path); + if (!dirstream) { + DBG(BTRFS, ul_debug("opendir() failed for \"%s\" [errno=%d %m]", path, errno)); + return UINT64_MAX; + } + fd = dirfd(dirstream); + if (fd < 0) { + DBG(BTRFS, ul_debug("dirfd(opendir()) failed for \"%s\" [errno=%d %m]", path, errno)); + goto out; + } + + memset(&args, 0, sizeof(args)); + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; + sk->max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID; + sk->min_type = BTRFS_DIR_ITEM_KEY; + sk->max_type = BTRFS_DIR_ITEM_KEY; + sk->max_offset = UINT64_MAX; + sk->max_transid = UINT64_MAX; + sk->nr_items = 1; + + iocret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + if (iocret < 0) { + DBG(BTRFS, ul_debug("ioctl() failed for \"%s\" [errno=%d %m]", path, errno)); + goto out; + } + + /* the ioctl returns the number of items it found in nr_items */ + if (sk->nr_items == 0) { + DBG(BTRFS, ul_debug("root tree dir object id not found")); + goto out; + } + DBG(BTRFS, ul_debug("found %d root tree dir object id items", sk->nr_items)); + + sh = (struct btrfs_ioctl_search_header *)args.buf; + + if (sh->type == BTRFS_DIR_ITEM_KEY) { + struct btrfs_dir_item *di; + int name_len; + char *name; + + di = (struct btrfs_dir_item *)(sh + 1); + name_len = btrfs_stack_dir_name_len(di); + name = (char *)(di + 1); + + if (!strncmp("default", name, name_len)) { + found = btrfs_disk_key_objectid(&di->location); + DBG(BTRFS, ul_debug("\"default\" id is %llu", (unsigned long long)found)); + } else { + DBG(BTRFS, ul_debug("\"default\" id not found in tree root")); + goto out; + } + } else { + DBG(BTRFS, ul_debug("unexpected type found: %d", (int)sh->type)); + goto out; + } + +out: + closedir(dirstream); + + return found; +} |