[lxc-devel] [PATCH RFC] btrfs: support recursive subvolume deletion

Serge Hallyn serge.hallyn at ubuntu.com
Tue Jul 29 02:41:05 UTC 2014


[[ This seems to DTRT in my local tests.  However this is the result of
me wasting a weekend trying to understand how to use the btrfs ioctl,
so it's possible that I completely misunderstand something in the
layout.
I'd have liked to simply parse the 'btrfs subvolume list' output,
but I don't want to depend on btrfs-progs being installed at this
point]]

The problem: If a btrfs-backed container creates a btrfs subvolume in
its rootfs, then lxc-destroy will fail to delete the container rootfs,
just as 'btrfs subvolume delete' will fail.  To address this, delete
all subvolumes found under the container before we try to delete the
rootfs.

Pull the #defines and struct definitions for btrfs into a separate
.h file to not clutter bdev.c

Implement btrfs recursive delete support

A non-root user isn't allow to do the ioctls needed for searching (as you can
verify with 'btrfs subvolume list').  So for an unprivileged user, if the
rootfs has subvolumes under it, deletion will fail.  Otherwise, it will
succeed.

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 src/lxc/Makefile.am |   3 +-
 src/lxc/bdev.c      | 413 ++++++++++++++++++++++++++++++++++++++++++++--------
 src/lxc/lxc-btrfs.h | 287 ++++++++++++++++++++++++++++++++++++
 3 files changed, 639 insertions(+), 64 deletions(-)
 create mode 100644 src/lxc/lxc-btrfs.h

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index cdc6833..56acba4 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -15,6 +15,7 @@ noinst_HEADERS = \
 	list.h \
 	log.h \
 	lxc.h \
+	lxc-btrfs.h \
 	lxclock.h \
 	monitor.h \
 	namespace.h \
@@ -53,7 +54,7 @@ endif
 
 liblxc_so_SOURCES = \
 	arguments.c arguments.h \
-	bdev.c bdev.h \
+	bdev.c bdev.h lxc-btrfs.h \
 	commands.c commands.h \
 	start.c start.h \
 	execute.c \
diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c
index e1ced1b..9a9c898 100644
--- a/src/lxc/bdev.c
+++ b/src/lxc/bdev.c
@@ -53,6 +53,7 @@
 #include "namespace.h"
 #include "parse.h"
 #include "lxclock.h"
+#include "lxc-btrfs.h"
 
 #ifndef BLKGETSIZE64
 #define BLKGETSIZE64 _IOR(0x12,114,size_t)
@@ -1181,26 +1182,76 @@ static const struct bdev_ops lvm_ops = {
 	.can_snapshot = true,
 };
 
+char *get_btrfs_subvol_path(int fd, u64 dir_id, u64 objid,
+		char *name, int name_len)
+{
+	struct btrfs_ioctl_ino_lookup_args args;
+	int ret, e;
+	size_t len;
+	char *retpath;
+
+	memset(&args, 0, sizeof(args));
+	args.treeid = objid;
+	args.objectid = dir_id;
+
+	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+	e = errno;
+	if (ret) {
+		if (e == ENOENT) {
+			ERROR("PATHPRINT: ENOENT on %s\n", name);
+			return NULL;
+		}
+		ERROR("PATHPRINT: ERROR: Failed to lookup path for root %llu - %s\n",
+				(unsigned long long)objid,
+				strerror(e));
+		return NULL;
+	}
+
+	if (args.name[0]) {
+		/*
+		 * we're in a subdirectory of ref_tree, the kernel ioctl
+		 * puts a / in there for us
+		 */
+		len = strlen(args.name) + name_len + 2;
+		retpath = malloc(len);
+		if (!retpath)
+			return NULL;
+		strcpy(retpath, args.name);
+		strcat(retpath, "/");
+		strncat(retpath, name, name_len);
+	} else {
+		/* we're at the root of ref_tree */
+		len = name_len + 1;
+		retpath = malloc(len);
+		if (!retpath)
+			return NULL;
+		*retpath = '\0';
+		strncat(retpath, name, name_len);
+	}
+	return retpath;
+}
+
 //
 // btrfs ops
 //
 
-struct btrfs_ioctl_space_info {
-	unsigned long long flags;
-	unsigned long long total_bytes;
-	unsigned long long used_bytes;
-};
+int btrfs_list_get_path_rootid(int fd, u64 *treeid)
+{
+	int  ret;
+	struct btrfs_ioctl_ino_lookup_args args;
 
-struct btrfs_ioctl_space_args {
-	unsigned long long space_slots;
-	unsigned long long total_spaces;
-	struct btrfs_ioctl_space_info spaces[0];
-};
+	memset(&args, 0, sizeof(args));
+	args.objectid = BTRFS_FIRST_FREE_OBJECTID;
 
-#define BTRFS_IOCTL_MAGIC 0x94
-#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long)
-#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
-                                    struct btrfs_ioctl_space_args)
+	ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+	if (ret < 0) {
+		WARN("Warning: can't perform the search -%s\n",
+				strerror(errno));
+		return ret;
+	}
+	*treeid = args.treeid;
+	return 0;
+}
 
 static bool is_btrfs_fs(const char *path)
 {
@@ -1270,41 +1321,6 @@ static int btrfs_umount(struct bdev *bdev)
 	return umount(bdev->dest);
 }
 
-#define BTRFS_SUBVOL_NAME_MAX 4039
-#define BTRFS_PATH_NAME_MAX 4087
-
-struct btrfs_ioctl_vol_args {
-	signed long long fd;
-	char name[BTRFS_PATH_NAME_MAX + 1];
-};
-
-#define BTRFS_IOCTL_MAGIC 0x94
-#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \
-                                   struct btrfs_ioctl_vol_args_v2)
-#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
-                                   struct btrfs_ioctl_vol_args_v2)
-#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
-                                   struct btrfs_ioctl_vol_args)
-#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
-                                   struct btrfs_ioctl_vol_args)
-
-#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
-
-struct btrfs_ioctl_vol_args_v2 {
-	signed long long fd;
-	unsigned long long transid;
-	unsigned long long flags;
-	union {
-		struct {
-			unsigned long long size;
-			//struct btrfs_qgroup_inherit *qgroup_inherit;
-			void *qgroup_inherit;
-		};
-		unsigned long long unused[4];
-	};
-	char name[BTRFS_SUBVOL_NAME_MAX + 1];
-};
-
 static int btrfs_subvolume_create(const char *path)
 {
 	int ret, fd = -1;
@@ -1342,17 +1358,6 @@ static int btrfs_subvolume_create(const char *path)
 	return ret;
 }
 
-#define BTRFS_FSID_SIZE 16
-struct btrfs_ioctl_fs_info_args {
-	unsigned long long max_id;
-	unsigned long long num_devices;
-	char fsid[BTRFS_FSID_SIZE];
-	unsigned long long reserved[124];
-};
-
-#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
-		struct btrfs_ioctl_fs_info_args)
-
 static int btrfs_same_fs(const char *orig, const char *new) {
 	int fd_orig = -1, fd_new = -1, ret = -1;
 	struct btrfs_ioctl_fs_info_args orig_args, new_args;
@@ -1506,11 +1511,10 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old
 	return btrfs_subvolume_create(new->dest);
 }
 
-static int btrfs_destroy(struct bdev *orig)
+static int btrfs_do_destroy_subvol(const char *path)
 {
 	int ret, fd = -1;
 	struct btrfs_ioctl_vol_args  args;
-	char *path = orig->src;
 	char *p, *newfull = strdup(path);
 
 	if (!newfull) {
@@ -1537,7 +1541,7 @@ static int btrfs_destroy(struct bdev *orig)
 	strncpy(args.name, p+1, BTRFS_SUBVOL_NAME_MAX);
 	args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
 	ret = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
-	INFO("btrfs: snapshot destroy ioctl returned %d", ret);
+	INFO("btrfs: snapshot destroy ioctl returned %d for %s", ret, path);
 	if (ret < 0 && errno == EPERM)
 		INFO("Is the rootfs mounted with -o user_subvol_rm_allowed?");
 
@@ -1546,6 +1550,289 @@ static int btrfs_destroy(struct bdev *orig)
 	return ret;
 }
 
+struct mytree_node {
+	u64 objid;
+	u64 parentid;
+	char *name;
+	char *dirname;
+};
+
+struct my_btrfs_tree {
+	struct mytree_node *nodes;
+	int num;
+};
+
+static int get_btrfs_tree_idx(struct my_btrfs_tree *tree, u64 id)
+{
+	int i;
+	if (!tree)
+		return -1;
+	for (i = 0; i < tree->num; i++) {
+		if (tree->nodes[i].objid == id)
+			return i;
+	}
+	return -1;
+}
+
+static struct my_btrfs_tree *create_my_btrfs_tree(u64 id, const char *path, int name_len)
+{
+	struct my_btrfs_tree *tree;
+
+	tree = malloc(sizeof(tree));
+	if (!tree)
+		return NULL;
+	tree->nodes = malloc(sizeof(struct mytree_node));
+	if (!tree->nodes) {
+		free(tree);
+		return NULL;
+	}
+	tree->num = 1;
+	tree->nodes[0].dirname = NULL;
+	tree->nodes[0].name = strdup(path);
+	if (!tree->nodes[0].name) {
+		free(tree->nodes);
+		free(tree);
+		return NULL;
+	}
+	tree->nodes[0].parentid = 0;
+	tree->nodes[0].objid = id;
+	return tree;
+}
+
+static bool update_tree_node(struct mytree_node *n, u64 id, u64 parent, char *name,
+		int name_len, char *dirname)
+{
+	if (id)
+		n->objid = id;
+	if (parent)
+		n->parentid = parent;
+	if (name) {
+		n->name = malloc(name_len + 1);
+		if (!n->name)
+			return false;
+		strncpy(n->name, name, name_len);
+		n->name[name_len] = '\0';
+	}
+	if (dirname) {
+		n->dirname = malloc(strlen(dirname) + 1);
+		if (!n->dirname) {
+			free(n->name);
+			return false;
+		}
+		strcpy(n->dirname, dirname);
+	}
+	return true;
+}
+
+static bool add_btrfs_tree_node(struct my_btrfs_tree *tree, u64 id, u64 parent,
+		char *name, int name_len, char *dirname)
+{
+	struct mytree_node *tmp;
+
+	int i = get_btrfs_tree_idx(tree, id);
+	if (i != -1)
+		return update_tree_node(&tree->nodes[i], id, parent, name,
+				name_len, dirname);
+
+	tmp = realloc(tree->nodes, (tree->num+1) * sizeof(struct mytree_node));
+	if (!tmp)
+		return false;
+	tree->nodes = tmp;
+	memset(&tree->nodes[tree->num], 0, sizeof(struct mytree_node));
+	if (!update_tree_node(&tree->nodes[tree->num], id, parent, name,
+				name_len, dirname))
+		return false;
+	tree->num++;
+	return true;
+}
+
+static void free_btrfs_tree(struct my_btrfs_tree *tree)
+{
+	int i;
+	if (!tree)
+		return;
+	for (i = 0; i < tree->num;  i++) {
+		free(tree->nodes[i].name);
+		free(tree->nodes[i].dirname);
+	}
+	free(tree->nodes);
+	free(tree);
+}
+
+static bool do_remove_btrfs_children(struct my_btrfs_tree *tree, u64 root_id,
+		const char *path)
+{
+	int i;
+	char *newpath;
+	size_t len;
+
+	for (i = 0; i < tree->num; i++) {
+		if (tree->nodes[i].parentid == root_id) {
+				tree->nodes[i].name ? tree->nodes[i].name : "(null)",
+				tree->nodes[i].dirname ? tree->nodes[i].dirname : "(null)");
+			if (tree->nodes[i].dirname) {
+				len = strlen(path) + strlen(tree->nodes[i].dirname) + 2;
+				newpath = malloc(len);
+				if (!newpath) {
+					ERROR("Out of memory");
+					return false;
+				}
+				snprintf(newpath, len, "%s/%s", path, tree->nodes[i].dirname);
+			} else
+				newpath = NULL;
+			if (!do_remove_btrfs_children(tree, tree->nodes[i].objid, newpath)) {
+				ERROR("Failed to prune %s\n", tree->nodes[i].name);
+				free(newpath);
+				return false;
+			}
+			if (btrfs_do_destroy_subvol(newpath) != 0) {
+				ERROR("Failed to remove %s\n", newpath);
+				free(newpath);
+				return false;
+			}
+			free(newpath);
+		}
+	}
+	return true;
+}
+
+static int btrfs_recursive_destroy(const char *path)
+{
+	u64 root_id;
+	int fd;
+	struct btrfs_ioctl_search_args args;
+	struct btrfs_ioctl_search_key *sk = &args.key;
+	struct btrfs_ioctl_search_header *sh;
+	struct btrfs_root_ref *ref;
+	struct my_btrfs_tree *tree;
+	int ret, i;
+	unsigned long off = 0;
+	int name_len;
+	char *name;
+	char *tmppath;
+	u64 dir_id = 0;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		ERROR("Failed to open %s\n", path);
+		return -1;
+	}
+
+	if (btrfs_list_get_path_rootid(fd, &root_id)) {
+		close(fd);
+		if (errno == EPERM || errno == EACCES) {
+			WARN("Will simply try removing");
+			goto ignore_search;
+		}
+
+		return -1;
+	}
+
+	tree = create_my_btrfs_tree(root_id, path, strlen(path));
+	if (!tree) {
+		ERROR("Out of memory\n");
+		close(fd);
+		return -1;
+	}
+	/* Walk all subvols looking for any under this id */
+	memset(&args, 0, sizeof(args));
+
+	/* search in the tree of tree roots */
+	sk->tree_id = 1;
+
+	sk->max_type = BTRFS_ROOT_REF_KEY;
+	sk->min_type = BTRFS_ROOT_ITEM_KEY;
+	sk->min_objectid = 0;
+	sk->max_objectid = (u64)-1;
+	sk->max_offset = (u64)-1;
+	sk->min_offset = 0;
+	sk->max_transid = (u64)-1;
+	sk->nr_items = 4096;
+
+	while(1) {
+		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+		if (ret < 0) {
+			close(fd);
+			ERROR("Error: can't perform the search under %s\n", path);
+			free_btrfs_tree(tree);
+			return -1;
+		}
+		if (sk->nr_items == 0)
+			break;
+
+		off = 0;
+		for (i = 0; i < sk->nr_items; i++) {
+			sh = (struct btrfs_ioctl_search_header *)(args.buf + off);
+			off += sizeof(*sh);
+			/*
+			 * A backref key with the name and dirid of the parent
+			 * comes followed by the reoot ref key which has the
+			 * name of the child subvol in question.
+			 */
+			if (sh->objectid != root_id && sh->type == BTRFS_ROOT_BACKREF_KEY) {
+				ref = (struct btrfs_root_ref *)(args.buf + off);
+				name_len = ref->name_len;
+				name = (char *)(ref + 1);
+				dir_id = ref->dirid;
+				tmppath = get_btrfs_subvol_path(fd, dir_id,
+						sh->objectid, name, name_len);
+				if (!add_btrfs_tree_node(tree, sh->objectid,
+							sh->offset, name,
+							name_len, tmppath)) {
+					ERROR("Out of memory");
+					free_btrfs_tree(tree);
+					free(tmppath);
+					close(fd);
+					return -1;
+				}
+				free(tmppath);
+			}
+			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;
+		}
+		sk->nr_items = 4096;
+		sk->min_offset++;
+		if (!sk->min_offset)
+			sk->min_type++;
+		else
+			continue;
+
+		if (sk->min_type > BTRFS_ROOT_BACKREF_KEY) {
+			sk->min_type = BTRFS_ROOT_ITEM_KEY;
+			sk->min_objectid++;
+		} else
+			continue;
+
+		if (sk->min_objectid >= sk->max_objectid)
+			break;
+	}
+	close(fd);
+
+	/* now actually remove them */
+
+	if (!do_remove_btrfs_children(tree, root_id, path)) {
+		ERROR("failed pruning\n");
+		return -1;
+	}
+
+	free_btrfs_tree(tree);
+	/* All child subvols have been removed, now remove this one */
+ignore_search:
+	return btrfs_do_destroy_subvol(path);
+}
+
+static int btrfs_destroy(struct bdev *orig)
+{
+	return btrfs_recursive_destroy(orig->src);
+}
+
 static int btrfs_create(struct bdev *bdev, const char *dest, const char *n,
 			struct bdev_specs *specs)
 {
diff --git a/src/lxc/lxc-btrfs.h b/src/lxc/lxc-btrfs.h
new file mode 100644
index 0000000..035479e
--- /dev/null
+++ b/src/lxc/lxc-btrfs.h
@@ -0,0 +1,287 @@
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+struct btrfs_ioctl_space_info {
+	unsigned long long flags;
+	unsigned long long total_bytes;
+	unsigned long long used_bytes;
+};
+
+struct btrfs_ioctl_space_args {
+	unsigned long long space_slots;
+	unsigned long long total_spaces;
+	struct btrfs_ioctl_space_info spaces[0];
+};
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, unsigned long long)
+#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
+                                    struct btrfs_ioctl_space_args)
+
+#define BTRFS_FSID_SIZE 16
+struct btrfs_ioctl_fs_info_args {
+	unsigned long long max_id;
+	unsigned long long num_devices;
+	char fsid[BTRFS_FSID_SIZE];
+	unsigned long long reserved[124];
+};
+
+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
+		struct btrfs_ioctl_fs_info_args)
+
+
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#define BTRFS_PATH_NAME_MAX 4087
+
+struct btrfs_ioctl_vol_args {
+	signed long long fd;
+	char name[BTRFS_PATH_NAME_MAX + 1];
+};
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \
+                                   struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+                                   struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
+                                   struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
+                                   struct btrfs_ioctl_vol_args)
+
+#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+
+struct btrfs_ioctl_vol_args_v2 {
+	signed long long fd;
+	unsigned long long transid;
+	unsigned long long flags;
+	union {
+		struct {
+			unsigned long long size;
+			//struct btrfs_qgroup_inherit *qgroup_inherit;
+			void *qgroup_inherit;
+		};
+		unsigned long long unused[4];
+	};
+	char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
+/*
+ * root backrefs tie subvols and snapshots to the directory entries that
+ * reference them
+ */
+#define BTRFS_ROOT_BACKREF_KEY  144
+
+/*
+ * root items point to tree roots.  There are typically in the root
+ * tree used by the super block to find all the other trees
+ */
+#define BTRFS_ROOT_ITEM_KEY     132
+
+/*
+ * root refs make a fast index for listing all of the snapshots and
+ * subvolumes referenced by a given root.  They point directly to the
+ * directory item in the root that references the subvol
+ */
+#define BTRFS_ROOT_REF_KEY      156
+
+#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__));
+
+#define BTRFS_IOCTL_MAGIC 0x94
+#define BTRFS_VOL_NAME_MAX 255
+#define BTRFS_PATH_NAME_MAX 4087
+
+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_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_UUID_SIZE 16
+
+struct btrfs_timespec {
+	__le64 sec;
+	__le32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_item {
+	/* nfs style generation number */
+	__le64 generation;
+	/* transid that last touched this inode */
+	__le64 transid;
+	__le64 size;
+	__le64 nbytes;
+	__le64 block_group;
+	__le32 nlink;
+	__le32 uid;
+	__le32 gid;
+	__le32 mode;
+	__le64 rdev;
+	__le64 flags;
+
+	/* modification sequence number for NFS */
+	__le64 sequence;
+
+	/*
+	 * a little future expansion, for more than this we can
+	 * just grow the inode item and version it
+	 */
+	__le64 reserved[4];
+	struct btrfs_timespec atime;
+	struct btrfs_timespec ctime;
+	struct btrfs_timespec mtime;
+	struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_item_v0 {
+	struct btrfs_inode_item inode;
+	__le64 generation;
+	__le64 root_dirid;
+	__le64 bytenr;
+	__le64 byte_limit;
+	__le64 bytes_used;
+	__le64 last_snapshot;
+	__le64 flags;
+	__le32 refs;
+	struct btrfs_disk_key drop_progress;
+	u8 drop_level;
+	u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_item {
+	struct btrfs_inode_item inode;
+	__le64 generation;
+	__le64 root_dirid;
+	__le64 bytenr;
+	__le64 byte_limit;
+	__le64 bytes_used;
+	__le64 last_snapshot;
+	__le64 flags;
+	__le32 refs;
+	struct btrfs_disk_key drop_progress;
+	u8 drop_level;
+	u8 level;
+
+	/*
+	 * The following fields appear after subvol_uuids+subvol_times
+	 * were introduced.
+	 */
+
+	/*
+	 * This generation number is used to test if the new fields are valid
+	 * and up to date while reading the root item. Everytime the root item
+	 * is written out, the "generation" field is copied into this field. If
+	 * anyone ever mounted the fs with an older kernel, we will have
+	 * mismatching generation values here and thus must invalidate the
+	 * new fields. See btrfs_update_root and btrfs_find_last_root for
+	 * details.
+	 * the offset of generation_v2 is also used as the start for the memset
+	 * when invalidating the fields.
+	 */
+	__le64 generation_v2;
+	u8 uuid[BTRFS_UUID_SIZE];
+	u8 parent_uuid[BTRFS_UUID_SIZE];
+	u8 received_uuid[BTRFS_UUID_SIZE];
+	__le64 ctransid; /* updated when an inode changes */
+	__le64 otransid; /* trans when created */
+	__le64 stransid; /* trans when sent. non-zero for received subvol */
+	__le64 rtransid; /* trans when received. non-zero for received subvol */
+	struct btrfs_timespec ctime;
+	struct btrfs_timespec otime;
+	struct btrfs_timespec stime;
+	struct btrfs_timespec rtime;
+	__le64 reserved[8]; /* for future */
+} __attribute__ ((__packed__));
+
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+                                   struct btrfs_ioctl_ino_lookup_args)
+
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+struct btrfs_ioctl_ino_lookup_args {
+	__u64 treeid;
+	__u64 objectid;
+	char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+/*
+ * All files have objectids in this range.
+ */
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
-- 
1.9.1



More information about the lxc-devel mailing list