mnt_table_find_srcpath
mnt_table_find_tag
mnt_table_find_target
+mnt_table_find_target_with_option
mnt_table_first_fs
mnt_table_get_cache
mnt_table_get_intro_comment
if LINUX
libmount_la_SOURCES += \
+ libmount/src/btrfs.c \
+ libmount/src/btrfs.h \
libmount/src/context.c \
libmount/src/context_loopdev.c \
libmount/src/context_mount.c \
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* This is an excerpt from btrfs-progs-v4.3.1
+ * All kernel types are converted to stdint.h types. */
+
+/*
+ * Copyright (C) 2007 Oracle. 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 v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <libio.h>
+#include <stdint.h>
+#include <linux/btrfs.h>
+#include "mountP.h"
+#include "bitops.h"
+
+
+/* linux/btrfs.h lacks large parts of stuff needed for getting default
+ * sub-volume. Suppose that if BTRFS_DIR_ITEM_KEY is not defined, all
+ * declarations are still missing.
+ */
+#ifndef BTRFS_DIR_ITEM_KEY
+
+
+/* from ctree.h */
+
+/*
+ * dir items are the name -> inode pointers in a directory. There is one
+ * for every name in a directory.
+ */
+#define BTRFS_DIR_ITEM_KEY 84
+
+/* holds pointers to all of the tree roots */
+#define BTRFS_ROOT_TREE_OBJECTID 1ULL
+
+/* directory objectid inside the root tree */
+#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
+
+/*
+ * the key defines the order in the tree, and so it also defines (optimal)
+ * block layout. objectid corresonds to the inode number. The flags
+ * tells us things about the object, and is a kind of stream selector.
+ * so for a given inode, keys with flags of 1 might refer to the inode
+ * data, flags of 2 may point to file data in the btree and flags == 3
+ * may point to extents.
+ *
+ * offset is the starting byte offset for this key in the stream.
+ *
+ * btrfs_disk_key is in disk byte order. struct btrfs_key is always
+ * in cpu native order. Otherwise they are identical and their sizes
+ * should be the same (ie both packed)
+ */
+struct btrfs_disk_key {
+ uint64_t objectid; /* little endian */
+ uint8_t type;
+ uint64_t offset; /* little endian */
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ uint64_t transid; /* little endian */
+ uint16_t data_len; /* little endian */
+ uint16_t name_len; /* little endian */
+ uint8_t type;
+} __attribute__ ((__packed__));
+
+#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
+static inline uint##bits##_t btrfs_##name(const type *s) \
+{ \
+ return le##bits##_to_cpu(s->member); \
+} \
+static inline void btrfs_set_##name(type *s, uint##bits##_t val) \
+{ \
+ s->member = cpu_to_le##bits(val); \
+}
+
+/* struct btrfs_disk_key */
+BTRFS_SETGET_STACK_FUNCS(disk_key_objectid, struct btrfs_disk_key,
+ objectid, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_dir_name_len, struct btrfs_dir_item, name_len, 16);
+
+
+/* from rbtree.h */
+
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+struct rb_node {
+ unsigned long __rb_parent_color;
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+ /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+#endif
{ "update", MNT_DEBUG_UPDATE, "mtab, utab updates" },
{ "utils", MNT_DEBUG_UTILS, "misc library utils" },
{ "monitor", MNT_DEBUG_MONITOR, "mount tables monitor" },
+ { "btrfs", MNT_DEBUG_BTRFS, "btrfs specific routines" },
{ NULL, 0 }
};
const char *path, int direction);
extern struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
const char *val, int direction);
+extern struct libmnt_fs *mnt_table_find_target_with_option(struct libmnt_table *tb, const char *path,
+ const char *option, const char *val, int direction);
extern struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
const char *source, int direction);
extern struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb,
mnt_ref_monitor;
mnt_unref_monitor;
} MOUNT_2.25;
+
+MOUNT_2.28 {
+ mnt_table_find_target_with_option;
+} MOUNT_2.26;
#define MNT_DEBUG_CXT (1 << 9)
#define MNT_DEBUG_DIFF (1 << 10)
#define MNT_DEBUG_MONITOR (1 << 11)
+#define MNT_DEBUG_BTRFS (1 << 12)
#define MNT_DEBUG_ALL 0xFFFF
extern int mnt_update_already_done(struct libmnt_update *upd,
struct libmnt_lock *lc);
+#if __linux__
+/* btrfs.c */
+extern uint64_t btrfs_get_default_subvol_id(const char *path);
+#endif
+
#endif /* _LIBMOUNT_PRIVATE_H */
return NULL;
}
+/**
+ * mnt_table_find_target_with_option:
+ * @tb: tab pointer
+ * @path: mountpoint directory
+ * @option: option name (e.g "subvol", "subvolid", ...)
+ * @val: option value or NULL
+ * @direction: MNT_ITER_{FORWARD,BACKWARD}
+ *
+ * Try to lookup an entry in the given tab that matches combination of @path
+ * and @option. In difference to mnt_table_find_target(), only @path iteration
+ * is done. No lookup by device name, no canonicalization.
+ *
+ * Returns: a tab entry or NULL.
+ *
+ * Since: 2.28
+ */
+struct libmnt_fs *mnt_table_find_target_with_option(
+ struct libmnt_table *tb, const char *path,
+ const char *option, const char *val, int direction)
+{
+ struct libmnt_iter itr;
+ struct libmnt_fs *fs = NULL;
+ char *optval = NULL;
+ size_t optvalsz = 0, valsz = val ? strlen(val) : 0;
+
+ if (!tb || !path || !*path || !option || !*option || !val)
+ return NULL;
+ if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s' with OPTION %s %s", path, option, val));
+
+ /* look up by native @target with OPTION */
+ mnt_reset_iter(&itr, direction);
+ while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_streq_target(fs, path)
+ && mnt_fs_get_option(fs, option, &optval, &optvalsz) == 0
+ && (!val || (optvalsz == valsz
+ && strncmp(optval, val, optvalsz) == 0)))
+ return fs;
+ }
+ return NULL;
+}
+
/**
* mnt_table_find_source:
* @tb: tab pointer
}
/* It's possible that fstab_fs source is subdirectory on btrfs
- * subvolume or anothe bind mount. For example:
+ * subvolume or another bind mount. For example:
*
* /dev/sdc /mnt/test btrfs subvol=/anydir
+ * /dev/sdc /mnt/test btrfs defaults
* /mnt/test/foo /mnt/test2 auto bind
*
* in this case, the root for /mnt/test2 will be /anydir/foo on
char *vol = NULL, *p;
size_t sz, volsz = 0;
- if (mnt_fs_get_option(fs, "subvol", &vol, &volsz))
- goto dflt;
+ DBG(BTRFS, ul_debug("lookup for FS root"));
- DBG(TAB, ul_debug("setting FS root: btrfs subvol"));
+ if (mnt_fs_get_option(fs, "subvol", &vol, &volsz)) {
+ /* If fstab entry does not contain "subvol", we have to
+ * check, whether btrfs has default subvolume defined.
+ */
+ uint64_t default_id;
+ char *target;
+ struct libmnt_fs *f;
+ char default_id_str[sizeof(stringify_value(UINT64_MAX))];
+
+ default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
+ if (default_id == UINT64_MAX)
+ goto dflt;
+
+ /* Volume has default subvolume. Check if it matches to
+ * the one in mountinfo.
+ *
+ * Only kernel >= 4.2 reports subvolid. On older
+ * kernels, there is no reasonable way to detect which
+ * subvolume was mounted.
+ */
+ target = mnt_resolve_spec(mnt_fs_get_target(fs), tb->cache);
+ if (!target)
+ goto err;
+
+ snprintf(default_id_str, sizeof(default_id_str), "%llu",
+ (unsigned long long int) default_id);
+
+ DBG(BTRFS, ul_debug("target=%s subvolid=%s", target, default_id_str));
+ f = mnt_table_find_target_with_option(tb, target,
+ "subvolid", default_id_str,
+ MNT_ITER_BACKWARD);
+ if (!tb->cache)
+ free(target);
+ if (!f)
+ goto dflt;
+
+ /* Instead of set of BACKREF queries constructing
+ * subvol path, use the one in mountinfo. Kernel does
+ * the evaluation for us. */
+ DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s", default_id_str));
+ if (mnt_fs_get_option(f, "subvol", &vol, &volsz))
+ goto dflt;
+ } else
+ DBG(BTRFS, ul_debug("setting FS root: btrfs subvol"));
sz = volsz;
if (*vol != '/')