[Ocfs2-tools-devel] [PATCH]: Add removing slots support for ocfs2-tools

tao.ma tao.ma at oracle.com
Wed Jun 6 23:40:44 PDT 2007


tunefs.ocfs2 has been able to increase the slot for a long time. Now the 
support for removing slots is also added. There are also some changes in 
debugfs.ocfs2 and fsck.ocfs2 to be fit for this new feature.

The patch is processed by quilt. and the general description is as 
follows and any comments are welcome.
1. ocfs2_head_1.patch adds some macros in the header files.
2. debugfs_1.patch adds some output for "stats".
3. remove_slot_1.patch adds slot remove mechanism in tunefs.ocfs2.
4. tunefs_1.patch incorporate slot remove into tunefs.ocfs2.
5. group_check.patch adds group check mechanism in fsck.ocfs2 in case of 
bad slot remove.
6. orphan_check.patch adds orphan dir content check in fsck.ocfs2 in 
case of bad slot remove..
7. journal_check.patch adds journal content check in fsck.ocfs2.

I have also written some test scripts and programs to test the function 
and recovery. It works OK by now. They will also be sent out soon, but I 
think this series should be sent out  sooner rather than later for review.
-------------- next part --------------
Index: new.ocfs2-tools/debugfs.ocfs2/utils.c
===================================================================
--- new.ocfs2-tools.orig/debugfs.ocfs2/utils.c	2007-06-06 11:13:36.000000000 -0400
+++ new.ocfs2-tools/debugfs.ocfs2/utils.c	2007-06-06 11:20:06.000000000 -0400
@@ -42,10 +42,14 @@ void get_incompat_flag(uint32_t flag, GS
 	if (flag & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
 		g_string_append(str, "Sparse ");
 
+	if (flag & OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG)
+		g_string_append(str, "AbortedSlotRemove ");
+
 	if (flag & ~(OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV |
 		     OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG |
 		     OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT |
-		     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC))
+		     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC |
+		     OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG))
 		g_string_append(str, "Unknown ");
 
 	if (!str->len)
-------------- next part --------------
Index: new.ocfs2-tools/fsck.ocfs2/fsck.ocfs2.checks.8.in
===================================================================
--- new.ocfs2-tools.orig/fsck.ocfs2/fsck.ocfs2.checks.8.in	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/fsck.ocfs2/fsck.ocfs2.checks.8.in	2007-06-06 11:20:06.000000000 -0400
@@ -150,6 +150,13 @@ inode that doesn't match the descriptor'
 Answering yes updates the group descriptor's parent pointer to match the inode
 it resides in.
 
+.SS "GROUP_DUPLICATE"
+Group descriptors contain a pointer to the allocator inode which contains
+the chain they belong to.  A group descriptor was found in two allocator
+inodes so it may be duplicated.
+
+Answering yes removes the group descriptor from current allocator inode.
+
 .SS "GROUP_BLKNO"
 Group descriptors have a field which records their block location on disk.  A
 group descriptor was found at a given location but is recorded as being
@@ -657,6 +664,21 @@ Answering yes will refresh the superbloc
 only disable the copying of the backup superblock and will not effect the
 remaining \fIfsck.ocfs2\fR processing.
 
+.SS "ORPHAN_DIR_MISSING"
+While files are being deleted they are placed in an internal directory, named
+orphan directory. If an orphan directory does't exist, an OCFS2 volume can't
+be mounted successfully. Fsck has found the orphan directory is missing and
+would like to create it for future use.
+
+Answering yes creates the orphan directory in the system directory.
+
+.SS "JOURNAL_FILE_EMPTY"
+OCFS2 uses JDB for journalling and some journal files exist in the
+system directory. Fsck has found some journal file is empty and would
+like to extend it for future use.
+
+Answering yes extends the journal file in the system directory.
+
 .SH "SEE ALSO"
 .BR fsck.ocfs2(8)
 
Index: new.ocfs2-tools/fsck.ocfs2/pass0.c
===================================================================
--- new.ocfs2-tools.orig/fsck.ocfs2/pass0.c	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/fsck.ocfs2/pass0.c	2007-06-06 14:05:32.000000000 -0400
@@ -91,11 +91,76 @@ static void find_max_free_bits(struct oc
 	}
 }
 
+/* check whether the group really exists in the specified chain of
+ * the specified allocator file.
+ */
+static errcode_t check_group_parent(ocfs2_filesys *fs, uint64_t group,
+				    uint64_t ino, uint16_t chain,int *exist)
+{
+	errcode_t ret;
+	uint64_t gd_blkno;
+	char *buf = NULL, *gd_buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	struct ocfs2_group_desc *gd = NULL;
+	struct ocfs2_chain_rec *cr = NULL;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto out;
+
+	ret = ocfs2_read_inode(fs, ino, buf);
+	if (ret) {
+		goto out;
+	}
+
+	di = (struct ocfs2_dinode *)buf;
+
+	if (!(di->i_flags & OCFS2_VALID_FL) ||
+	    !(di->i_flags & OCFS2_BITMAP_FL) ||
+	    !(di->i_flags & OCFS2_CHAIN_FL))
+		goto out;
+
+	if (di->id1.bitmap1.i_total == 0)
+		goto out;
+
+	if (di->id2.i_chain.cl_next_free_rec <= chain)
+		goto out;
+
+	cr = &di->id2.i_chain.cl_recs[chain];
+
+	ret = ocfs2_malloc_block(fs->fs_io, &gd_buf);
+	if (ret)
+		goto out;
+
+	gd_blkno = cr->c_blkno;
+	while (gd_blkno) {
+		if (gd_blkno ==  group) {
+			*exist = 1;
+			break;
+		}
+
+		ret = ocfs2_read_group_desc(fs, gd_blkno, gd_buf);
+		if (ret)
+			goto out;
+		gd = (struct ocfs2_group_desc *)gd_buf;
+
+		gd_blkno = gd->bg_next_group;
+	}
+
+out:
+	if (gd_buf)
+		ocfs2_free(&gd_buf);
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
 static errcode_t repair_group_desc(o2fsck_state *ost,
 				   struct ocfs2_dinode *di,
 				   struct chain_state *cs,
 				   struct ocfs2_group_desc *bg,
-				   uint64_t blkno)
+				   uint64_t blkno,
+				   int *clear_ref)
 {
 	errcode_t ret = 0;
 	int changed = 0;
@@ -121,14 +186,35 @@ static errcode_t repair_group_desc(o2fsc
 	/* XXX maybe for advanced pain we could check to see if these 
 	 * kinds of descs have valid generations for the inodes they
 	 * reference */
-	if ((bg->bg_parent_dinode != di->i_blkno) &&
-	    prompt(ost, PY, PR_GROUP_PARENT,
+	if ((bg->bg_parent_dinode != di->i_blkno)) {
+		int exist = 0;
+		ret = check_group_parent(ost->ost_fs, bg->bg_blkno,
+					 bg->bg_parent_dinode,
+					 bg->bg_chain, &exist);
+
+		/* If we finds that the group really exists in the specified
+		 * chain of the specified alloc inode, then this may be a
+		 * duplicated group and we may need to remove it from current
+		 * inode.
+		 */
+		if (!ret && exist && prompt(ost, PY, PR_GROUP_DUPLICATE,
+		   "Group descriptor at block %"PRIu64" is "
+		   "referenced by inode %"PRIu64" but thinks its parent inode "
+		   "is %"PRIu64" and we can also see it in that inode."
+		    " So it may be duplicated.  Remove it from this inode?",
+		    blkno, di->i_blkno, bg->bg_parent_dinode)) {
+			*clear_ref = 1;
+			goto out;
+		}
+
+		if (prompt(ost, PY, PR_GROUP_PARENT,
 		   "Group descriptor at block %"PRIu64" is "
 		   "referenced by inode %"PRIu64" but thinks its parent inode "
 		   "is %"PRIu64".  Fix the descriptor's parent inode?", blkno,
 		   di->i_blkno, bg->bg_parent_dinode)) {
-		bg->bg_parent_dinode = di->i_blkno;
-		changed = 1;
+			bg->bg_parent_dinode = di->i_blkno;
+			changed = 1;
+		}
 	}
 
 	if ((bg->bg_blkno != blkno) &&
@@ -179,7 +265,7 @@ static errcode_t repair_group_desc(o2fsc
 
 	cs->cs_total_bits += bg->bg_bits;
 	cs->cs_free_bits += bg->bg_free_bits_count;
-
+out:
 	return ret;
 }
 
@@ -474,10 +560,18 @@ static errcode_t check_chain(o2fsck_stat
 			break;
 		}
 
-		ret = repair_group_desc(ost, di, cs, bg2, blkno);
+		ret = repair_group_desc(ost, di, cs, bg2, blkno, &clear_ref);
 		if (ret)
 			goto out;
 
+		/* If we find a duplicate chain, we need to clear it from the
+		 * current chain.
+		 *
+		 * Please note that all the groups below this group will also
+		 * be removed from this chain.
+		 */
+		if (clear_ref)
+			break;
 
 		/* the loop will now start by reading bg1->next_group */
 		memcpy(buf1, buf2, ost->ost_fs->fs_blocksize);
-------------- next part --------------
Index: new.ocfs2-tools/fsck.ocfs2/fsck.c
===================================================================
--- new.ocfs2-tools.orig/fsck.ocfs2/fsck.c	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/fsck.ocfs2/fsck.c	2007-06-06 14:08:06.000000000 -0400
@@ -174,6 +174,66 @@ static errcode_t check_superblock(o2fsck
 	return ret;
 }
 
+/* When we remove slot in tunefs.ocfs2, there may be some panic and
+ * we may empty some journal files, so we have to check whether the
+ * journal file is empty and extend it.
+ */
+static errcode_t check_journals(o2fsck_state *ost)
+{
+	errcode_t ret;
+	uint64_t blkno;
+	uint32_t num_clusters = 0;
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	ocfs2_filesys *fs = ost->ost_fs;
+	char fname[OCFS2_MAX_FILENAME_LEN];
+	uint16_t i, max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < max_slots; i++) {
+		ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE, i,
+						&blkno);
+		if (ret)
+			goto out;
+
+		ret = ocfs2_read_inode(fs, blkno, buf);
+		if (ret)
+			goto out;
+
+		di = (struct ocfs2_dinode *)buf;
+
+		if (di->i_clusters > 0) {
+			num_clusters = di->i_clusters;
+			continue;
+		}
+
+		if (num_clusters == 0) {
+			/* none of the journal has contents, severe errors. */
+			ret = OCFS2_ET_JOURNAL_TOO_SMALL;
+			goto out;
+		}
+
+		sprintf(fname,
+			ocfs2_system_inodes[JOURNAL_SYSTEM_INODE].si_name, i);
+		if (!prompt(ost, PY, PR_JOURNAL_FILE_EMPTY,
+			    "journal file %s is empty, extend it"
+			    " to %u clusters?", fname, num_clusters))
+			continue;
+
+		ret = ocfs2_make_journal(fs, blkno, num_clusters);
+		if (ret)
+			goto out;
+	}
+
+out:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
 static errcode_t write_out_superblock(o2fsck_state *ost)
 {
 	struct ocfs2_dinode *di = ost->ost_fs->fs_super;
@@ -182,6 +242,10 @@ static errcode_t write_out_superblock(o2
 	if (sb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG)
 		sb->s_feature_incompat &= ~OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG;
 
+	if (sb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG)
+		sb->s_feature_incompat &=
+				 ~OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG;
+
 	if (ost->ost_num_clusters)
 		di->i_clusters = ost->ost_num_clusters;
 
@@ -262,6 +326,9 @@ static int fs_is_clean(o2fsck_state *ost
 	else if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat &
 		  OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG))
 		strcpy(reason, "incomplete volume resize detected");
+	else if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat &
+		  OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG))
+		strcpy(reason, "incomplete slot remove detected");
 	else if (sb->s_state & OCFS2_ERROR_FS)
 		strcpy(reason, "contains a file system with errors");
 	else if (sb->s_max_mnt_count > 0 &&
@@ -655,6 +722,15 @@ int main(int argc, char **argv)
 	printf("  max slots:          %u\n\n", 
 	       OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_max_slots);
 
+	if (open_flags & OCFS2_FLAG_RW) {
+		ret = check_journals(ost);
+		if (ret) {
+			printf("fsck saw unrecoverable errors in the journal "
+				"files and will not continue.\n");
+			goto unlock;
+		}
+	}
+
 	ret = maybe_replay_journals(ost, filename, open_flags, blkno, blksize);
 	if (ret) {
 		printf("fsck encountered unrecoverable errors while "
-------------- next part --------------
Index: new.ocfs2-tools/libocfs2/include/ocfs2.h
===================================================================
--- new.ocfs2-tools.orig/libocfs2/include/ocfs2.h	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/libocfs2/include/ocfs2.h	2007-06-06 11:20:06.000000000 -0400
@@ -76,7 +76,8 @@
 #define OCFS2_LIB_FEATURE_INCOMPAT_SUPP		(OCFS2_FEATURE_INCOMPAT_SUPP | \
 						 OCFS2_FEATURE_INCOMPAT_HEARTBEAT_DEV | \
 						 OCFS2_FEATURE_INCOMPAT_RESIZE_INPROG | \
-						 OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT)
+						 OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT   | \
+						 OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG)
 
 #define OCFS2_LIB_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_SUPP
 
Index: new.ocfs2-tools/libocfs2/include/ocfs2_fs.h
===================================================================
--- new.ocfs2-tools.orig/libocfs2/include/ocfs2_fs.h	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/libocfs2/include/ocfs2_fs.h	2007-06-06 11:20:06.000000000 -0400
@@ -109,6 +109,12 @@
 /* Support for sparse allocation in b-trees */
 #define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC	0x0010
 
+/* tunefs sets this incompat flag before starting slot remove and clears it
+ * at the end. This flag protects users from inadvertently mounting the fs
+ * after an aborted run without fsck-ing.
+ */
+#define OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG	0x0020
+
 /*
  * backup superblock flag is used to indicate that this volume
  * has backup superblocks.
-------------- next part --------------
Index: new.ocfs2-tools/fsck.ocfs2/pass4.c
===================================================================
--- new.ocfs2-tools.orig/fsck.ocfs2/pass4.c	2007-06-06 11:13:33.000000000 -0400
+++ new.ocfs2-tools/fsck.ocfs2/pass4.c	2007-06-06 11:20:06.000000000 -0400
@@ -152,6 +152,46 @@ out:
 	return ret_flags;
 }
 
+static errcode_t create_orphan_dir(o2fsck_state *ost, char *fname)
+{
+	errcode_t ret;
+	uint64_t blkno;
+	ocfs2_filesys *fs = ost->ost_fs;
+
+	/* create inode for system file */
+	ret = ocfs2_new_system_inode(fs, &blkno,
+			ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_mode,
+			ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_iflags);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_expand_dir(fs, blkno, fs->fs_sysdir_blkno);
+	if (ret)
+		goto bail;
+
+	/* Add the inode to the system dir */
+	ret = ocfs2_link(fs, fs->fs_sysdir_blkno, fname, blkno,
+			 OCFS2_FT_DIR);
+	if (ret == OCFS2_ET_DIR_NO_SPACE) {
+		ret = ocfs2_expand_dir(fs, fs->fs_sysdir_blkno,
+				       fs->fs_sysdir_blkno);
+		if (!ret)
+			ret = ocfs2_link(fs, fs->fs_sysdir_blkno,
+					 fname, blkno, OCFS2_FT_DIR);
+	}
+
+	if (ret)
+		goto bail;
+
+	/* we have created an orphan dir under system dir and updated the disk,
+	 * so we have to update the refs in ost accordingly.
+	 */
+	o2fsck_icount_delta(ost->ost_icount_refs, fs->fs_sysdir_blkno, 1);
+	o2fsck_icount_delta(ost->ost_icount_in_inodes, fs->fs_sysdir_blkno, 1);
+bail:
+	return ret;
+}
+
 static errcode_t replay_orphan_dir(o2fsck_state *ost)
 {
 	errcode_t ret = OCFS2_ET_CORRUPT_SUPERBLOCK;
@@ -171,8 +211,25 @@ static errcode_t replay_orphan_dir(o2fsc
 
 		ret = ocfs2_lookup(ost->ost_fs, ost->ost_fs->fs_sysdir_blkno,
 				   name, bytes, NULL, &ino);
-		if (ret)
-			goto out;
+		if (ret) {
+			if (ret != OCFS2_ET_FILE_NOT_FOUND)
+				goto out;
+
+			/* orphan dir is missing, it may be caused by an
+			 * unsuccessful removing slots in tunefs.ocfs2.
+			 * so create it.
+			 */
+	   		if (prompt(ost, PY, PR_ORPHAN_DIR_MISSING,
+				   "%s is missing in system directory. "
+				   "Create it?", name)) {
+				ret = create_orphan_dir(ost, name);
+				if (ret) {
+					com_err(whoami, ret, "while creating"
+						"orphan directory %s", name);
+					continue;
+				}
+			}
+		}
 
 		ret = ocfs2_dir_iterate(ost->ost_fs, ino,
 					OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL,
-------------- next part --------------
Index: new.ocfs2-tools/tunefs.ocfs2/Makefile
===================================================================
--- new.ocfs2-tools.orig/tunefs.ocfs2/Makefile	2007-06-06 11:13:36.000000000 -0400
+++ new.ocfs2-tools/tunefs.ocfs2/Makefile	2007-06-06 11:20:06.000000000 -0400
@@ -30,7 +30,7 @@ DEFINES = -DOCFS2_FLAT_INCLUDES -DVERSIO
 
 MANS = tunefs.ocfs2.8
 
-CFILES = tunefs.c query.c
+CFILES = tunefs.c query.c remove_slot.c
 HFILES = tunefs.h
 
 OBJS = $(subst .c,.o,$(CFILES))
Index: new.ocfs2-tools/tunefs.ocfs2/remove_slot.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ new.ocfs2-tools/tunefs.ocfs2/remove_slot.c	2007-06-06 13:45:20.000000000 -0400
@@ -0,0 +1,716 @@
+/*
+ * remove_slot.c
+ *
+ * The function for removing slots from ocfs2 volume.
+ *
+ * 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 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 021110-1307, USA.
+ *
+ */
+
+#include <inttypes.h>
+#include <bitops.h>
+#include <ocfs2.h>
+
+#include <assert.h>
+
+#include "tunefs.h"
+
+extern ocfs2_tune_opts opts;
+
+enum relink_alloc_action {
+	RELINK_EXTENT_ALLOC = 1,
+	RELINK_INODE_ALLOC
+};
+
+struct relink_ctxt {
+	enum relink_alloc_action action;
+	struct ocfs2_chain_rec *cr;
+	uint16_t new_slot;
+	uint64_t new_inode;
+	char *gd_buf;
+	char *start_gd_buf;
+	char *src_inode;
+	char *dst_inode;
+	char *ex_buf;
+};
+
+struct remove_slot_ctxt {
+	ocfs2_filesys *fs;
+	uint16_t removed_slot;
+	errcode_t errcode;
+};
+
+static errcode_t change_sub_alloc_slot(ocfs2_filesys *fs,
+				       uint64_t blkno,
+				       struct relink_ctxt *ctxt)
+{
+	errcode_t ret;
+	struct ocfs2_dinode *di = NULL;
+	struct ocfs2_extent_block *eb = NULL;
+
+	if (ctxt->action == RELINK_EXTENT_ALLOC) {
+		/* change sub alloc slot in the extent block. */
+		ret = ocfs2_read_extent_block(fs, blkno, ctxt->ex_buf);
+		if (ret)
+			goto bail;
+
+		eb = (struct ocfs2_extent_block *)ctxt->ex_buf;
+		eb->h_suballoc_slot = ctxt->new_slot;
+
+		ret = ocfs2_write_extent_block(fs, blkno, ctxt->ex_buf);
+		if (ret)
+			goto bail;
+	} else {
+		/* change sub alloc slot in the inode. */
+		ret = ocfs2_read_inode(fs, blkno, ctxt->ex_buf);
+		if (ret)
+			goto bail;
+
+		di = (struct ocfs2_dinode *)ctxt->ex_buf;
+		di->i_suballoc_slot = ctxt->new_slot;
+
+		ret = ocfs2_write_inode(fs, blkno, ctxt->ex_buf);
+		if (ret)
+			goto bail;
+	}
+bail:
+	return ret;
+}
+
+/*
+ * Move the chain record to the new alloc file.
+ * The chain's start group is stored in ctxt->start_gd_buf and
+ * tail group is ctxt->gd_buf. They are set by move_chain_rec.
+ *
+ * Note:
+ * currently we link all the group descriptor in the chain "0" of the
+ * new alloc file.
+ */
+static errcode_t move_groups(ocfs2_filesys *fs,
+			     struct relink_ctxt *ctxt)
+{
+	errcode_t ret;
+	struct ocfs2_group_desc *gd_start = NULL, *gd_end = NULL;
+	struct ocfs2_dinode *di = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+	struct ocfs2_chain_rec *cr = NULL;
+	uint32_t clusters;
+
+	ret = ocfs2_read_inode(fs, ctxt->new_inode, ctxt->dst_inode);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)ctxt->dst_inode;
+	cl = &di->id2.i_chain;
+	cr = &cl->cl_recs[0];
+
+	gd_start = (struct ocfs2_group_desc *)ctxt->start_gd_buf;
+	gd_end = (struct ocfs2_group_desc *)ctxt->gd_buf;
+
+	assert (gd_end->bg_next_group == 0);
+
+	/* link the original chain to the group list's end. */
+	gd_end->bg_next_group = cr->c_blkno;
+	ret = ocfs2_write_group_desc(fs, gd_end->bg_blkno, ctxt->gd_buf);
+	if (ret)
+		goto bail;
+
+	/* link the group list's head in the chain.
+	 * modify the chain record and the new files simultaneously.
+	 */
+	cr->c_blkno = gd_start->bg_blkno;
+	cr->c_total += ctxt->cr->c_total;
+	cr->c_free += ctxt->cr->c_free;
+
+	/* If the chain is empty, increase the free_rec. */
+	if (cl->cl_next_free_rec == 0)
+		cl->cl_next_free_rec = 1;
+
+	di->id1.bitmap1.i_total += ctxt->cr->c_total;
+	di->id1.bitmap1.i_used += ctxt->cr->c_total;
+	di->id1.bitmap1.i_used -= ctxt->cr->c_free;
+	clusters = ctxt->cr->c_total / cl->cl_bpc;
+	di->i_clusters += clusters;
+	di->i_size += clusters * fs->fs_clustersize;
+
+	ret = ocfs2_write_inode(fs, ctxt->new_inode, ctxt->dst_inode);
+	if (ret)
+		goto bail;
+
+bail:
+	return ret;
+}
+
+/*
+ * This function will iterate the chain_rec and do the following modifications:
+ * 1. modify  Sub Alloc Slot in extent block/inodes accordingly.
+ * 2. change the group parent and bg_chain according to its future owner.
+ * 3. link the chain to the new slot files.
+ */
+static errcode_t move_chain_rec(ocfs2_filesys *fs, struct relink_ctxt *ctxt)
+{
+	errcode_t ret = 0;
+	int i, start, end = 1;
+	uint64_t blkno, gd_blkno = ctxt->cr->c_blkno;
+	struct ocfs2_group_desc *gd = NULL;
+
+	if (gd_blkno == 0)
+		goto bail;
+
+	memset(ctxt->start_gd_buf, 0, fs->fs_blocksize);
+
+	while (gd_blkno) {
+		ret = ocfs2_read_group_desc(fs, gd_blkno, ctxt->gd_buf);
+		if (ret)
+			return OCFS2_CHAIN_ERROR;
+
+		/* record the first group descriptor. */
+		if (ctxt->start_gd_buf[0] == 0)
+			memcpy(ctxt->start_gd_buf, ctxt->gd_buf,
+			       fs->fs_blocksize);
+
+		gd = (struct ocfs2_group_desc *)ctxt->gd_buf;
+
+		end = 1;
+		/* Modify the "Sub Alloc Slot" in the extent block/inodes. */
+		while (end < gd->bg_bits) {
+			start = ocfs2_find_next_bit_set(gd->bg_bitmap,
+							gd->bg_bits, end);
+			if (start >= gd->bg_bits)
+				break;
+
+			end = ocfs2_find_next_bit_clear(gd->bg_bitmap,
+							gd->bg_bits, start);
+
+			for (i = start; i < end; i++) {
+				blkno = gd_blkno + i;
+				ret = change_sub_alloc_slot(fs, blkno, ctxt);
+				if (ret)
+					goto bail;
+			}
+		}
+
+		gd->bg_chain = 0;
+		gd->bg_parent_dinode = ctxt->new_inode;
+
+		ret = ocfs2_write_group_desc(fs, gd_blkno, ctxt->gd_buf);
+		if (ret)
+			goto bail;
+
+		gd_blkno = gd->bg_next_group;
+	}
+
+	/* move the chain to the new slot file.
+	 * now the start_gd_buf contains the first group descriptor in the
+	 * chain and gd_buf contains the last group descriptor in the chain.
+	 */
+	ret = move_groups(fs, ctxt);
+
+bail:
+	return ret;
+}
+
+static errcode_t relink_system_alloc(ocfs2_filesys *fs,
+				     uint16_t removed_slot,
+				     enum relink_alloc_action action)
+{
+	errcode_t ret;
+	uint16_t i;
+	uint64_t blkno;
+	struct ocfs2_dinode *di = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+	struct relink_ctxt ctxt;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+
+	if (action == RELINK_EXTENT_ALLOC)
+		ret = ocfs2_lookup_system_inode(fs,
+						EXTENT_ALLOC_SYSTEM_INODE,
+						removed_slot, &blkno);
+	else
+		ret = ocfs2_lookup_system_inode(fs,
+						INODE_ALLOC_SYSTEM_INODE,
+						removed_slot, &blkno);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &ctxt.src_inode);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during relinking system alloc");
+		goto bail;
+	}
+
+	ret = ocfs2_read_inode(fs, blkno, ctxt.src_inode);
+	if (ret) {
+		com_err(opts.progname, ret, "while reading inode "
+			"%"PRIu64" during relinking system alloc", blkno);
+		goto bail;
+	}
+
+	di = (struct ocfs2_dinode *)ctxt.src_inode;
+
+	if (!(di->i_flags & OCFS2_VALID_FL) ||
+	    !(di->i_flags & OCFS2_BITMAP_FL) ||
+	    !(di->i_flags & OCFS2_CHAIN_FL)) {
+		com_err(opts.progname, 0, "system  alloc %"PRIu64" corrupts."
+			"during relinking system alloc", blkno);
+		goto bail;
+	}
+
+	if (di->id1.bitmap1.i_total == 0)
+		goto bail;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &ctxt.gd_buf);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during relinking system alloc");
+		goto bail;
+	}
+
+	ret = ocfs2_malloc_block(fs->fs_io, &ctxt.ex_buf);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during relinking system alloc");
+		goto bail;
+	}
+
+	ret = ocfs2_malloc_block(fs->fs_io, &ctxt.dst_inode);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during relinking system alloc");
+		goto bail;
+	}
+
+	ret = ocfs2_malloc_block(fs->fs_io, &ctxt.start_gd_buf);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during relinking system alloc");
+		goto bail;
+	}
+
+	cl = &di->id2.i_chain;
+	ctxt.action = action;
+
+	/* Iterate all the chain record and move them to the new slots.
+	 * In order to balance the chain to the reserved slots, we divide the
+	 * chains among all the slots.
+	 */
+	for (i = 0; i < cl->cl_next_free_rec; i ++) {
+		ctxt.new_slot = i % opts.num_slots;
+		if (ctxt.action == RELINK_EXTENT_ALLOC)
+			ret = ocfs2_lookup_system_inode(fs,
+						EXTENT_ALLOC_SYSTEM_INODE,
+						ctxt.new_slot,
+						&ctxt.new_inode);
+		else
+			ret = ocfs2_lookup_system_inode(fs,
+						INODE_ALLOC_SYSTEM_INODE,
+						ctxt.new_slot,
+						&ctxt.new_inode);
+		if (ret)
+			goto bail;
+
+		ctxt.cr = &cl->cl_recs[i];
+
+		ret = move_chain_rec(fs, &ctxt);
+		if (ret) {
+			com_err(opts.progname, ret,
+				"while iterating system alloc file");
+			goto bail;
+		}
+	}
+
+	/* emtpy the original alloc files. */
+	di->id1.bitmap1.i_used = 0;
+	di->id1.bitmap1.i_total = 0;
+	di->i_clusters = 0;
+	di->i_size = 0;
+
+	cl = &di->id2.i_chain;
+	cl->cl_next_free_rec = 0;
+	memset(cl->cl_recs, 0, sizeof(struct ocfs2_chain_rec) * cl->cl_count);
+
+	ret = ocfs2_write_inode(fs, blkno, ctxt.src_inode);
+
+bail:
+	if (ctxt.gd_buf)
+		ocfs2_free(&ctxt.gd_buf);
+	if (ctxt.ex_buf)
+		ocfs2_free(&ctxt.ex_buf);
+	if (ctxt.dst_inode)
+		ocfs2_free(&ctxt.dst_inode);
+	if (ctxt.ex_buf)
+		ocfs2_free(&ctxt.start_gd_buf);
+	if (ctxt.src_inode)
+		ocfs2_free(&ctxt.src_inode);
+
+	return ret;
+}
+
+static errcode_t truncate_journal_orphan_dir(ocfs2_filesys *fs,
+					     uint16_t removed_slot)
+{
+	errcode_t ret;
+	uint64_t blkno;
+
+	/* Truncate orphan dir. */
+	ret = ocfs2_lookup_system_inode(fs, ORPHAN_DIR_SYSTEM_INODE,
+					removed_slot, &blkno);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_truncate(fs, blkno, 0);
+	if (ret)
+		goto bail;
+
+	/* Truncate the journal file. */
+	ret = ocfs2_lookup_system_inode(fs, JOURNAL_SYSTEM_INODE,
+					removed_slot, &blkno);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_truncate(fs, blkno, 0);
+
+bail:
+	return ret;
+}
+
+static int remove_slot_iterate(struct ocfs2_dir_entry *dirent, int offset,
+			       int blocksize, char *buf, void *priv_data)
+{
+	struct remove_slot_ctxt *ctxt = (struct remove_slot_ctxt *)priv_data;
+	int ret_flags = 0;
+	errcode_t ret;
+	char fname[SYSTEM_FILE_NAME_MAX];
+
+	sprintf(fname, "%04d", ctxt->removed_slot);
+
+	if (strstr(dirent->name, fname)) {
+
+		ret = ocfs2_delete_inode(ctxt->fs, dirent->inode);
+		if (ret) {
+			ret_flags |= OCFS2_DIRENT_ERROR;
+			ctxt->errcode = ret;
+			goto out;
+		}
+
+		dirent->inode = 0;
+		ret_flags |= OCFS2_DIRENT_CHANGED;
+	}
+
+out:
+	return ret_flags;
+}
+
+/* Remove all the system files which belong to the removed slot. */
+static errcode_t remove_slot_entry(ocfs2_filesys *fs, uint16_t removed_slot)
+{
+	struct remove_slot_ctxt ctxt = {
+		.fs = fs,
+		.removed_slot = removed_slot,
+		.errcode = 0
+	};
+
+	ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno,
+			  OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL,
+			  remove_slot_iterate, &ctxt);
+
+	return ctxt.errcode;
+}
+
+/* Decrease the i_links_count of the inode "blkno". */
+static errcode_t decrease_link_count(ocfs2_filesys *fs, uint16_t blkno)
+{
+	errcode_t ret;
+	char *buf = NULL;
+	struct ocfs2_dinode *di  = NULL;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_read_inode(fs, blkno, buf);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)buf;
+
+	if (di->i_links_count > 0)
+		di->i_links_count--;
+	else {
+		ret = OCFS2_ET_INODE_NOT_VALID;
+		goto bail;
+	}
+
+	ret = ocfs2_write_inode(fs, blkno, buf);
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+errcode_t remove_slots(ocfs2_filesys *fs)
+{
+	errcode_t ret = 0;
+	uint16_t old_num = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+	uint16_t removed_slot = old_num - 1;
+
+	/* we will remove the slots once at a time so that fsck.ocfs2 can work
+	 * well and we can continue our work easily in case of any panic.
+	 */
+	while (removed_slot >= opts.num_slots) {
+		/* Link the specified extent alloc file to others. */
+		ret = relink_system_alloc(fs, removed_slot,
+					  RELINK_EXTENT_ALLOC);
+		if (ret)
+			goto bail;
+
+		/* Link the specified inode alloc file to others. */
+		ret = relink_system_alloc(fs, removed_slot,
+					  RELINK_INODE_ALLOC);
+		if (ret)
+			goto bail;
+
+		/* Truncate the journal and orphan dir to release their
+		 * clusters to the global bitmap.
+		 */
+		ret = truncate_journal_orphan_dir(fs, removed_slot);
+		if (ret)
+			goto bail;
+
+		/* Now, we decrease the max_slots first and then remove the
+		 * slots for the reason that:
+		 *
+		 * 1. ocfs2_lock_down_clusters needs to lock all the journal
+		 * files. so if we delete the journal entry first and fail
+		 * to decrease the max_slots, the whole cluster can't be
+		 * locked any more due to the loss of journals.
+		 *
+		 * 2. Now all the resources except the inodes are freed
+		 * so it is safe to decrease the slots first, and if any
+		 * panic happens after we decrease the slots, we can ignore
+		 * them, and actually if we want to increase the slot in the
+		 * future, we can reuse these inodes.
+		 */
+
+		/* The slot number is updated in the super block.*/
+		OCFS2_RAW_SB(fs->fs_super)->s_max_slots--;
+		ret = ocfs2_write_super(fs);
+		if (ret)
+			goto bail;
+
+		/* The extra system dir entries should be removed. */
+		ret = remove_slot_entry(fs, removed_slot);
+		if (ret)
+			goto bail;
+
+		/* Decrease the i_links_count in system file directory
+		 * since the orphan_dir is removed.
+		 */
+		ret = decrease_link_count(fs, fs->fs_sysdir_blkno);
+		if (ret)
+			goto bail;
+
+		removed_slot--;
+	}
+
+bail:
+	return ret;
+}
+
+static int orphan_iterate(struct ocfs2_dir_entry *dirent, int offset,
+			  int blocksize, char *buf, void *priv_data)
+{
+	int *has_orphan = (int *)priv_data;
+
+	*has_orphan = 1;
+
+	/* we have found some file/dir in the orphan_dir,
+	 * so there is no need to go on the iteration.
+	 */
+	return OCFS2_DIRENT_ABORT;
+}
+
+static errcode_t orphan_dir_check(ocfs2_filesys *fs,
+				  int *has_orphan)
+{
+	errcode_t ret = 0;
+	uint64_t blkno;
+	int i;
+	uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+
+	for (i = opts.num_slots ; i < max_slots; ++i) {
+		ret = ocfs2_lookup_system_inode(fs, ORPHAN_DIR_SYSTEM_INODE,
+						i, &blkno);
+		if (ret) {
+			com_err(opts.progname, ret, "while looking up "
+				"orphan dir for slot %u during orphan dir "
+				"check", i);
+			goto bail;
+		}
+
+		ret = ocfs2_dir_iterate(fs, blkno,
+					OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL,
+					orphan_iterate, has_orphan);
+		if (ret || *has_orphan) {
+			com_err(opts.progname, 0, "orphan dir for slot %u "
+				"has entries", i);
+			goto bail;
+		}
+	}
+
+bail:
+	return ret;
+}
+
+static errcode_t local_alloc_check(ocfs2_filesys *fs,
+				  int *has_local_alloc)
+{
+	errcode_t ret = 0;
+	uint16_t i;
+	uint64_t blkno;
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during local alloc check");
+		goto bail;
+	}
+
+	for (i = opts.num_slots ; i < max_slots; ++i) {
+		ret = ocfs2_lookup_system_inode(fs, LOCAL_ALLOC_SYSTEM_INODE,
+						i, &blkno);
+		if (ret) {
+			com_err(opts.progname, ret, "while looking up "
+				"local alloc for slot %u during local alloc "
+				"check", i);
+			goto bail;
+		}
+
+		ret = ocfs2_read_inode(fs, blkno, buf);
+		if (ret) {
+			com_err(opts.progname, ret, "while reading inode "
+				"%"PRIu64" during local alloc check", blkno);
+			goto bail;
+		}
+
+		di = (struct ocfs2_dinode *)buf;
+
+		if (di->id1.bitmap1.i_total > 0) {
+			*has_local_alloc = 1;
+			com_err(opts.progname, 0, "local alloc for slot %u "
+				"isn't empty", i);
+			goto bail;
+		}
+	}
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+static errcode_t truncate_log_check(ocfs2_filesys *fs,
+				    int *has_truncate_log)
+{
+	errcode_t ret = 0;
+	uint16_t i;
+	uint64_t blkno;
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	uint16_t max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret) {
+		com_err(opts.progname, ret, "while allocating a block "
+			"during truncate log check");
+		goto bail;
+	}
+
+	for (i = opts.num_slots ; i < max_slots; ++i) {
+		ret = ocfs2_lookup_system_inode(fs, TRUNCATE_LOG_SYSTEM_INODE,
+						i, &blkno);
+		if (ret) {
+			com_err(opts.progname, ret, "while looking up "
+				"truncate log for slot %u during truncate log "
+				"check", i);
+			goto bail;
+		}
+
+		ret = ocfs2_read_inode(fs, blkno, buf);
+		if (ret) {
+			com_err(opts.progname, ret, "while reading inode "
+				"%"PRIu64" during truncate log check", blkno);
+			goto bail;
+		}
+
+		di = (struct ocfs2_dinode *)buf;
+
+		if (di->id2.i_dealloc.tl_used > 0) {
+			*has_truncate_log = 1;
+			com_err(opts.progname, 0, "truncate log for slot %u "
+				"isn't empty", i);
+			goto bail;
+		}
+	}
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+errcode_t remove_slot_check(ocfs2_filesys *fs)
+{
+	errcode_t ret;
+	int has_orphan = 0, has_truncate_log = 0, has_local_alloc = 0;
+
+	/* we don't allow remove_slot to coexist with other tunefs
+	 * options to keep things simple.
+	 */
+	if (opts.backup_super ||opts.vol_label ||
+	     opts.mount || opts.jrnl_size || opts.num_blocks) {
+		com_err(opts.progname, 0, "Cannot remove slot"
+			" along with other tasks");
+		exit(1);
+	}
+
+	ret = orphan_dir_check(fs, &has_orphan);
+	if (ret || has_orphan) {
+		ret = 1;
+		goto bail;
+	}
+
+	ret = local_alloc_check(fs, &has_local_alloc);
+	if (ret || has_local_alloc) {
+		ret = 1;
+		goto bail;
+	}
+
+	ret = truncate_log_check(fs, &has_truncate_log);
+	if (ret || has_truncate_log) {
+		ret = 1;
+		goto bail;
+	}
+bail:
+	return ret;
+}
Index: new.ocfs2-tools/tunefs.ocfs2/tunefs.h
===================================================================
--- new.ocfs2-tools.orig/tunefs.ocfs2/tunefs.h	2007-06-06 11:13:36.000000000 -0400
+++ new.ocfs2-tools/tunefs.ocfs2/tunefs.h	2007-06-06 11:20:06.000000000 -0400
@@ -92,3 +92,5 @@ typedef struct _ocfs2_tune_opts {
 
 void print_query(char *queryfmt);
 
+errcode_t remove_slots(ocfs2_filesys *fs);
+errcode_t remove_slot_check(ocfs2_filesys *fs);
-------------- next part --------------
ocfs2_head_1.patch 
debugfs_1.patch 
remove_slot_1.patch 
tunefs_1.patch 
group_check.patch 
orphan_check.patch 
journal_check.patch 
-------------- next part --------------
Index: new.ocfs2-tools/tunefs.ocfs2/tunefs.c
===================================================================
--- new.ocfs2-tools.orig/tunefs.ocfs2/tunefs.c	2007-06-06 11:13:36.000000000 -0400
+++ new.ocfs2-tools/tunefs.ocfs2/tunefs.c	2007-06-06 11:20:06.000000000 -0400
@@ -863,7 +863,10 @@ static errcode_t update_slots(ocfs2_file
 	errcode_t ret = 0;
 
 	block_signals(SIG_BLOCK);
-	ret = add_slots(fs);
+	if (opts.num_slots > OCFS2_RAW_SB(fs->fs_super)->s_max_slots)
+		ret = add_slots(fs);
+	else
+		ret = remove_slots(fs);
 	block_signals(SIG_UNBLOCK);
 	if (ret)
 		return ret;
@@ -1253,7 +1256,7 @@ int main(int argc, char **argv)
 	int upd_incompat = 0;
 	int upd_backup_super = 0;
 	char *tmpstr;
-	uint16_t tmp;
+	uint16_t max_slots;
 	uint64_t def_jrnl_size = 0;
 	uint64_t num_clusters;
 	int dirty = 0;
@@ -1377,15 +1380,23 @@ int main(int argc, char **argv)
 	}
 
 	/* validate num slots */
+	max_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
 	if (opts.num_slots) {
-		tmp = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
-		if (opts.num_slots > tmp) {
+		if (opts.num_slots < max_slots) {
+			ret = remove_slot_check(fs);
+			if (ret) {
+				com_err(opts.progname, 0,
+					"remove slot check failed. ");
+				goto unlock;
+			}
+		}
+		if (opts.num_slots != max_slots) {
 			printf("Changing number of node slots from %d to %d\n",
-			       tmp, opts.num_slots);
+			       max_slots, opts.num_slots);
 		} else {
 			com_err(opts.progname, 0, "Node slots (%d) has to be "
-				"more than the configured node slots (%d)",
-			       opts.num_slots, tmp);
+				"different from the configured node slots",
+			       opts.num_slots);
 			goto unlock;
 		}
 
@@ -1445,6 +1456,19 @@ int main(int argc, char **argv)
 		upd_incompat = 1;
 	}
 
+	/* Set remove slots incompat flag on superblock */
+	if (opts.num_slots < max_slots) {
+		OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat |=
+			OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG;
+		ret = ocfs2_write_super(fs);
+		if (ret) {
+			com_err(opts.progname, ret,
+				"while writing remove slot incompat flag");
+			goto unlock;
+		}
+		upd_incompat = 1;
+	}
+
 	/* update volume label */
 	if (opts.vol_label) {
 		update_volume_label(fs, &upd_label);
@@ -1467,8 +1491,12 @@ int main(int argc, char **argv)
 				"while updating node slots");
 			goto unlock;
 		}
+		/* Clear remove slot incompat flag on superblock */
+		if (opts.num_slots < max_slots)
+			OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat &=
+				~OCFS2_FEATURE_INCOMPAT_REMOVE_SLOT_INPROG;
 		if (upd_slots)
-			printf("Added node slots\n");
+			printf("Changed node slots\n");
 	}
 
 	/* change mount type */


More information about the Ocfs2-tools-devel mailing list