[Ocfs2-tools-devel] Patch for journal truncate of ocfs2-tools.

tao.ma tao.ma at oracle.com
Wed May 16 00:41:00 PDT 2007


Marcos,
    I have added "-fy" in this new script. It works fine now with a 
small volume.
Please check.

>>
>> tao.ma wrote:
>>> Sunil Mushran wrote:
>>>> Yes,  that's correct.
>>>>
>>>> One solution is to expect this in your test script.
>>>> As in, run with -fy to clean this up after a new mkfs.  
-------------- next part --------------
Index: programs/tunefs-test/tunefs-test.sh
===================================================================
--- programs/tunefs-test/tunefs-test.sh	(revision 98)
+++ programs/tunefs-test/tunefs-test.sh	(working copy)
@@ -437,6 +437,66 @@ Add_Backup_Super()
 	   test_pass;
 	fi;
 }
+
+#
+# Journal_Node_Change - Change journal size and node slots simultaneously.
+#
+Journal_Node_Change()
+{
+	LogMsg "tunefs_test : Testing Changing journal size and node slots simultaneously."
+
+	# Increase journal size and node slots.
+	(( ++NUM_OF_TESTS ))
+	Set_Volume_For_Test;
+	CURRENT_TEST="Increase Journal Size to ${JOURNAL3} and increase nodes to ${NNODES2}";
+	echo "y"|${TUNEFS_BIN} -N ${NNODES2} -J size=${JOURNAL3} ${DEVICE} 2>&1 >> ${TUNEFSLOG};
+
+	# Check whether we succeed.
+	Check_Volume;
+	SB_NNODES=`${DEBUGFS_BIN} -n -R "stats" ${DEVICE}|${GREP} Slots| \
+		 ${GAWK} '{print \$4; exit}'`;
+	if [ ${SB_NNODES} -ne ${NNODES2} ]; then
+	   test_fail;
+	   LogMsg "tunefs_test : #of nodes change failed. \c"
+	   LogMsg "Superblock number of nodes (${SB_NNODES})"
+	else
+		SB_JSIZE=`${DEBUGFS_BIN} -n -R "ls -l //" ${DEVICE}|${GREP} -i Journal:0001| 
+			${GAWK} '{print \$6; exit}'`;
+		if [ ${SB_JSIZE} -ne ${JOURNAL3} ]; then
+		   test_fail;
+		   LogMsg "tunefs_test : Journal size change failed.\c"
+		   LogMsg " Superblock Journal Size (${SB_JSIZE})"
+		else
+		   test_pass;
+		fi;
+	fi;
+
+
+	# Decrease journal size and increase the slot nums.
+	(( ++NUM_OF_TESTS ))
+	CURRENT_TEST="Decrease Journal Size to ${JOURNAL2} and increase nodes to ${NNODES3}";
+	echo "y"|${TUNEFS_BIN} -N ${NNODES3} -J size=${JOURNAL2} ${DEVICE} 2>&1 >> ${TUNEFSLOG};
+
+	# Check whether we succeed.
+	Check_Volume;
+	SB_NNODES=`${DEBUGFS_BIN} -n -R "stats" ${DEVICE}|${GREP} Slots| \
+		 ${GAWK} '{print \$4; exit}'`;
+	if [ ${SB_NNODES} -ne ${NNODES3} ]; then
+	   test_fail;
+	   LogMsg "tunefs_test : #of nodes change failed. \c"
+	   LogMsg "Superblock number of nodes (${SB_NNODES})"
+	else
+		SB_JSIZE=`${DEBUGFS_BIN} -n -R "ls -l //" ${DEVICE}|${GREP} -i Journal:0001| 
+			${GAWK} '{print \$6; exit}'`;
+		if [ ${SB_JSIZE} -ne ${JOURNAL2} ]; then
+		   test_fail;
+		   LogMsg "tunefs_test : Journal size change failed.\c"
+		   LogMsg " Superblock Journal Size (${SB_JSIZE})"
+		else
+		   test_pass;
+		fi;
+	fi;
+}
 ################################################################
 
 #
@@ -522,6 +582,8 @@ Change_Mount_Type
 
 Add_Backup_Super
 
+Journal_Node_Change
+
 test_summary
 
 LogMsg "\ntunefs_test: Ending test (`date +%F-%H-%M-%S`)\n" 
Index: programs/truncate/ocfs2-tools/test_truncate.c
===================================================================
--- programs/truncate/ocfs2-tools/test_truncate.c	(revision 0)
+++ programs/truncate/ocfs2-tools/test_truncate.c	(revision 0)
@@ -0,0 +1,327 @@
+/*
+ * test_truncate.c
+ *
+ * test file for ocfs2_truncate
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is used to test whether ocfs2_truncate can truncate
+ * a file to a specified size.
+ *
+ * An additional option "c" is to create a file before we test truncating.
+ * We can give the tree depth so that the file can be created with the
+ * specified tree depth.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stddef.h>
+
+#include <ocfs2/ocfs2.h>
+
+
+char *progname = NULL;
+
+static void handle_signal (int sig)
+{
+	switch (sig) {
+	case SIGTERM:
+	case SIGINT:
+		exit(1);
+	}
+
+	return ;
+}
+
+enum operations {
+	CREATE = 1,
+	SET_SIZE
+};
+
+struct{
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	enum operations ops;
+	uint64_t extras;
+} options;
+
+static char *device = NULL;
+
+static void usage (const char *progname)
+{
+	fprintf(stderr, "usage: %s -f file-name\n"
+		"\t\t[-c tree-depth] [-s new-size] device\n",
+		progname);
+
+	exit(0);
+}
+
+static int read_options(int argc, char **argv)
+{
+	int c;
+
+	progname = basename(argv[0]);
+
+	if (argc < 2)
+		return 1;
+
+	while(1) {
+		c = getopt(argc, argv, "f:c:s:");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'f':	/* file name */
+			strcpy(options.file_name, optarg);
+			break;
+
+		case 'c':
+			if (options.ops != 0)
+				usage(progname);
+			options.ops = CREATE;
+			options.extras = strtoull(optarg, NULL, 0);
+			if (options.extras > 4) {
+				com_err(progname, 0, "We can only create"
+					"a file with tree_depth <= 4");
+				exit(1);
+			}
+			break;
+
+		case 's':
+			if (options.ops != 0)
+				usage(progname);
+			options.ops = SET_SIZE;
+			options.extras = strtoull(optarg, NULL, 0);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (optind < argc && argv[optind])
+		device = argv[optind];
+
+	return 0;
+}
+
+static errcode_t open_test_inode(ocfs2_filesys *fs, char *name, uint64_t *ino)
+{
+	errcode_t ret = 0;
+	uint64_t tmp_blkno = 0;
+	int namelen = strlen(name);
+
+	ret = ocfs2_lookup(fs, fs->fs_root_blkno, name, namelen,
+			   NULL, &tmp_blkno);
+	if (!ret) {
+		*ino = tmp_blkno;
+		return 0;
+	} else if (ret != OCFS2_ET_FILE_NOT_FOUND)
+		return ret;
+
+	ret = ocfs2_new_inode(fs, &tmp_blkno, S_IFREG | 0755);
+	if (ret)
+		return ret;
+
+	ret = ocfs2_link(fs, fs->fs_root_blkno, name,
+			 tmp_blkno, OCFS2_FT_REG_FILE);
+	if (ret == OCFS2_ET_DIR_NO_SPACE) {
+		ret = ocfs2_expand_dir(fs, fs->fs_root_blkno,
+				       fs->fs_root_blkno);
+		if (ret)
+			return ret;
+
+		ret = ocfs2_link(fs, fs->fs_root_blkno, name,
+				 tmp_blkno, OCFS2_FT_REG_FILE);
+		if (ret)
+			return ret;
+	} else if (ret)
+		return ret;
+
+	*ino = tmp_blkno;
+
+	return 0;
+}
+
+/*
+ * This function is similar to ocfs2_extend_allocation() as both extend files.
+ * However, this one ensures that the extent record tree grows much faster.
+ */
+static errcode_t custom_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
+					  uint32_t new_clusters)
+{
+	errcode_t ret;
+	uint32_t n_clusters;
+	uint32_t i;
+	uint64_t blkno;
+	uint64_t tmpblk;
+
+	while (new_clusters) {
+		ret = ocfs2_new_clusters(fs, 1, new_clusters, &blkno,
+					 &n_clusters);
+		if (ret)
+			goto bail;
+
+		/* In order to ensure the extent records are not coalesced,
+		 * we insert each cluster in reverse. */
+		for(i = n_clusters; i; --i) {
+			tmpblk = blkno + ocfs2_clusters_to_blocks(fs, i - 1);
+		 	ret = ocfs2_insert_extent(fs, ino, tmpblk, 1);
+			if (ret) 
+				goto bail;	
+		}
+	 	new_clusters -= n_clusters;
+	}
+
+bail:
+	return ret;
+}
+
+static inline int get_rand(int min, int max)
+{
+	if (min == max)
+		return min;
+
+	return min + (rand() % (max - min));
+}
+
+/* Create the file with the specified tree_depth. */
+static errcode_t create_file(ocfs2_filesys *fs, uint64_t ino, uint64_t tree_depth)
+{
+	errcode_t ret;
+	uint64_t size;
+	char *buf = NULL;
+	uint32_t clusters = 0;
+	struct ocfs2_dinode *di = NULL;
+	int random_rec_in_last_eb, random_rec_in_dinode;
+	int ext_rec_per_inode = ocfs2_extent_recs_per_inode(fs->fs_blocksize);
+	int ext_rec_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize);
+
+	srand((unsigned int)fs);
+	/* In order to build up the tree quickly, we allocate only 1 cluster
+	 * to an extent, so we can calculate the extent numbers and set the
+	 * clusters accordingly.
+	 */
+	random_rec_in_dinode = get_rand(1, ext_rec_per_inode);
+
+	if (tree_depth == 0)
+		clusters = random_rec_in_dinode;
+	else {
+		/* Now we will create a tree with the tree_depth.
+		 * we will increase the tree detph gradually.
+		 * 
+		 * In order to speed up the generation, we just set
+		 * the tree root to be "3"(there are only 2 extent rec
+		 * in the dinode which are full.
+		 */
+		clusters = 2;
+		while (tree_depth--) {
+			clusters *= ext_rec_per_eb;
+			random_rec_in_last_eb = get_rand(1, ext_rec_per_eb);
+			clusters += random_rec_in_last_eb;
+		}
+	}
+	
+	if (clusters == 0)
+		return -1;
+
+	ret = custom_extend_allocation(fs, ino, clusters);
+	if (ret)
+		goto bail;
+
+	/* set the file size accordingly since ocfs2_truncate will
+	 * check the size during its operation.
+	 */
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+	
+	ret = ocfs2_read_inode(fs, ino, buf);
+	if (ret)
+		goto bail;
+
+	size = clusters * fs->fs_clustersize;
+	di = (struct ocfs2_dinode *)buf;
+	di->i_size = size;
+
+	ret = ocfs2_write_inode(fs, ino, buf);	
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+int main (int argc, char **argv)
+{
+	ocfs2_filesys *fs = NULL;
+	errcode_t ret = 1;
+	uint64_t inode;
+
+	initialize_ocfs_error_table();
+
+#define INSTALL_SIGNAL(sig)					\
+	do {							\
+		if (signal(sig, handle_signal) == SIG_ERR) {	\
+		    printf("Could not set " #sig "\n");		\
+		    goto bail;					\
+		}						\
+	} while (0)
+
+	INSTALL_SIGNAL(SIGTERM);
+	INSTALL_SIGNAL(SIGINT);
+
+	memset(&options, 0, sizeof(options));
+	if (read_options(argc, argv)) {
+		usage(progname);
+		goto bail;
+	}
+
+	if (!device || !options.ops || !options.file_name[0])
+		goto bail;
+
+	ret = ocfs2_open(device, OCFS2_FLAG_RW, 0, 0, &fs);
+	if (ret) {
+		com_err(progname, ret, "while opening \"%s\"", device);
+		goto bail;
+	}
+
+	ret = open_test_inode(fs, options.file_name, &inode);
+	if (ret) {
+		com_err(progname, ret, "while open test inode");
+		goto bail;
+	}
+
+	if (options.ops == CREATE)
+		ret = create_file(fs, inode, options.extras);
+	else
+		ret = ocfs2_truncate(fs, inode, options.extras);
+
+	if (ret)
+		com_err(progname, ret, "while doing the test");
+bail:
+	if (fs)
+		ocfs2_close(fs);
+
+	return ret;
+}
Index: programs/truncate/ocfs2-tools/ocfs2_truncate.sh
===================================================================
--- programs/truncate/ocfs2-tools/ocfs2_truncate.sh	(revision 0)
+++ programs/truncate/ocfs2-tools/ocfs2_truncate.sh	(revision 0)
@@ -0,0 +1,247 @@
+#!/bin/bash
+#
+# 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, 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.
+# 
+# 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.
+#
+
+################################################################
+
+#
+# This script will check the function of ocfs2_truncate in ocfs2-tools.
+#
+# The main test script is truncate_test and it works like this:
+# 1. Create a file with the specified tree depth.
+
+# 2. Truncate the file to some size.
+#
+# 3. Using fsck.ocfs2 to check whether the volume is corrupted.
+#
+# Please Note that this script much have the --with-truncate option to
+# do the truncate test.
+#
+
+MKFS_BIN=`which mkfs.ocfs2`
+FSCK_BIN=`which fsck.ocfs2`
+DEBUGFS_BIN=`which debugfs.ocfs2`
+TRUNCATE_BIN=
+LOG_DIR=$PWD
+
+BLOCKDEV=`which blockdev`
+DEVICE=""
+
+FSCK_OUTPUT="/tmp/fsck.ocfs2.output"
+FSCK_OUTPUT_STANDARD="/tmp/fsck.ocfs2.output.std"
+
+#
+# usage			Display help information and exit.
+#
+function usage()
+{
+	local script="${0##*/}"
+	cat <<-EOF
+	Usage: $script [options] device
+
+	Options:
+	      --help                       display this help and exit
+	      --log-dir=DIRECTORY          use the DIRECTORY to store the log
+	      --with-fsck=PROGRAM          use the PROGRAM as fsck.ocfs2
+	      --with-mkfs=PROGRAM          use the PROGRAM as fswreck
+	      --with-debugfs=PROGRAM       use the PROGRAM as mkfs.ocfs2
+	      --with-truncate=PROGRAM      use the PROGRAM as truncate tools
+
+	Examples:
+
+	  $script --with-truncate=./test_truncate --log-dir=/tmp /dev/sdd1
+	EOF
+}
+
+#
+# warn_if_bad
+#
+#	$1	the result to check
+#	$2	the result we want
+#	$3	the error messge
+#	$4	the line num of the caller
+#
+#
+function warn_if_bad()
+{
+	local -i rc="$1"
+	local -i wanted="$2"
+	local script="${0##*/}"
+
+	# Ignore if no problems
+	[ "$rc" -eq "$wanted" ] && return 0
+
+	# Broken
+	shift
+	echo "$script: $@">&2
+	echo "$script: $@">&3
+	return "$rc"
+}
+
+#
+# exit_if_bad		Put out error message(s) if $1 has bad RC.
+#
+#	$1	the result to check
+#	$2	the result we want
+#	$3	the error messge
+#	$4	the line num of the caller
+#
+#       Exits with 1 unless $1 is 0
+#
+function exit_if_bad()
+{
+	warn_if_bad "$@" || exit 1
+	return 0
+}
+
+function set_log_file()
+{
+	mkdir -p $LOG_DIR
+	if [ ! -d $LOG_DIR ]; then
+		echo "log_dir[$LOG_DIR] not exist, use [$PWD] instead."
+		LOG_DIR=$PWD
+	fi
+	
+	output_log="$LOG_DIR/`date +%F-%H-%M-%S`-output.log"
+	exec 3>&1
+	exec 1>$output_log 2>&1
+}
+
+function truncate_test()
+{
+	local tree_depth="$1"
+
+	test_file="test$RANDOM"
+	$TRUNCATE_BIN -c $tree_depth -f $test_file $DEVICE
+
+	$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+	diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+	exit_if_bad $? "0" "fsck find errors when creating file $test_file." $LINENO
+
+	# get the file size first.
+	new_size=`echo "stat $test_file"|$DEBUGFS_BIN $DEVICE|grep "Size:"|awk '{print $8}'`
+
+	# truncate the file for 5 times, every time truncate
+	# it to half size.
+	for((i=0;i<5;i++))
+	do
+		new_size=`expr $new_size / 2`
+		$TRUNCATE_BIN -f $test_file -s $new_size $DEVICE
+
+		$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+		diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+		exit_if_bad $? "0" "fail to truncate file to size $new_size." $LINENO
+	done
+
+	#truncate the file to 0.
+	$TRUNCATE_BIN -f $test_file -s 0 $DEVICE
+
+	$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT
+	diff $FSCK_OUTPUT $FSCK_OUTPUT_STANDARD
+	exit_if_bad $? "0" "fail to truncate file to size $new_size." $LINENO
+}
+
+function normal_test()
+{
+	local test_file=""
+	local new_size=""
+	local -i i=0
+
+	for blocksize in 512 1024 2048 4096
+	do
+		for clustersize in \
+			4096 8192 16384 32768 65536 131072 262144 524288 1048576
+		do
+
+			dd if=/dev/zero of=$DEVICE bs=4096 count=3
+			$MKFS_BIN -b $blocksize -C $clustersize $DEVICE
+
+			# In some instance, fsck.ocfs2 will find "CHAIN_CPG"
+			# for a volume. So we fix it first.
+			$FSCK_BIN -fy $DEVICE>$FSCK_OUTPUT_STANDARD
+
+			#save the perfect fsck output first for our future use.
+			$FSCK_BIN -f $DEVICE>$FSCK_OUTPUT_STANDARD
+
+			truncate_test 0
+			truncate_test 1
+		done
+	done
+}
+
+################################################################
+
+#
+# main
+#
+if [ "$#" -eq "0" ]
+then
+	usage
+	exit 255
+fi
+
+while [ "$#" -gt "0" ]
+do
+	case "$1" in
+	"--help")
+		usage
+		exit 255
+		;;
+	"--log-dir="*)
+		LOG_DIR="${1#--log-dir=}"
+		;;
+	"--with-fsck="*)
+		FSCK_BIN="${1#--with-fsck=}"
+		;;
+	"--with-mkfs="*)
+		MKFS_BIN="${1#--with-mkfs=}"
+		;;
+	"--with-debugfs="*)
+		DEBUGFS_BIN="${1#--with-debugfs=}"
+		;;
+	"--with-truncate="*)
+		TRUNCATE_BIN="${1#--with-truncate=}"
+		;;
+	*)
+		DEVICE="$1"
+		;;
+	esac
+	shift
+done
+
+if [ ! -b "$DEVICE" ]; then
+	echo "invalid block device - $DEVICE"
+	usage
+	exit 1
+fi
+
+which $TRUNCATE_BIN
+if [ "$?" != "0" ]; then
+	echo "$TRUNCATE_BIN not exist, can't go on the test."
+	usage
+	exit 1
+fi
+
+set_log_file
+
+#from now on all the command and log will be recorded to the logfile.
+set -x
+
+normal_test
+
+exit 0

Property changes on: programs/truncate/ocfs2-tools/ocfs2_truncate.sh
___________________________________________________________________
Name: svn:executable
   + *

Index: programs/truncate/ocfs2-tools/Makefile
===================================================================
--- programs/truncate/ocfs2-tools/Makefile	(revision 0)
+++ programs/truncate/ocfs2-tools/Makefile	(revision 0)
@@ -0,0 +1,19 @@
+TOPDIR = ../../../
+
+include $(TOPDIR)/Preamble.make
+
+TESTS = test_truncate
+
+CFLAGS = -O2 -Wall -g
+
+SOURCES = test_truncate.c
+OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
+
+DIST_FILES = $(SOURCES)
+
+BIN_PROGRAMS = test_truncate
+
+test_truncate: $(OBJECTS)
+	$(LINK)  -locfs2 -lcom_err
+
+include $(TOPDIR)/Postamble.make


More information about the Ocfs2-tools-devel mailing list