[Ocfs2-tools-devel] [PATCH 3/3] tunefs.ocfs2: Add the cloned_volume operation.

Joel Becker joel.becker at oracle.com
Wed Sep 10 16:30:23 PDT 2008


Users with SANs often want to create clones or snapshots of ocfs2 volumes.
However, these volumes cannot be mounted, because the original volume
has the same UUID and is still in use.

The cloned_volume operation will reset the UUID and label of the given
device.  It cannot check with the cluster stack, so it must call
tunefs_interact_critical().

The --cloned-volume option has an optional <new-label> argument.  If
specified, the volume's label will be set to <new-label>.  If not, the
current volume label with have '-cloned' appended to it.  If the label
already ends in '-cloned', it is not modified.  The label will be
truncated to fit '-cloned'.

Because op_cloned_volume cannot start the cluster stack, a new
tunefs_open() flag is provided: TUNFES_FLAG_SKIPCLUSTER.  This skips all
cluster initialization.  Operations using TUNEFS_FLAG_SKIPCLUSTER are
run before operations specifying TUNEFS_FLAG_NOCLUSTER.

Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 tunefs.ocfs2/Makefile           |    1 +
 tunefs.ocfs2/libocfs2ne.c       |   36 +++++++---
 tunefs.ocfs2/libocfs2ne.h       |    2 +
 tunefs.ocfs2/o2ne_err.et        |    3 +
 tunefs.ocfs2/ocfs2ne.c          |   30 +++++++--
 tunefs.ocfs2/op_cloned_volume.c |  150 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 208 insertions(+), 14 deletions(-)
 create mode 100644 tunefs.ocfs2/op_cloned_volume.c

diff --git a/tunefs.ocfs2/Makefile b/tunefs.ocfs2/Makefile
index a1c00c7..6ba3d3f 100644
--- a/tunefs.ocfs2/Makefile
+++ b/tunefs.ocfs2/Makefile
@@ -25,6 +25,7 @@ OCFS2NE_FEATURES =			\
 	feature_unwritten_extents
 
 OCFS2NE_OPERATIONS =			\
+	op_cloned_volume		\
 	op_features			\
 	op_list_sparse_files		\
 	op_query			\
diff --git a/tunefs.ocfs2/libocfs2ne.c b/tunefs.ocfs2/libocfs2ne.c
index ec2b4be..796c367 100644
--- a/tunefs.ocfs2/libocfs2ne.c
+++ b/tunefs.ocfs2/libocfs2ne.c
@@ -751,6 +751,11 @@ static errcode_t tunefs_lock_cluster(ocfs2_filesys *fs, int flags)
 	if (state->ts_cluster_locked)
 		goto out;
 
+	if (flags & TUNEFS_FLAG_SKIPCLUSTER) {
+		err = TUNEFS_ET_CLUSTER_SKIPPED;
+		goto out;
+	}
+
 	if (!master_fs->fs_dlm_ctxt) {
 		err = o2cb_init();
 		if (err)
@@ -1223,6 +1228,23 @@ static void tunefs_remove_fs(ocfs2_filesys *fs)
 	}
 }
 
+
+/*
+ * Return true if this error code is a special (non-fatal) ocfs2ne
+ * error code.
+ */
+static int tunefs_special_errorp(errcode_t err)
+{
+	if (err == TUNEFS_ET_CLUSTER_SKIPPED)
+		return 1;
+	if (err == TUNEFS_ET_INVALID_STACK_NAME)
+		return 1;
+	if (err == TUNEFS_ET_PERFORM_ONLINE)
+		return 1;
+
+	return 0;
+}
+
 errcode_t tunefs_open(const char *device, int flags,
 		      ocfs2_filesys **ret_fs)
 {
@@ -1269,9 +1291,7 @@ errcode_t tunefs_open(const char *device, int flags,
 	}
 
 	err = tunefs_lock_filesystem(fs, flags);
-	if (err &&
-	    (err != TUNEFS_ET_INVALID_STACK_NAME) &&
-	    (err != TUNEFS_ET_PERFORM_ONLINE))
+	if (err && !tunefs_special_errorp(err))
 		goto out;
 
 	/*
@@ -1300,9 +1320,7 @@ errcode_t tunefs_open(const char *device, int flags,
 	}
 
 out:
-	if (err &&
-	    (err != TUNEFS_ET_INVALID_STACK_NAME) &&
-	    (err != TUNEFS_ET_PERFORM_ONLINE)) {
+	if (err && !tunefs_special_errorp(err)) {
 		if (fs) {
 			tunefs_remove_fs(fs);
 			ocfs2_close(fs);
@@ -1425,6 +1443,8 @@ errcode_t tunefs_op_run(ocfs2_filesys *master_fs,
 		flags |= TUNEFS_FLAG_ONLINE;
 	else if (err == TUNEFS_ET_INVALID_STACK_NAME)
 		flags |= TUNEFS_FLAG_NOCLUSTER;
+	else if (err == TUNEFS_ET_CLUSTER_SKIPPED)
+		flags |= TUNEFS_FLAG_SKIPCLUSTER;
 	else if (err)
 		goto out;
 
@@ -1685,9 +1705,7 @@ int tunefs_op_main(int argc, char *argv[], struct tunefs_operation *op)
 	}
 
 	err = tunefs_open(argv[1], op->to_open_flags, &fs);
-	if (err &&
-	    (err != TUNEFS_ET_PERFORM_ONLINE) &&
-	    (err != TUNEFS_ET_INVALID_STACK_NAME)) {
+	if (err && !tunefs_special_errorp(err)) {
 		tcom_err(err, "- Unable to open device \"%s\" read-write.",
 			 argv[1]);
 		goto out;
diff --git a/tunefs.ocfs2/libocfs2ne.h b/tunefs.ocfs2/libocfs2ne.h
index fd18d48..debf2c5 100644
--- a/tunefs.ocfs2/libocfs2ne.h
+++ b/tunefs.ocfs2/libocfs2ne.h
@@ -70,6 +70,8 @@
 					   cluster stack */
 #define TUNEFS_FLAG_ALLOCATION	0x08	/* Operation will use the
 					   allocator */
+#define TUNEFS_FLAG_SKIPCLUSTER	0x10	/* Operation cannot start the
+					   cluster stack */
 
 
 /* What to do with a feature */
diff --git a/tunefs.ocfs2/o2ne_err.et b/tunefs.ocfs2/o2ne_err.et
index 34f4853..20031a5 100644
--- a/tunefs.ocfs2/o2ne_err.et
+++ b/tunefs.ocfs2/o2ne_err.et
@@ -82,4 +82,7 @@ ec	TUNEFS_ET_OPERATION_FAILED,
 ec	TUNEFS_ET_ONLINE_NOT_SUPPORTED,
 	"This online operation is not available"
 
+ec	TUNEFS_ET_CLUSTER_SKIPPED,
+	"Cluster stack initialization was skipped"
+
 	end
diff --git a/tunefs.ocfs2/ocfs2ne.c b/tunefs.ocfs2/ocfs2ne.c
index 8bd12ad..1639c89 100644
--- a/tunefs.ocfs2/ocfs2ne.c
+++ b/tunefs.ocfs2/ocfs2ne.c
@@ -97,6 +97,7 @@ extern struct tunefs_operation set_journal_size_op;
 extern struct tunefs_operation set_label_op;
 extern struct tunefs_operation set_slot_count_op;
 extern struct tunefs_operation update_cluster_stack_op;
+extern struct tunefs_operation cloned_volume_op;
 
 static LIST_HEAD(tunefs_run_list);
 
@@ -480,6 +481,16 @@ static struct tunefs_option update_cluster_stack_option = {
 	.opt_op		= &update_cluster_stack_op,
 };
 
+static struct tunefs_option cloned_volume_option = {
+	.opt_option	= {
+		.name		= "cloned-volume",
+		.val		= CHAR_MAX,
+		.has_arg	= 2,
+	},
+	.opt_help	= "   --cloned-volume [new-label]",
+	.opt_op		= &cloned_volume_op,
+};
+
 static struct tunefs_option set_slot_count_option = {
 	.opt_option	= {
 		.name		= "node-slots",
@@ -567,6 +578,7 @@ static struct tunefs_option *options[] = {
 	&backup_super_option,
 	&features_option,
 	&update_cluster_stack_option,
+	&cloned_volume_option,
 	&yes_option,
 	&no_option,
 	NULL,
@@ -963,10 +975,16 @@ static int run_operations(const char *device)
 
 	/*
 	 * We have a specific order here.  If we open the filesystem and
-	 * get TUNEFS_ET_INVALID_STACK_NAME, we know that
-	 * update_cluster_stack is involved.  We want to run that, then
-	 * close and reopen the filesystem.  This should allow us to
-	 * continue with any other operations.
+	 * get TUNEFS_ET_CLUSTER_SKIPPED, we know that cloned_volume is
+	 * involved.  We want to run that first and change our volume's
+	 * UUID+label, then close and reopen the filesystem.  We should be
+	 * able to continue with any other operations.
+	 *
+	 * Next, if we open the filesystem and * get
+	 * TUNEFS_ET_INVALID_STACK_NAME, we know that update_cluster_stack
+	 * is involved.  We want to run that, and again close and reopen
+	 * the filesystem.  This should allow us to continue with any
+	 * other operations.
 	 *
 	 * Next, if we get TUNEFS_ET_PERFORM_ONLINE, we have at least
 	 * one operation capable of working online.  We want to run through
@@ -987,7 +1005,9 @@ static int run_operations(const char *device)
 		}
 
 		err = tunefs_open(device, open_flags, &fs);
-		if (err == TUNEFS_ET_INVALID_STACK_NAME)
+		if (err == TUNEFS_ET_CLUSTER_SKIPPED)
+			rc = run_operation_filter(fs, TUNEFS_FLAG_SKIPCLUSTER);
+		else if (err == TUNEFS_ET_INVALID_STACK_NAME)
 			rc = run_operation_filter(fs, TUNEFS_FLAG_NOCLUSTER);
 		else if (err == TUNEFS_ET_PERFORM_ONLINE)
 			rc = run_operation_filter(fs, TUNEFS_FLAG_ONLINE);
diff --git a/tunefs.ocfs2/op_cloned_volume.c b/tunefs.ocfs2/op_cloned_volume.c
new file mode 100644
index 0000000..09e501f
--- /dev/null
+++ b/tunefs.ocfs2/op_cloned_volume.c
@@ -0,0 +1,150 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * op_cloned_volume.c
+ *
+ * ocfs2 tune utility to change the uuid and label of cloned volumes.
+ *
+ * Copyright (C) 2004, 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 version 2 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.
+ */
+
+#define _GNU_SOURCE  /* For strnlen() */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <uuid/uuid.h>
+
+#include "ocfs2/ocfs2.h"
+
+#include "libocfs2ne.h"
+
+
+#define CLONED_LABEL "-cloned"
+#define CLONED_LABEL_LEN (strlen(CLONED_LABEL))
+
+static void update_volume_label(ocfs2_filesys *fs,
+				const char *new_label)
+{
+	int len;
+	char label_buf[OCFS2_MAX_VOL_LABEL_LEN];
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super);
+
+	memset(label_buf, 0, OCFS2_MAX_VOL_LABEL_LEN);
+
+	if (new_label) {
+		len = strlen(new_label);
+		if (len > OCFS2_MAX_VOL_LABEL_LEN)
+			len = OCFS2_MAX_VOL_LABEL_LEN;
+		if (!memcmp(sb->s_label, new_label, len)) {
+			verbosef(VL_APP,
+				 "Device \"%s\" already has the label "
+				 "\"%.*s\"; label not updated\n",
+				 fs->fs_devname, OCFS2_MAX_VOL_LABEL_LEN,
+				 new_label);
+			return;
+		}
+		strncpy(label_buf, new_label, OCFS2_MAX_VOL_LABEL_LEN);
+	} else {
+		strncpy(label_buf, (char *)sb->s_label,
+			OCFS2_MAX_VOL_LABEL_LEN);
+		len = strnlen(label_buf, OCFS2_MAX_VOL_LABEL_LEN);
+
+		/*
+		 * If we don't have CLONED_LABEL at the end of the label,
+		 * add it.  Truncate the existing label if necessary to
+		 * fit CLONED_LABEL.
+		 */
+		if (strncmp(label_buf + len - CLONED_LABEL_LEN,
+			    CLONED_LABEL, CLONED_LABEL_LEN)) {
+			if ((len + CLONED_LABEL_LEN) > OCFS2_MAX_VOL_LABEL_LEN)
+				len = OCFS2_MAX_VOL_LABEL_LEN -
+					CLONED_LABEL_LEN;
+			strncpy(label_buf + len, CLONED_LABEL,
+				CLONED_LABEL_LEN);
+		}
+	}
+
+	verbosef(VL_APP,
+		 "Setting the label \"%.*s\" on device \"%s\"\n",
+		 OCFS2_MAX_VOL_LABEL_LEN, label_buf, fs->fs_devname);
+	memset(OCFS2_RAW_SB(fs->fs_super)->s_label, 0,
+	       OCFS2_MAX_VOL_LABEL_LEN);
+	strncpy((char *)sb->s_label, label_buf,
+		OCFS2_MAX_VOL_LABEL_LEN);
+}
+
+static void update_volume_uuid(ocfs2_filesys *fs)
+{
+	unsigned char new_uuid[OCFS2_VOL_UUID_LEN];
+
+	uuid_generate(new_uuid);
+	memcpy(OCFS2_RAW_SB(fs->fs_super)->s_uuid, new_uuid,
+	       OCFS2_VOL_UUID_LEN);
+}
+
+static errcode_t cloned_volume(ocfs2_filesys *fs, const char *new_label)
+{
+	errcode_t err;
+
+	if (!tools_interact_critical(
+		"Updating the UUID and label on cloned volume \"%s\".\n"
+		"DANGER: THIS WILL MODIFY THE UUID WITHOUT ACCESSING THE "
+		"CLUSTER SOFTWARE.  YOU MUST BE ABSOLUTELY SURE THAT NO "
+		"OTHER NODE IS USING THIS FILESYSTEM BEFORE MODIFYING "
+		"ITS UUID.\n"
+	        "Update the UUID and label? ",
+		fs->fs_devname))
+		return 0;
+
+	update_volume_uuid(fs);
+	update_volume_label(fs, new_label);
+
+	tunefs_block_signals();
+	err = ocfs2_write_super(fs);
+	tunefs_unblock_signals();
+
+	return err;
+}
+
+static int cloned_volume_run(struct tunefs_operation *op, ocfs2_filesys *fs,
+			     int flags)
+{
+	errcode_t err;
+	int rc = 0;
+	char *new_label = op->to_private;
+
+	err = cloned_volume(fs, new_label);
+	if (err) {
+		tcom_err(err,
+			 "- unable to update the uuid and label on "
+			 "device \"%s\"",
+			 fs->fs_devname);
+		rc = 1;
+	}
+
+	return rc;
+}
+
+
+DEFINE_TUNEFS_OP(cloned_volume,
+		 "Usage: cloned_volume [opts] <device> [<label>]\n",
+		 TUNEFS_FLAG_RW | TUNEFS_FLAG_SKIPCLUSTER,
+		 NULL,
+		 cloned_volume_run);
+
+#ifdef DEBUG_EXE
+int main(int argc, char *argv[])
+{
+	return tunefs_op_main(argc, argv, &cloned_volume_op);
+}
+#endif
-- 
1.5.6.3




More information about the Ocfs2-tools-devel mailing list