All of lore.kernel.org
 help / color / mirror / Atom feed
From: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>
To: linux-btrfs@vger.kernel.org
Subject: [PATCH] btrfs-progs: New utility to swap subvolumes
Date: Thu, 24 Dec 2009 11:18:57 +0900	[thread overview]
Message-ID: <4B32CF91.8080908@jp.fujitsu.com> (raw)
In-Reply-To: <4B32CF4D.90406@jp.fujitsu.com>

Add btrfsrevert utility to swap subvolumes.

Signed-off-by: TARUISI Hiroaki <taruishi.hiroak@jp.fujitsu.com>
---
 Makefile      |    5
 btrfsrevert.c |  778 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 782 insertions(+), 1 deletion(-)

Index: b/Makefile
===================================================================
--- a/Makefile	2009-12-24 10:45:24.000000000 +0900
+++ b/Makefile	2009-12-24 10:45:43.000000000 +0900
@@ -17,7 +17,7 @@ bindir = $(prefix)/bin
 LIBS=-luuid

 progs = btrfsctl mkfs.btrfs btrfs-debug-tree btrfs-show btrfs-vol btrfsck \
-	btrfs-map-logical
+	btrfs-map-logical btrfsrevert

 # make C=1 to enable sparse
 ifdef C
@@ -63,6 +63,9 @@ btrfs-map-logical: $(objects) btrfs-map-
 btrfs-image: $(objects) btrfs-image.o
 	gcc $(CFLAGS) -o btrfs-image $(objects) btrfs-image.o -lpthread -lz $(LDFLAGS) $(LIBS)

+btrfsrevert: $(objects) btrfsrevert.o
+	gcc $(CFLAGS) -o btrfsrevert $(objects) btrfsrevert.o $(LDFLAGS) $(LIBS)
+
 dir-test: $(objects) dir-test.o
 	gcc $(CFLAGS) -o dir-test $(objects) dir-test.o $(LDFLAGS) $(LIBS)

Index: b/btrfsrevert.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/btrfsrevert.c	2009-12-24 10:45:43.000000000 +0900
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#define _XOPEN_SOURCE 500
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <uuid/uuid.h>
+#include "kerncompat.h"
+#include "ctree.h"
+#include "disk-io.h"
+#include "transaction.h"
+#include "utils.h"
+#include "version.h"
+#include "ioctl.h"
+#include "hash.h"
+#include <mcheck.h>
+
+#define BTRFS_REVERTED_TREE_LOCATION ".old_trees"
+
+struct tree_info {
+	u64 id, parent_treeid;
+	u64 parent_dirid, index;
+	int name_len;
+	char *name;
+};
+
+struct revert_packet {
+	struct tree_info source, target, backup;
+};
+
+static void print_usage(void)
+{
+	fprintf(stderr, "usage: btrfsrevert [options] device\n");
+	fprintf(stderr, "\t-s source tree\n");
+	fprintf(stderr, "\t-t target tree\n");
+}
+
+static int find_file_tree(struct btrfs_root *root, struct tree_info *tinfo)
+{
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	struct btrfs_root_ref *ref;
+	int ret;
+
+	if (tinfo->id == BTRFS_FS_TREE_OBJECTID) {
+		tinfo->name = malloc(9);
+		strcpy(tinfo->name, "default");
+		tinfo->name_len = 8;
+		return 0;
+	}
+	if (tinfo->id < BTRFS_FIRST_FREE_OBJECTID ||
+		BTRFS_LAST_FREE_OBJECTID < tinfo->id)
+			return 2;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	key.objectid = tinfo->id;
+	key.type = BTRFS_ROOT_BACKREF_KEY;
+	key.offset = (u64)-1;
+
+	ret = btrfs_search_slot(NULL, root->fs_info->tree_root, &key,
+		path, 0, 0);
+	if (ret < 0)
+		goto out;
+
+	BUG_ON(ret == 0);
+	if (path->slots[0] == 0) {
+		ret = btrfs_prev_leaf(root->fs_info->tree_root, path);
+
+		if (ret)
+			goto out;
+	} else
+		path->slots[0]--;
+
+	btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+	if (key.objectid == tinfo->id && key.type == BTRFS_ROOT_BACKREF_KEY) {
+		ret = 0;
+		tinfo->parent_treeid = key.offset;
+		ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			struct btrfs_root_ref);
+		tinfo->parent_dirid = btrfs_root_ref_dirid(path->nodes[0], ref);
+		tinfo->index = btrfs_root_ref_sequence(path->nodes[0], ref);
+		tinfo->name_len = btrfs_root_ref_name_len(path->nodes[0], ref);
+		tinfo->name = malloc(tinfo->name_len + 1);
+		if (tinfo->name) {
+			tinfo->name[tinfo->name_len] = '\0';
+			read_extent_buffer(path->nodes[0], tinfo->name,
+				(long)(ref + 1), tinfo->name_len);
+		} else
+			ret = -ENOMEM;
+	} else
+		ret = 1;
+
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
+static int btrfs_revert_root(struct btrfs_fs_info *fs_info,
+		struct revert_packet *rvp)
+{
+	printf("Root swap is not implemented yet.\n");
+	return 0;
+}
+
+static int btrfs_update_root_ref(struct btrfs_trans_handle *trans,
+		struct btrfs_root *root, struct btrfs_key *ref_key,
+		struct tree_info *f_tinfo, struct tree_info *t_tinfo,
+		char *new_name)
+{
+	struct btrfs_root *tree_root = root->fs_info->tree_root;
+	struct btrfs_root_ref *oldref;
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	int ret, name_len, new_name_len = strlen(new_name);
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+	if (f_tinfo->parent_treeid != t_tinfo->parent_treeid) {
+		if (ref_key->type == BTRFS_ROOT_REF_KEY) {
+			ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
+				t_tinfo->parent_treeid, BTRFS_ROOT_REF_KEY, f_tinfo->id,
+				t_tinfo->parent_dirid, t_tinfo->index, new_name,
+				strlen(new_name));
+			if (ret)
+				goto error;
+			ref_key->objectid = t_tinfo->parent_treeid;
+			ref_key->offset = f_tinfo->id;
+
+			key.objectid = f_tinfo->parent_treeid;
+			key.type = BTRFS_ROOT_REF_KEY;
+			key.offset = f_tinfo->id;
+			ret = btrfs_search_slot(trans, tree_root, &key, path,
+				-(sizeof(struct btrfs_root_ref) + f_tinfo->name_len), 1);
+			if (ret)
+				goto error;
+			ret = btrfs_del_item(trans, tree_root, path);
+			if (ret)
+				goto error;
+		} else if (ref_key->type == BTRFS_ROOT_BACKREF_KEY) {
+			ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
+				f_tinfo->id, BTRFS_ROOT_BACKREF_KEY, t_tinfo->parent_treeid,
+				t_tinfo->parent_dirid, t_tinfo->index, new_name,
+				strlen(new_name));
+			if (ret)
+				goto error;
+			ref_key->objectid = f_tinfo->id;
+			ref_key->offset = t_tinfo->parent_treeid;
+
+			key.objectid = f_tinfo->id;
+			key.type = BTRFS_ROOT_BACKREF_KEY;
+			key.offset = f_tinfo->parent_treeid;
+			ret = btrfs_search_slot(trans, tree_root, &key, path,
+				-(sizeof(struct btrfs_root_ref) + f_tinfo->name_len), 1);
+			if (ret)
+				goto error;
+			ret = btrfs_del_item(trans, tree_root, path);
+			if (ret)
+				goto error;
+		}
+		btrfs_release_path(tree_root, path);
+	}
+
+	ret = btrfs_search_slot(trans, tree_root, ref_key, path,
+		strlen(new_name) - f_tinfo->name_len, 1);
+	if (ret)
+		goto error;
+
+	oldref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+		struct btrfs_root_ref);
+	name_len = btrfs_root_ref_name_len(path->nodes[0], oldref);
+
+	if (name_len < new_name_len) {
+		ret = btrfs_extend_item(trans, tree_root, path,
+			new_name_len - name_len);
+		if (ret)
+			goto error;
+	} else {
+		ret = btrfs_truncate_item(trans, tree_root, path,
+			sizeof(*oldref) + new_name_len, 1);
+		if (ret)
+			goto error;
+	}
+	oldref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+		struct btrfs_root_ref);
+	write_extent_buffer(path->nodes[0], new_name,
+		(unsigned long)(oldref + 1), new_name_len);
+	btrfs_set_root_ref_name_len(path->nodes[0], oldref, new_name_len);
+	btrfs_set_root_ref_dirid(path->nodes[0], oldref, t_tinfo->parent_dirid);
+	btrfs_set_root_ref_sequence(path->nodes[0], oldref, t_tinfo->index);
+
+	btrfs_free_path(path);
+	return 0;
+
+error:
+	btrfs_free_path(path);
+	return 1;
+}
+
+static int btrfs_find_highest_index(struct btrfs_root *fs_root,
+		u64 i_no, u64 *index)
+{
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	int ret;
+
+	key.objectid = i_no;
+	key.type = BTRFS_DIR_INDEX_KEY;
+	key.offset = (u64)-1;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+	ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
+	if (ret <0)
+		goto out;
+	BUG_ON(ret == 0);
+	if (path->slots[0] == 0) {
+		*index = 1ULL;
+	} else {
+		path->slots[0]--;
+		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+		if (key.objectid == i_no && key.type == BTRFS_DIR_INDEX_KEY)
+			*index = key.offset;
+		else {
+			*index = 1ULL;
+		}
+	}
+	ret = 0;
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
+static int btrfs_update_parent_dir_size(struct btrfs_trans_handle *trans,
+		struct btrfs_root *fs_root, struct btrfs_path *path, u64 parent_id,
+		int name_len)
+{
+	struct btrfs_key parent_key = {parent_id, BTRFS_INODE_ITEM_KEY, 0ULL};;
+	struct btrfs_inode_item *parent_inode;
+	u64 dir_size;
+	int ret;
+
+	ret = btrfs_search_slot(trans, fs_root, &parent_key, path, 0, 1);
+	BUG_ON(ret);
+
+	parent_inode = btrfs_item_ptr(path->nodes[0], path->slots[0],
+			struct btrfs_inode_item);
+
+	dir_size = btrfs_inode_size(path->nodes[0], parent_inode);
+	btrfs_set_inode_size(path->nodes[0], parent_inode, dir_size+name_len*2);
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+
+	return 0;
+}
+
+static int btrfs_create_dir(struct btrfs_root *root, u64 dirid,
+		char *dir_name, struct revert_packet *rvp)
+{
+	struct btrfs_inode_item inode_item;
+	struct btrfs_dir_item *dir_item;
+	struct btrfs_path *path;
+	struct btrfs_trans_handle *trans;
+	struct btrfs_key location, found_key;
+	int ret;
+	u64 objectid, index = 0;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+
+	dir_item = btrfs_lookup_dir_item(NULL, root, path, dirid,
+		dir_name, strlen(dir_name), 0);
+
+	if (dir_item) {
+		btrfs_dir_item_key_to_cpu(path->nodes[0], dir_item, &found_key);
+		if (rvp) {
+			rvp->backup.id = root->root_key.objectid;
+			rvp->backup.parent_dirid = found_key.objectid;
+			rvp->backup.parent_treeid = root->root_key.objectid;
+		}
+		btrfs_free_path(path);
+		return 0;
+	}
+
+	btrfs_release_path(root, path);
+
+	ret = btrfs_find_highest_inode(root, &objectid);
+	if (ret)
+		return -1;
+
+	ret = btrfs_find_highest_index(root, BTRFS_FIRST_FREE_OBJECTID, &index);
+	if (ret)
+		return -1;
+
+	trans = btrfs_start_transaction(root, 1);
+	if (!trans)
+		return -ENOMEM;
+
+	objectid++;
+	index++;
+	if (rvp) {
+		rvp->backup.id = root->root_key.objectid;
+		rvp->backup.parent_dirid = objectid;
+		rvp->backup.parent_treeid = root->root_key.objectid;
+	}
+
+	memset(&inode_item, 0, sizeof(inode_item));
+	btrfs_set_stack_inode_generation(&inode_item, trans->transid);
+	btrfs_set_stack_inode_size(&inode_item, 0);
+	btrfs_set_stack_inode_nlink(&inode_item, 1);
+	btrfs_set_stack_inode_nbytes(&inode_item, root->leafsize);
+	btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0555);
+	ret = btrfs_insert_inode(trans, root, objectid, &inode_item);
+	if (ret)
+		goto out;
+	ret = btrfs_insert_inode_ref(trans, root, dir_name, strlen(dir_name),
+			objectid, BTRFS_FIRST_FREE_OBJECTID, index);
+	if (ret)
+		goto out;
+
+	location.objectid = objectid;
+	location.offset = 0ULL;
+	location.type = BTRFS_INODE_ITEM_KEY;
+
+	ret = btrfs_insert_dir_item(trans, root, dir_name, strlen(dir_name),
+			BTRFS_FIRST_FREE_OBJECTID, &location, BTRFS_FT_DIR, index);
+	if (ret)
+		goto out;
+	ret = btrfs_update_parent_dir_size(trans, root, path,
+		BTRFS_FIRST_FREE_OBJECTID, strlen(dir_name));
+
+	btrfs_commit_transaction(trans, root);
+out:
+	btrfs_free_path(path);
+	return 0;
+}
+
+static char *generate_uuid_string(char *name)
+{
+	uuid_t uuid;
+	int len = strlen(name);
+	char *ret_name;
+
+	if (len + 37 > BTRFS_VOL_NAME_MAX)
+		len = BTRFS_VOL_NAME_MAX - 37;
+	ret_name = malloc(38+len);
+	if (!ret_name)
+		return NULL;
+	ret_name[37+len]='\0';
+	strncpy(ret_name, name, len);
+	ret_name[len] = '_';
+	uuid_generate_time(uuid);
+	uuid_unparse(uuid, ret_name + len + 1);
+
+	return ret_name;
+}
+
+static int btrfs_create_backup_link(struct btrfs_trans_handle *trans,
+		struct btrfs_root *backup_root, char *name, struct revert_packet *rvp)
+{
+	struct btrfs_dir_item *dir_item;
+	struct btrfs_path *path;
+	struct btrfs_key location, ref_key;
+	struct btrfs_root *tree_root = backup_root->fs_info->tree_root;
+	int ret;
+
+	if (!name)
+		return -1;
+	path = btrfs_alloc_path();
+	if (!path) {
+		return -ENOMEM;
+	}
+
+	dir_item = btrfs_lookup_dir_item(NULL, backup_root, path,
+		rvp->backup.parent_dirid, name, strlen(name), 0);
+	if (dir_item)
+		goto error;
+
+	btrfs_release_path(backup_root, path);
+
+	ret = btrfs_find_highest_index(backup_root, rvp->backup.parent_dirid,
+		&rvp->backup.index);
+	if (ret)
+		goto error;
+
+	rvp->backup.index++;
+
+	location.objectid = rvp->target.id;
+	location.type = BTRFS_ROOT_ITEM_KEY;
+	location.offset = (u64)-1;
+
+	ret = btrfs_insert_dir_item(trans, backup_root, name, strlen(name),
+			rvp->backup.parent_dirid, &location, BTRFS_FT_DIR,
+			rvp->backup.index);
+	if (ret)
+		goto error;
+
+	ret = btrfs_update_parent_dir_size(trans, backup_root, path,
+		rvp->backup.parent_dirid, strlen(name));
+	if (ret)
+		goto error;
+
+	btrfs_free_path(path);
+
+	ref_key.objectid = rvp->target.id;
+	ref_key.type = BTRFS_ROOT_BACKREF_KEY;
+	ref_key.offset = rvp->target.parent_treeid;
+	ret = btrfs_update_root_ref(trans, tree_root, &ref_key, &rvp->target,
+		&rvp->backup, name);
+	if (ret)
+		return -1;
+
+	ref_key.objectid = rvp->target.parent_treeid;
+	ref_key.type = BTRFS_ROOT_REF_KEY;
+	ref_key.offset = rvp->target.id;
+	ret = btrfs_update_root_ref(trans, tree_root, &ref_key, &rvp->target,
+		&rvp->backup, name);
+	if (ret)
+		return -1;
+
+	return 0;
+
+error:
+	btrfs_free_path(path);
+	return -1;
+}
+
+static u64 btrfs_find_current_root(struct btrfs_root *root)
+{
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	struct btrfs_dir_item *dir_item;
+	struct btrfs_root *tree_root = root->fs_info->tree_root;
+	int ret;
+	char *default_name = "default";
+
+	key.objectid = BTRFS_ROOT_TREE_DIR_OBJECTID;
+	key.type = BTRFS_DIR_ITEM_KEY;
+	key.offset = btrfs_name_hash(default_name, strlen(default_name));
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return 0ULL;
+	ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
+	BUG_ON(ret);
+
+	dir_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+		struct btrfs_dir_item);
+	BUG_ON(IS_ERR(dir_item));
+
+	btrfs_dir_item_key_to_cpu(path->nodes[0], dir_item, &key);
+
+	btrfs_free_path(path);
+	return key.objectid;
+}
+
+static struct btrfs_root *btrfs_read_fs_root_by_objectid(
+		struct btrfs_fs_info *fs_info, u64 tree_id)
+{
+	struct btrfs_key key;
+
+	key.objectid = tree_id;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	return btrfs_read_fs_root(fs_info, &key);
+}
+
+static void btrfs_update_dir_location(struct btrfs_trans_handle *trans,
+		struct btrfs_path *path, u64 tree_id)
+{
+	struct btrfs_dir_item *di_ref;
+	struct btrfs_key location;
+	struct btrfs_disk_key disk_location;
+
+	di_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
+		struct btrfs_dir_item);
+	BUG_ON(!di_ref);
+	location.objectid = tree_id;
+	location.type = BTRFS_ROOT_ITEM_KEY;
+	location.offset = (u64)-1;
+
+	btrfs_cpu_key_to_disk(&disk_location, &location);
+	btrfs_set_dir_item_key(path->nodes[0], di_ref, &disk_location);
+
+	btrfs_mark_buffer_dirty(path->nodes[0]);
+}
+
+static int btrfs_switch_dir_item(struct btrfs_trans_handle *trans,
+		struct btrfs_root *root, struct revert_packet *rvp)
+{
+	struct btrfs_root *tree_root, *file_root;
+	struct btrfs_key key;
+	struct btrfs_path *path;
+	int ret;
+
+	path = btrfs_alloc_path();
+	if (!path)
+		return -ENOMEM;
+	file_root = btrfs_read_fs_root_by_objectid(root->fs_info,
+		rvp->target.parent_treeid);
+	if (IS_ERR(file_root))
+		return -EINVAL;
+	tree_root = btrfs_read_fs_root_by_objectid(root->fs_info,
+		BTRFS_ROOT_TREE_OBJECTID);
+	BUG_ON(IS_ERR(tree_root));
+
+	key.objectid = rvp->target.parent_dirid;
+	key.type = BTRFS_DIR_ITEM_KEY;
+	key.offset = btrfs_name_hash(rvp->target.name, rvp->target.name_len);
+
+	ret = btrfs_search_slot(trans, file_root, &key, path, 0, 1);
+	BUG_ON(ret);
+
+	btrfs_update_dir_location(trans, path, rvp->source.id);
+	btrfs_release_path(file_root, path);
+
+	key.objectid = rvp->target.parent_dirid;
+	key.type = BTRFS_DIR_INDEX_KEY;
+	key.offset = rvp->target.index;
+	ret = btrfs_search_slot(trans, file_root, &key, path, 0, 1);
+	BUG_ON(ret);
+
+	btrfs_update_dir_location(trans, path, rvp->source.id);
+	btrfs_free_path(path);
+
+	key.objectid = rvp->source.parent_treeid;
+	key.type = BTRFS_ROOT_REF_KEY;
+	key.offset = rvp->source.id;
+	ret = btrfs_update_root_ref(trans, tree_root, &key, &rvp->source,
+		&rvp->target, rvp->target.name);
+	if (ret)
+		return ret;
+
+	key.objectid = rvp->source.id;
+	key.type = BTRFS_ROOT_BACKREF_KEY;
+	key.offset = rvp->source.parent_treeid;
+	ret = btrfs_update_root_ref(trans, tree_root, &key, &rvp->source,
+		&rvp->target, rvp->target.name);
+
+	return ret;
+}
+
+static int btrfs_del_dir_item(struct btrfs_trans_handle *trans,
+		struct btrfs_root *root, struct tree_info *tinfo)
+{
+	struct btrfs_path *path;
+	struct btrfs_key key;
+	int ret, del_size;
+
+	path = btrfs_alloc_path();
+	key.objectid = tinfo->parent_dirid;
+	key.type = BTRFS_DIR_ITEM_KEY;
+	key.offset = btrfs_name_hash(tinfo->name, tinfo->name_len);
+	del_size = sizeof(struct btrfs_dir_item) + tinfo->name_len;
+
+	ret = btrfs_search_slot(trans, root, &key, path, -del_size, 1);
+	BUG_ON(ret);
+	ret = btrfs_del_item(trans, root, path);
+	if (ret)
+		goto out;
+	btrfs_release_path(root, path);
+
+	key.objectid = tinfo->parent_dirid;
+	key.type = BTRFS_DIR_INDEX_KEY;
+	key.offset = tinfo->index;
+
+	ret = btrfs_search_slot(trans, root, &key, path, -del_size, 1);
+	BUG_ON(ret);
+	ret = btrfs_del_item(trans, root, path);
+	if (ret)
+		goto out;
+	btrfs_release_path(root, path);
+
+	ret = btrfs_update_parent_dir_size(trans, root, path,
+		tinfo->parent_dirid, -tinfo->name_len);
+out:
+	btrfs_free_path(path);
+	return ret;
+}
+
+static int btrfs_revert_directory(struct btrfs_fs_info *fs_info,
+		struct revert_packet *rvp)
+{
+	struct btrfs_trans_handle *trans;
+	struct btrfs_root *backup_root, *target_root, *source_root;
+	int ret;
+	char *store_name;
+
+	store_name = generate_uuid_string(rvp->target.name);
+	printf("\tOld target subvolume is moved to default filesystem root\n");
+	printf("\t  (path = /%s/%s)\n",
+		BTRFS_REVERTED_TREE_LOCATION, store_name);
+
+	ret = btrfs_create_dir(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
+		BTRFS_REVERTED_TREE_LOCATION, rvp);
+	if(ret)
+		goto out_error;
+
+	backup_root = btrfs_read_fs_root_by_objectid(fs_info, rvp->backup.id);
+	if (!backup_root)
+		goto out_error;
+	trans = btrfs_start_transaction(backup_root, 1);
+	if (!trans)
+		goto out_error;
+	ret = btrfs_create_backup_link(trans, backup_root, store_name, rvp);
+	if (ret)
+		goto out_error;
+	btrfs_commit_transaction(trans, backup_root);
+
+	target_root = btrfs_read_fs_root_by_objectid(fs_info,
+		rvp->target.parent_treeid);
+	if (!target_root)
+		goto out_error;
+	trans = btrfs_start_transaction(target_root, 1);
+	if (!trans)
+		goto out_error;
+	ret = btrfs_switch_dir_item(trans, backup_root, rvp);
+	if (ret)
+		goto out_error;
+	btrfs_commit_transaction(trans, target_root);
+
+	source_root = btrfs_read_fs_root_by_objectid(fs_info,
+		rvp->source.parent_treeid);
+	if (!source_root)
+		goto out_error;
+	trans = btrfs_start_transaction(source_root, 1);
+	if (!trans)
+		goto out_error;
+	ret = btrfs_del_dir_item(trans, source_root, &rvp->source);
+	if (ret)
+		goto out_error;
+	btrfs_commit_transaction(trans, source_root);
+
+	free(store_name);
+	return 0;
+
+out_error:
+	free(store_name);
+	return -1;
+}
+
+static char *device;
+
+int main(int argc, char *argv[])
+{
+	struct btrfs_root *root = NULL;
+	struct revert_packet rv_packet;
+	u64 cur_root;
+	int ret = 1;
+
+	rv_packet.source.id = 0ULL;
+	rv_packet.target.id = 0ULL;
+	rv_packet.source.name = NULL;
+	rv_packet.target.name = NULL;
+	while(1) {
+		int c = getopt(argc, argv, "s:t:");
+		if (c < 0)
+			break;
+		switch(c) {
+		case 's':
+			rv_packet.source.id = atoll(optarg);
+			break;
+		case 't':
+			rv_packet.target.id = atoll(optarg);
+			break;
+		default:
+			print_usage();
+			ret = 1;
+			goto out;
+		}
+	}
+
+	if (rv_packet.source.id == 0ULL) {
+		fprintf(stderr, "source tree is not specified\n");
+		goto error;
+	}
+	if (rv_packet.target.id == 0ULL) {
+		fprintf(stderr, "target tree is not specified\n");
+		goto error;
+	}
+	if (rv_packet.source.id == rv_packet.target.id) {
+		fprintf(stderr, "source tree and target tree are a same tree\n");
+		goto error;
+	}
+
+	argc = argc - optind;
+	device = argv[optind];
+	if (argc != 1) {
+		fprintf(stderr, "device not specified\n");
+		goto error;
+	}
+
+	if (check_mounted(device)) {
+		fprintf(stderr, "%s is mounted\n", device);
+		goto error;
+	}
+
+	root = open_ctree(device, 0, 1);
+	if (!root) {
+		printf("ctree open error.\n");
+		goto error;
+	}
+
+	cur_root = btrfs_find_current_root(root);
+
+	if (find_file_tree(root, &rv_packet.source)) {
+		printf("source tree not exist\n");
+		goto error_out;
+	}
+	if (find_file_tree(root, &rv_packet.target)) {
+		printf("target tree not exist\n");
+		goto error_out;
+	}
+	if (rv_packet.source.id == BTRFS_FS_TREE_OBJECTID)
+		if(rv_packet.target.id != cur_root) {
+			printf("filesystem root(id=%llu) cannot replaces non-root tree(id=%llu)\n",
+				rv_packet.source.id, rv_packet.target.id);
+			goto error_out;
+		}
+
+	printf("tree reverting utilitiy\n");
+	printf("\t subvolume\t: \"%s\"\n", rv_packet.target.name);
+	printf("\t subvolume id\t: [before] %llu -> [after] %llu\n",
+		rv_packet.target.id, rv_packet.source.id);
+
+	if (rv_packet.target.id == cur_root) {
+		ret = btrfs_revert_root(root->fs_info, &rv_packet);
+	} else {
+ 		ret = btrfs_revert_directory(root->fs_info, &rv_packet);
+	}
+
+	printf("operation complete\n");
+close_out:
+	close_ctree(root);
+
+out:
+	if (rv_packet.source.name)
+		free(rv_packet.source.name);
+	if (rv_packet.target.name)
+		free(rv_packet.target.name);
+
+	printf("%s\n",BTRFS_BUILD_VERSION);
+	return ret;
+
+error_out:
+	ret = 1;
+	goto close_out;
+
+error:
+	ret = 1;
+	goto out;
+}


  reply	other threads:[~2009-12-24  2:18 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-12-24  2:17 btrfs-progs: New utility to swap subvolumes TARUISI Hiroaki
2009-12-24  2:18 ` TARUISI Hiroaki [this message]
2009-12-27 20:23 ` Goffredo Baroncelli
2009-12-28  1:38   ` TARUISI Hiroaki
2009-12-28 19:08     ` Goffredo Baroncelli
2010-01-04  0:49     ` TARUISI Hiroaki
2010-01-04 17:20       ` Goffredo Baroncelli
2010-01-06  0:09         ` TARUISI Hiroaki
2010-01-06  7:56           ` Goffredo Baroncelli
     [not found]             ` <4B4450C0.3050607@cs.bgu.ac.il>
2010-01-06 20:21               ` Goffredo Baroncelli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4B32CF91.8080908@jp.fujitsu.com \
    --to=taruishi.hiroak@jp.fujitsu.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.