[Ocfs2-tools-commits] taoma commits r1284 - in trunk: debugfs.ocfs2 debugfs.ocfs2/include fsck.ocfs2 libocfs2 libocfs2/include mkfs.ocfs2 tunefs.ocfs2

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Thu Jan 18 20:38:33 PST 2007


Author: taoma
Date: 2007-01-18 20:38:22 -0800 (Thu, 18 Jan 2007)
New Revision: 1284

Added:
   trunk/libocfs2/backup_super.c
Modified:
   trunk/debugfs.ocfs2/commands.c
   trunk/debugfs.ocfs2/include/main.h
   trunk/debugfs.ocfs2/main.c
   trunk/fsck.ocfs2/fsck.c
   trunk/fsck.ocfs2/fsck.ocfs2.checks.8.in
   trunk/fsck.ocfs2/pass1.c
   trunk/libocfs2/Makefile
   trunk/libocfs2/include/ocfs2.h
   trunk/libocfs2/include/ocfs2_fs.h
   trunk/libocfs2/openfs.c
   trunk/mkfs.ocfs2/mkfs.c
   trunk/mkfs.ocfs2/mkfs.h
   trunk/tunefs.ocfs2/tunefs.c
Log:
Add backup-super facilities for ocfs2-tools.
Signed-off-by: smushran

Modified: trunk/debugfs.ocfs2/commands.c
===================================================================
--- trunk/debugfs.ocfs2/commands.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/debugfs.ocfs2/commands.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -294,7 +294,100 @@
 	return  i;
 }
 
+/* open the device, read the block from the device and get the
+ * blocksize from the offset of the ocfs2_super_block.
+ */
+static errcode_t get_blocksize(char* dev, uint64_t offset, uint64_t *blocksize)
+{
+	errcode_t ret;
+	uint64_t blkno;
+	uint32_t blocksize_bits;
+	char *buf = NULL;
+	io_channel* channel = NULL;
+	struct ocfs2_dinode *di = NULL;
+
+	ret = io_open(dev, OCFS2_FLAG_RO, &channel);
+	if (ret)
+		goto bail;
+
+	/* since ocfs2_super_block inode can be stored in OCFS2_MIN_BLOCKSIZE,
+	 * so here we just use the minimum block size and read the information
+	 * in the specific offset.
+	 */
+	ret = io_set_blksize(channel, OCFS2_MIN_BLOCKSIZE);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_malloc_block(channel, &buf);
+	if (ret)
+		goto bail;
+
+	blkno = offset / OCFS2_MIN_BLOCKSIZE;
+	ret = io_read_block(channel, blkno, 1, buf);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)buf;
+	blocksize_bits = le32_to_cpu(di->id2.i_super.s_blocksize_bits);
+	*blocksize = 1ULL << blocksize_bits;
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	if (channel)
+		io_close(channel);
+	return ret;
+}
 /*
+ * process_open_args
+ *
+ */
+static int process_open_args(char **args,
+			     uint64_t *superblock, uint64_t *blocksize)
+{
+	errcode_t ret = 0;
+	uint32_t s;
+	char *ptr;
+	uint64_t byte_off[OCFS2_MAX_BACKUP_SUPERBLOCKS], blksize;
+	int num, ind = 2;
+
+	if (!args[ind])
+		return 0;
+
+	if (args[ind] && !strcmp(args[ind], "-s"))
+		ind++;
+	else
+		return -1;
+
+	if(!args[ind])
+		return -1;
+
+	num = ocfs2_get_backup_super_offset(NULL,
+					    byte_off, ARRAY_SIZE(byte_off));
+	if (!num)
+		return -1;
+
+	s = strtoul(args[ind], &ptr, 0);
+	if (s < 1 || s > num) {
+		fprintf (stderr, "Backup super block is outside of valid range"
+			 "(between 1 and %d)\n", num);
+		return -1;
+	}
+
+	ret = get_blocksize(args[1], byte_off[s-1], &blksize);
+	if (ret) {
+		com_err(args[0],ret, "Can't get the blocksize from the device"
+			" by the num %u\n", s);
+		goto bail;
+	}
+
+	*blocksize = blksize;
+	*superblock = byte_off[s-1]/blksize;
+	ret = 0;
+bail:
+	return ret;
+}
+
+/*
  * get_slotnum()
  *
  */
@@ -475,18 +568,19 @@
 	char sysfile[SYSTEM_FILE_NAME_MAX];
 	int i;
 	struct ocfs2_super_block *sb;
+	uint64_t superblock = 0, block_size = 0;
 
 	if (gbls.device)
 		do_close (NULL);
 
-	if (dev == NULL) {
-		fprintf (stderr, "usage: %s <device>\n", args[0]);
+	if (dev == NULL || process_open_args(args, &superblock, &block_size)) {
+		fprintf (stderr, "usage: %s <device> [-s num]\n", args[0]);
 		return ;
 	}
 
 	flags = gbls.allow_write ? OCFS2_FLAG_RW : OCFS2_FLAG_RO;
         flags |= OCFS2_FLAG_HEARTBEAT_DEV_OK;
-	ret = ocfs2_open(dev, flags, 0, 0, &gbls.fs);
+	ret = ocfs2_open(dev, flags, superblock, block_size, &gbls.fs);
 	if (ret) {
 		gbls.fs = NULL;
 		com_err(args[0], ret, "while opening context for device %s",
@@ -687,7 +781,7 @@
 	printf ("logdump <slot#>\t\t\t\tPrints journal file for the node slot\n");
 	printf ("ls [-l] <filespec>\t\t\tList directory\n");
 	printf ("ncheck <block#> ...\t\t\tList all pathnames of the inode(s)/lockname(s)\n");
-	printf ("open <device>\t\t\t\tOpen a device\n");
+	printf ("open <device> [-s backup#]\t\t\t\tOpen a device\n");
 	printf ("quit, q\t\t\t\t\tExit the program\n");
 	printf ("rdump [-v] <filespec> <outdir>\t\tRecursively dumps from src to a dir on a mounted filesystem\n");
 	printf ("slotmap\t\t\t\t\tShow slot map\n");

Modified: trunk/debugfs.ocfs2/include/main.h
===================================================================
--- trunk/debugfs.ocfs2/include/main.h	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/debugfs.ocfs2/include/main.h	2007-01-19 04:38:22 UTC (rev 1284)
@@ -88,6 +88,7 @@
 typedef struct _dbgfs_opts {
 	int allow_write;
 	int no_prompt;
+	uint32_t sb_num;
 	char *cmd_file;
 	char *one_cmd;
 	char *device;

Modified: trunk/debugfs.ocfs2/main.c
===================================================================
--- trunk/debugfs.ocfs2/main.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/debugfs.ocfs2/main.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -51,9 +51,10 @@
 	g_print ("usage: %s -l [<logentry> ... [allow|off|deny]] ...\n", progname);
 	g_print ("usage: %s -d, --decode <lockres>\n", progname);
 	g_print ("usage: %s -e, --encode <lock type> <block num> <generation>\n", progname);
-	g_print ("usage: %s [-f cmdfile] [-R request] [-V] [-w] [-n] [-?] [device]\n", progname);
+	g_print ("usage: %s [-f cmdfile] [-R request] [-s backup#] [-V] [-w] [-n] [-?] [device]\n", progname);
 	g_print ("\t-f, --file <cmdfile>\tExecute commands in cmdfile\n");
 	g_print ("\t-R, --request <command>\tExecute a single command\n");
+	g_print ("\t-s, --superblock <backup#>\tOpen the device using a backup superblock\n");
 	g_print ("\t-w, --write\t\tOpen in read-write mode instead of the default of read-only\n");
 	g_print ("\t-V, --version\t\tShow version\n");
 	g_print ("\t-n, --noprompt\t\tHide prompt\n");
@@ -188,6 +189,7 @@
 static void get_options(int argc, char **argv, dbgfs_opts *opts)
 {
 	int c;
+	char *ptr = NULL;
 	static struct option long_options[] = {
 		{ "file", 1, 0, 'f' },
 		{ "request", 1, 0, 'R' },
@@ -198,6 +200,7 @@
 		{ "noprompt", 0, 0, 'n' },
 		{ "decode", 0, 0, 'd' },
 		{ "encode", 0, 0, 'e' },
+		{ "superblock", 0, 0, 's' },
 		{ 0, 0, 0, 0}
 	};
 
@@ -205,7 +208,7 @@
 		if (decodemode || encodemode || logmode)
 			break;
 
-		c = getopt_long(argc, argv, "lf:R:deV?wn", long_options, NULL);
+		c = getopt_long(argc, argv, "lf:R:deV?wns:", long_options, NULL);
 		if (c == -1)
 			break;
 
@@ -257,6 +260,10 @@
 			exit(0);
 			break;
 
+		case 's':
+			opts->sb_num = strtoul(optarg, &ptr, 0);
+			break;
+
 		default:
 			usage(gbls.progname);
 			break;
@@ -476,7 +483,10 @@
 		gbls.interactive++;
 
 	if (opts.device) {
-		line = g_strdup_printf ("open %s", opts.device);
+		if (opts.sb_num)
+			line = g_strdup_printf ("open %s -s %u", opts.device, opts.sb_num);
+		else
+			line = g_strdup_printf ("open %s", opts.device);
 		do_command (line);
 		g_free (line);
 	}

Modified: trunk/fsck.ocfs2/fsck.c
===================================================================
--- trunk/fsck.ocfs2/fsck.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/fsck.ocfs2/fsck.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -74,13 +74,14 @@
 {
 	fprintf(stderr,
 		"Usage: fsck.ocfs2 [ -fGnuvVy ] [ -b superblock block ]\n"
-		"		    [ -B block size ] device\n"
+		"		    [ -B block size ] [-r num] device\n"
 		"\n"
 		"Critical flags for emergency repair:\n" 
 		" -n		Check but don't change the file system\n"
 		" -y		Answer 'yes' to all repair questions\n"
 		" -f		Force checking even if file system is clean\n"
 		" -F		Ignore cluster locking (dangerous!)\n"
+		" -r		restore backup superblock(dangerous!)\n"
 		"\n"
 		"Less critical flags:\n"
 		" -b superblock	Treat given block as the super block\n"
@@ -191,6 +192,32 @@
 	return ocfs2_write_super(ost->ost_fs);
 }
 
+static errcode_t update_backup_super(o2fsck_state *ost)
+{
+	errcode_t ret;
+	int num;
+	struct ocfs2_dinode *di = ost->ost_fs->fs_super;
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(di);
+	uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+
+	if (!OCFS2_HAS_COMPAT_FEATURE(sb, OCFS2_FEATURE_COMPAT_BACKUP_SB))
+		return 0;
+
+	num = ocfs2_get_backup_super_offset(ost->ost_fs,
+					    blocks, ARRAY_SIZE(blocks));
+	if (!num)
+		return 0;
+
+	ret = ocfs2_refresh_backup_super(ost->ost_fs, blocks, num);
+	if (ret) {
+		com_err(whoami, ret, "while refreshing backup superblocks.");
+		goto bail;
+	}
+
+bail:
+	return ret;
+}
+
 static void scale_time(time_t secs, unsigned *scaled, char **units)
 {
 	if (secs < 60) {
@@ -410,12 +437,63 @@
 	return ret;
 }
 
+static errcode_t recover_backup_super(o2fsck_state *ost,
+				      char* device, int sb_num)
+{
+	errcode_t ret;
+	uint64_t offsets[OCFS2_MAX_BACKUP_SUPERBLOCKS], blksize, sb;
+	ocfs2_filesys *fs = NULL;
+
+	if (sb_num < 1 || sb_num > OCFS2_MAX_BACKUP_SUPERBLOCKS)
+		return -1;
+
+	ocfs2_get_backup_super_offset(NULL, offsets, ARRAY_SIZE(offsets));
+
+	/* iterate all the blocksize to get the right one. */
+	for (blksize = OCFS2_MIN_BLOCKSIZE;
+		blksize <= OCFS2_MAX_BLOCKSIZE;	blksize <<= 1) {
+		sb = offsets[sb_num - 1] / blksize;
+		/* Here we just give the possible value of block num and
+		 * block size to ocfs2_open and this function will check
+		 * them and return '0' if they meet the right one.
+		 */
+		ret = ocfs2_open(device, OCFS2_FLAG_RW, sb, blksize, &fs);
+		if (!ret)
+			break;
+	}
+
+	if (ret)
+		goto bail;
+
+	/* recover the backup information to superblock. */
+	if (prompt(ost, PN, PR_RECOVER_BACKUP_SUPERBLOCK,
+	    	   "Recover superblock information from backup block"
+		   "#%"PRIu64"?", sb)) {
+		fs->fs_super->i_blkno = OCFS2_SUPER_BLOCK_BLKNO;
+		ret = ocfs2_write_super(fs);
+		if (ret)
+			goto bail;
+	}
+
+	/* no matter whether the user recover the superblock or not above,
+	 * we should return 0 in case the superblock can be opened
+	 * without the recovery.
+	 */
+	ret = 0;
+
+bail:
+	if (fs)
+		ocfs2_close(fs);
+	return ret;
+}
+
 int main(int argc, char **argv)
 {
 	char *filename;
 	int64_t blkno, blksize;
 	o2fsck_state _ost, *ost = &_ost;
 	int c, open_flags = OCFS2_FLAG_RW | OCFS2_FLAG_STRICT_COMPAT_CHECK;
+	int sb_num = 0;
 	int fsck_mask = FSCK_OK;
 	errcode_t ret;
 
@@ -434,7 +512,7 @@
 	setlinebuf(stderr);
 	setlinebuf(stdout);
 
-	while((c = getopt(argc, argv, "b:B:fFGnuvVy")) != EOF) {
+	while((c = getopt(argc, argv, "b:B:fFGnuvVyr:")) != EOF) {
 		switch (c) {
 			case 'b':
 				blkno = read_number(optarg);
@@ -496,6 +574,10 @@
 				version();
 				break;
 
+			case 'r':
+				sb_num = read_number(optarg);
+				break;
+
 			default:
 				fsck_mask |= FSCK_USAGE;
 				print_usage();
@@ -523,6 +605,17 @@
 
 	filename = argv[optind];
 
+	/* recover superblock should be called at first. */
+	if (sb_num) {
+		ret = recover_backup_super(ost, filename, sb_num);
+		if (ret) {
+			com_err(whoami, ret, "recover superblock failed.\n");
+			fsck_mask |= FSCK_ERROR;
+			goto out;
+		}
+
+	}
+
 	ret = open_and_check(ost, filename, open_flags, blkno, blksize);
 	if (ret) {
 		fsck_mask |= FSCK_ERROR;
@@ -638,6 +731,12 @@
 		if (ret)
 			com_err(whoami, ret, "while writing back the "
 				"superblock");
+		else {
+			ret = update_backup_super(ost);
+			if (ret)
+				com_err(whoami, ret,
+					"while updating backup superblock.");
+		}
 	}
 
 unlock:

Modified: trunk/fsck.ocfs2/fsck.ocfs2.checks.8.in
===================================================================
--- trunk/fsck.ocfs2/fsck.ocfs2.checks.8.in	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/fsck.ocfs2/fsck.ocfs2.checks.8.in	2007-01-19 04:38:22 UTC (rev 1284)
@@ -648,6 +648,12 @@
 Answering yes removes the file data associated with the inode and frees
 the inode.
 
+.SS "RECOVER_BACKUP_SUPERBLOCK"
+A ocfs2 volume has many backup superblocks. User can recover the superblock
+when it is corrupted.
+
+Answering yes will copy the backup block to the superblock location.
+
 .SH "SEE ALSO"
 .BR fsck.ocfs2(8)
 

Modified: trunk/fsck.ocfs2/pass1.c
===================================================================
--- trunk/fsck.ocfs2/pass1.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/fsck.ocfs2/pass1.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -1023,6 +1023,18 @@
 	return ret;
 }
 
+static inline int bit_in_backup_super(uint64_t bit,
+				      uint32_t *clusters, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (bit == clusters[i])
+			return 1;
+	}
+	return 0;
+}
+
 /* once we've iterated all the inodes we should have the current working
  * set of which blocks we think are in use.  we use this to derive the set
  * of clusters that should be allocated in the cluster chain allocators.  we
@@ -1033,6 +1045,9 @@
 	errcode_t ret;
 	uint64_t blkno, last_cbit, cbit, cbit_found;
 	struct ocfs2_cluster_group_sizes cgs;
+	uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+	uint32_t clusters[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+	int backup_super = 0, num = 0, i;
 
 	ocfs2_calc_cluster_groups(ost->ost_fs->fs_clusters,
 				  ost->ost_fs->fs_blocksize, &cgs);
@@ -1061,6 +1076,22 @@
 		goto out;
 	}
 
+	/* handle the condition of backup superblock. */
+	memset(&blocks, 0, sizeof(blocks));
+	memset(&clusters, 0, sizeof(clusters));
+	if (OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(ost->ost_fs->fs_super),
+				     OCFS2_FEATURE_COMPAT_BACKUP_SB)) {
+		num = ocfs2_get_backup_super_offset(ost->ost_fs, blocks,
+					   ARRAY_SIZE(blocks));
+		if (num) {
+			backup_super = 1;
+			for (i = 0; i < num; i++)
+				clusters[i] =
+					ocfs2_blocks_to_clusters(ost->ost_fs,
+								 blocks[i]);
+		}
+	}
+
 	/* we walk our found blocks bitmap to find clusters that we think
 	 * are in use.  each time we find a block in a cluster we skip ahead
 	 * to the first block of the next cluster when looking for the next.
@@ -1072,6 +1103,10 @@
 	 * we special case the number of clusters as the cluster offset which
 	 * indicates that the rest of the bits to the end of the bitmap
 	 * should be clear.
+	 *
+	 * we should take backup superblock as a special case since it doesn't
+	 * belong to any inode. So it shouldn't be exist in
+	 * ost->ost_allocated_clusters.
 	 */
 	for (last_cbit = 0, cbit = 0;
 	     cbit < ost->ost_fs->fs_clusters; 
@@ -1097,7 +1132,13 @@
 
 		/* clear set bits that should have been clear up to cbit */
 		while (cbit_found < cbit) {
-			force_cluster_bit(ost, ci, cbit_found, 0);
+			/* check whether the volume has backup blocks
+			 * and if yes, check whether the cluster contains
+			 * one of the backup blocks.
+			 */
+			if (!backup_super ||
+			    !bit_in_backup_super(cbit_found, clusters, num))
+				force_cluster_bit(ost, ci, cbit_found, 0);
 			cbit_found++;
 			ret = ocfs2_bitmap_find_next_set(ci->ci_chains, cbit_found, 
 							 &cbit_found);

Modified: trunk/libocfs2/Makefile
===================================================================
--- trunk/libocfs2/Makefile	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/libocfs2/Makefile	2007-01-19 04:38:22 UTC (rev 1284)
@@ -80,7 +80,8 @@
 	truncate.c	\
 	unix_io.c	\
 	unlink.c	\
-	lockid.c
+	lockid.c	\
+	backup_super.c
 
 HFILES =				\
 	include/bitmap.h		\

Added: trunk/libocfs2/backup_super.c
===================================================================
--- trunk/libocfs2/backup_super.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/libocfs2/backup_super.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -0,0 +1,165 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * backup_super.c
+ *
+ * Backup superblocks for an OCFS2 volume.
+ *
+ * Copyright (C) 2006 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.
+ *
+ */
+
+#include <errno.h>
+#include "ocfs2.h"
+
+/* In case we don't have fs_blocksize, we will return
+ * byte offsets and let the caller calculate them by itself.
+ */
+int ocfs2_get_backup_super_offset(ocfs2_filesys *fs,
+				  uint64_t *offsets, size_t len)
+{
+	size_t i;
+	uint64_t blkno;
+	uint32_t blocksize;
+
+	memset(offsets, 0, sizeof(uint64_t) * len);
+	len = ocfs2_min(len, (size_t)OCFS2_MAX_BACKUP_SUPERBLOCKS);
+
+	if (fs)
+		blocksize = fs->fs_blocksize;
+	else
+		blocksize = 1;
+
+	for (i = 0; i < len; i++) {
+		blkno = ocfs2_backup_super_blkno(blocksize, i);
+		if (fs && fs->fs_blocks <= blkno)
+			break;
+
+		offsets[i] = blkno;
+	}
+	return i;
+}
+
+static errcode_t check_cluster(ocfs2_bitmap *bitmap, uint64_t bit)
+{
+	errcode_t ret;
+	int val;
+
+	ret = ocfs2_bitmap_test(bitmap, bit, &val);
+	if (ret)
+		goto bail;
+
+	if (val) {
+		ret = ENOSPC;
+		goto bail;
+	}
+
+	ret = 0;
+bail:
+	return ret;
+}
+
+errcode_t ocfs2_set_backup_super(ocfs2_filesys *fs,
+				 uint64_t *blocks, size_t len)
+{
+	size_t i;
+	errcode_t ret = 0;
+	char *buf = NULL;
+	uint64_t bm_blk, *blkno = blocks;
+	int val;
+	uint32_t cluster, bpc = fs->fs_clustersize / fs->fs_blocksize;
+
+	if (!len || !blocks || !*blocks)
+		goto bail;
+	len = ocfs2_min(len,(size_t)OCFS2_MAX_BACKUP_SUPERBLOCKS);
+
+	if (!fs->fs_cluster_alloc) {
+		ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE,
+						0, &bm_blk);
+		if (ret)
+			goto bail;
+
+		ret = ocfs2_read_cached_inode(fs, bm_blk, &fs->fs_cluster_alloc);
+		if (ret)
+			goto bail;
+
+		ret = ocfs2_load_chain_allocator(fs, fs->fs_cluster_alloc);
+		if (ret)
+			goto bail;
+	}
+
+	if (!OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+				      OCFS2_FEATURE_COMPAT_BACKUP_SB)) {
+		/* check all the blkno to see whether it is used. */
+		for (i = 0; i < len; i++, blkno++) {
+			ret = check_cluster(fs->fs_cluster_alloc->ci_chains,
+					ocfs2_blocks_to_clusters(fs, *blkno));
+			if (ret)
+				goto bail;
+		}
+	}
+
+	ret = ocfs2_malloc_blocks(fs->fs_io, bpc, &buf);
+	if (ret)
+		goto bail;
+	memset(buf, 0, fs->fs_clustersize);
+
+	/* zero all the clusters at first */
+	blkno = blocks;
+	for (i = 0; i < len; i++, blkno++) {
+		cluster = ocfs2_blocks_to_clusters(fs, *blkno);
+		ret = io_write_block(fs->fs_io, cluster*bpc, bpc, buf);
+		if (ret)
+			goto bail;
+	}
+
+	ret = ocfs2_refresh_backup_super(fs, blocks, len);
+	if (ret)
+		goto bail;
+
+	blkno = blocks;
+	for (i = 0; i < len; i++, blkno++)
+		ocfs2_bitmap_set(fs->fs_cluster_alloc->ci_chains,
+				 ocfs2_blocks_to_clusters(fs, *blkno), &val);
+
+	ret = ocfs2_write_chain_allocator(fs, fs->fs_cluster_alloc);
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	if (fs->fs_cluster_alloc) {
+		ocfs2_free_cached_inode(fs, fs->fs_cluster_alloc);
+		fs->fs_cluster_alloc = NULL;
+	}
+	return ret;
+}
+
+errcode_t ocfs2_refresh_backup_super(ocfs2_filesys *fs,
+				     uint64_t *blocks, size_t len)
+{
+	errcode_t ret = 0;
+	size_t i;
+
+	for (i = 0; i < len; i++, blocks++) {
+		ret = ocfs2_write_backup_super(fs, *blocks);
+		if (ret)
+			goto bail;
+	}
+
+bail:
+	return ret;
+}

Modified: trunk/libocfs2/include/ocfs2.h
===================================================================
--- trunk/libocfs2/include/ocfs2.h	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/libocfs2/include/ocfs2.h	2007-01-19 04:38:22 UTC (rev 1284)
@@ -613,7 +613,30 @@
 errcode_t ocfs2_decode_lockres(char *lockres, int len, enum ocfs2_lock_type *type,
 			       uint64_t *blkno, uint32_t *generation);
 
+/* write the superblock at the specific block. */
+errcode_t ocfs2_write_backup_super(ocfs2_filesys *fs, uint64_t blkno);
 
+/* Get the blkno according to the file system info.
+ * The unused ones, depending on the volume size, are zeroed.
+ * Return the length of the block array.
+ */
+int ocfs2_get_backup_super_offset(ocfs2_filesys *fs,
+				  uint64_t *blocks, size_t len);
+
+/* This function will get the superblock pointed to by fs and copy it to
+ * the blocks. But first it will ensure all the appropriate clusters are free.
+ * If not, it will error out with ENOSPC. If free, it will set bits for all
+ * the clusters, zero the clusters and write the backup sb.
+ * In case of updating, it will override the backup blocks with the newest
+ * superblock information.
+ */
+errcode_t ocfs2_set_backup_super(ocfs2_filesys *fs,
+				 uint64_t *blocks, size_t len);
+
+/* Refresh the backup superblock inoformation. */
+errcode_t ocfs2_refresh_backup_super(ocfs2_filesys *fs,
+				     uint64_t *blocks, size_t len);
+
 /* 
  * ${foo}_to_${bar} is a floor function.  blocks_to_clusters will
  * returns the cluster that contains a block, not the number of clusters
@@ -728,4 +751,6 @@
 	(void) (&_x == &_y);            \
 	_x > _y ? _x : _y; })
 
+/* lifted from the kernel. include/linux/kernel.h */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 #endif  /* _FILESYS_H */

Modified: trunk/libocfs2/include/ocfs2_fs.h
===================================================================
--- trunk/libocfs2/include/ocfs2_fs.h	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/libocfs2/include/ocfs2_fs.h	2007-01-19 04:38:22 UTC (rev 1284)
@@ -85,7 +85,7 @@
 #define OCFS2_CLEAR_INCOMPAT_FEATURE(sb,mask)			\
 	OCFS2_SB(sb)->s_feature_incompat &= ~(mask)
 
-#define OCFS2_FEATURE_COMPAT_SUPP	0
+#define OCFS2_FEATURE_COMPAT_SUPP	OCFS2_FEATURE_COMPAT_BACKUP_SB
 #define OCFS2_FEATURE_INCOMPAT_SUPP	OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT
 #define OCFS2_FEATURE_RO_COMPAT_SUPP	0
 
@@ -110,6 +110,20 @@
 #define OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC	0x0010
 
 /*
+ * backup superblock flag is used to indicate that this volume
+ * has backup superblocks.
+ */
+#define OCFS2_FEATURE_COMPAT_BACKUP_SB		0x0001
+
+/* The byte offset of the first backup block will be 1G.
+ * The following will be 4G, 16G, 64G, 256G and 1T.
+ */
+#define OCFS2_BACKUP_SB_START			1 << 30
+
+/* the max backup superblock nums */
+#define OCFS2_MAX_BACKUP_SUPERBLOCKS	6
+
+/*
  * Flags on ocfs2_dinode.i_flags
  */
 #define OCFS2_VALID_FL		(0x00000001)	/* Inode is valid */
@@ -631,6 +645,19 @@
 
 	return size / sizeof(struct ocfs2_truncate_rec);
 }
+
+static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index)
+{
+	uint64_t offset = OCFS2_BACKUP_SB_START;
+
+	if (index >= 0 && index < OCFS2_MAX_BACKUP_SUPERBLOCKS) {
+		offset <<= (2 * index);
+		offset /= blocksize;
+		return offset;
+	}
+
+	return 0;
+}
 #endif  /* __KERNEL__ */
 
 

Modified: trunk/libocfs2/openfs.c
===================================================================
--- trunk/libocfs2/openfs.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/libocfs2/openfs.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -129,6 +129,42 @@
 	return ret;
 }
 
+errcode_t ocfs2_write_backup_super(ocfs2_filesys *fs, uint64_t blkno)
+{
+	errcode_t ret;
+	char *buf = NULL;
+	struct ocfs2_dinode *di;
+
+	if (!(fs->fs_flags & OCFS2_FLAG_RW))
+		return OCFS2_ET_RO_FILESYS;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto out_blk;
+
+	memcpy(buf, (char *)fs->fs_super, fs->fs_blocksize);
+	di = (struct ocfs2_dinode *)buf;
+
+	ret = OCFS2_ET_BAD_MAGIC;
+	if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE,
+		   strlen(OCFS2_SUPER_BLOCK_SIGNATURE)))
+		goto out_blk;
+
+	di->i_blkno = blkno;
+	OCFS2_SET_COMPAT_FEATURE(OCFS2_RAW_SB(di),
+				 OCFS2_FEATURE_COMPAT_BACKUP_SB);
+	ret = ocfs2_write_inode(fs, blkno, buf);
+	if (ret)
+		goto out_blk;
+
+	ret = 0;
+
+out_blk:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
 int ocfs2_mount_local(ocfs2_filesys *fs)
 {
 	return OCFS2_RAW_SB(fs->fs_super)->s_feature_incompat &

Modified: trunk/mkfs.ocfs2/mkfs.c
===================================================================
--- trunk/mkfs.ocfs2/mkfs.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/mkfs.ocfs2/mkfs.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -84,6 +84,7 @@
 					   uint16_t bpc);
 static void create_lost_found_dir(State *s);
 static void format_journals(State *s);
+static int format_backup_super(State *s);
 
 extern char *optarg;
 extern int optind, opterr, optopt;
@@ -113,6 +114,10 @@
 	{NULL, FS_DEFAULT},
 };
 
+enum {
+	BACKUP_SUPER_OPTION = CHAR_MAX + 1,
+};
+
 static uint64_t align_bytes_to_clusters_ceil(State *s,
 					     uint64_t bytes)
 {
@@ -438,6 +443,15 @@
 	if (!s->quiet)
 		printf("done\n");
 
+	if (!s->no_backup_super) {
+		if (!s->quiet)
+			printf("Writing backup superblock: ");
+
+		num = format_backup_super(s);
+		if (!s->quiet)
+			printf("%d block(s)\n", num);
+	}
+
 	if (!s->hb_dev) {
 		/* These routines use libocfs2 to do their work. We
 		 * don't share an ocfs2_filesys context between the
@@ -510,6 +524,7 @@
 	uint64_t journal_size_in_bytes = 0;
 	enum ocfs2_fs_types fs_type = FS_DEFAULT;
 	int mount = 0;
+	int no_backup_super = 0;
 
 	static struct option long_options[] = {
 		{ "block-size", 1, 0, 'b' },
@@ -523,6 +538,7 @@
 		{ "heartbeat-device", 0, 0, 'H'},
 		{ "force", 0, 0, 'F'},
 		{ "mount", 1, 0, 'M'},
+		{ "no-backup-super", 0, 0, BACKUP_SUPER_OPTION },
 		{ 0, 0, 0, 0}
 	};
 
@@ -653,6 +669,10 @@
 			parse_fs_type_opts(progname, optarg, &fs_type);
 			break;
 
+		case BACKUP_SUPER_OPTION:
+			no_backup_super = 1;
+			break;
+
 		default:
 			usage(progname);
 			break;
@@ -717,6 +737,8 @@
 
 	s->mount = mount;
 
+	s->no_backup_super = no_backup_super;
+
 	return s;
 }
 
@@ -829,7 +851,7 @@
 	fprintf(stderr, "usage: %s [-b block-size] [-C cluster-size] "
 		"[-J journal-options]\n\t\t[-L volume-label] [-M mount-type] "
 		"[-N number-of-node-slots]\n\t\t[-T filesystem-type] [-HFqvV] "
-		"device [blocks-count]\n", progname);
+		"[--no-backup-super] device [blocks-count]\n", progname);
 	exit(0);
 }
 
@@ -2245,3 +2267,42 @@
 	clear_both_ends(s);
 	exit(1);
 }
+
+static int format_backup_super(State *s)
+{
+	errcode_t ret;
+	ocfs2_filesys *fs = NULL;
+	size_t len;
+	uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+
+	ret = ocfs2_open(s->device_name, OCFS2_FLAG_RW, 0, 0, &fs);
+	if (ret) {
+		com_err(s->progname, ret,
+			"while opening file system for backup superblock.");
+		goto error;
+	}
+
+	len = ocfs2_get_backup_super_offset(fs, blocks, ARRAY_SIZE(blocks));
+
+	ret = ocfs2_set_backup_super(fs, blocks, len);
+	if (ret) {
+		com_err(s->progname, ret, "while backing up superblock.");
+		goto error;
+	}
+
+	OCFS2_SET_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+				 OCFS2_FEATURE_COMPAT_BACKUP_SB);
+
+	ret = ocfs2_write_super(fs);
+	if (ret) {
+		com_err(s->progname, ret, "while updating superblock.");
+		goto error;
+	}
+
+	ocfs2_close(fs);
+	return len;
+
+error:
+	clear_both_ends(s);
+	exit(1);
+}

Modified: trunk/mkfs.ocfs2/mkfs.h
===================================================================
--- trunk/mkfs.ocfs2/mkfs.h	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/mkfs.ocfs2/mkfs.h	2007-01-19 04:38:22 UTC (rev 1284)
@@ -203,6 +203,7 @@
 	int prompt;
 	int hb_dev;
 	int mount;
+	int no_backup_super;
 
 	uint32_t blocksize;
 	uint32_t blocksize_bits;

Modified: trunk/tunefs.ocfs2/tunefs.c
===================================================================
--- trunk/tunefs.ocfs2/tunefs.c	2007-01-12 00:59:28 UTC (rev 1283)
+++ trunk/tunefs.ocfs2/tunefs.c	2007-01-19 04:38:22 UTC (rev 1284)
@@ -69,6 +69,10 @@
 #define MOUNT_LOCAL_STR         "local"
 #define MOUNT_CLUSTER_STR       "cluster"
 
+enum {
+	BACKUP_SUPER_OPTION = CHAR_MAX + 1,
+};
+
 typedef struct _ocfs2_tune_opts {
 	uint16_t num_slots;
 	uint64_t num_blocks;
@@ -81,6 +85,7 @@
 	int verbose;
 	int quiet;
 	int prompt;
+	int backup_super;
 	time_t tune_time;
 	int fd;
 } ocfs2_tune_opts;
@@ -253,6 +258,7 @@
 		{ "volume-size", 0, 0, 'S'},
 		{ "uuid-reset", 0, 0, 'U'},
 		{ "mount", 1, 0, 'M' },
+		{ "backup-super", 0, 0, BACKUP_SUPER_OPTION },
 		{ 0, 0, 0, 0}
 	};
 
@@ -264,7 +270,7 @@
 	opts.prompt = 1;
 
 	while (1) {
-		c = getopt_long(argc, argv, "L:N:J:M:SUvqVx", long_options,
+		c = getopt_long(argc, argv, "L:N:J:M:SUvqVxb", long_options,
 				NULL);
 
 		if (c == -1)
@@ -344,12 +350,27 @@
 			opts.prompt = 0;
 			break;
 
+		case BACKUP_SUPER_OPTION:
+			opts.backup_super = 1;
+			break;
+
 		default:
 			usage(opts.progname);
 			break;
 		}
 	}
 
+	/* we don't allow backup_super to be coexist with other tunefs
+	 * options to keep things simple.
+	 */
+	if (opts.backup_super &&
+	    (opts.vol_label || opts.num_slots ||
+	     opts.mount || opts.jrnl_size || resize)) {
+		com_err(opts.progname, 0, "Cannot backup superblock"
+			" along with other tasks");
+		exit(1);
+	}
+
 	if (!opts.quiet || show_version)
 		version(opts.progname);
 
@@ -784,6 +805,82 @@
 	return ret;
 }
 
+static errcode_t load_chain_allocator(ocfs2_filesys *fs,
+					     ocfs2_cached_inode** inode)
+{
+	errcode_t ret;
+	uint64_t blkno;
+
+	ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE,
+					0, &blkno);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_read_cached_inode(fs, blkno, inode);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_load_chain_allocator(fs, *inode);
+
+bail:
+	return ret;
+}
+
+static errcode_t backup_super_check(ocfs2_filesys *fs)
+{
+	errcode_t ret;
+	int i, num, val, failed = 0;
+	ocfs2_cached_inode *chain_alloc = NULL;
+	uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+
+	/* if the compat flag is set, just return. */
+	if (OCFS2_HAS_COMPAT_FEATURE(super, OCFS2_FEATURE_COMPAT_BACKUP_SB)) {
+		com_err(opts.progname, 0,
+			"Volume has been enabled for Backup superblock");
+		return -1;
+	}
+
+	num = ocfs2_get_backup_super_offset(fs, blocks, ARRAY_SIZE(blocks));
+	if (!num) {
+		com_err(opts.progname, 0,
+			"Volume is too small to hold backup superblocks");
+		return -1;
+	}
+
+	ret = load_chain_allocator(fs, &chain_alloc);
+	if (ret)
+		goto bail;
+
+	for (i = 0; i < num; i++) {
+		ret = ocfs2_bitmap_test(chain_alloc->ci_chains,
+					ocfs2_blocks_to_clusters(fs, blocks[i]),
+					&val);
+		if (ret)
+			goto bail;
+
+		if (val) {
+			com_err(opts.progname, 0, "block %"PRIu64
+				" is in use.", blocks[i]);
+			/* in order to verify all the block in the 'blocks',
+			 * we don't stop the loop here.
+			 */
+			failed = 1;
+		}
+	}
+
+	if (failed) {
+		ret = ENOSPC;
+		com_err(opts.progname, 0, "Cannot enable backup superblock as "
+			"backup blocks are in use");
+	}
+
+	if (chain_alloc)
+		ocfs2_free_cached_inode(fs, chain_alloc);
+bail:
+	return ret;
+}
+
 static void update_volume_label(ocfs2_filesys *fs, int *changed)
 {
   	memset (OCFS2_RAW_SB(fs->fs_super)->s_label, 0,
@@ -1141,6 +1238,56 @@
 	return ret;
 }
 
+static errcode_t refresh_backup_super(ocfs2_filesys *fs)
+{
+	errcode_t ret;
+	int num;
+	uint64_t blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+
+	num = ocfs2_get_backup_super_offset(fs, blocks, ARRAY_SIZE(blocks));
+	if (!num)
+		return 0;
+
+	ret = ocfs2_refresh_backup_super(fs, blocks, num);
+
+	return ret;
+}
+
+static errcode_t update_backup_super(ocfs2_filesys *fs, uint64_t newblocks)
+{
+	errcode_t ret;
+	int num, i;
+	uint64_t *new_backup_super, blocks[OCFS2_MAX_BACKUP_SUPERBLOCKS];
+	uint64_t startblk = fs->fs_blocks - newblocks;
+
+	num = ocfs2_get_backup_super_offset(fs, blocks, ARRAY_SIZE(blocks));
+	if (!num)
+		return 0;
+
+	if (newblocks) {
+		for (i = 0; i < num; i++) {
+			if (blocks[i] >= startblk)
+				break;
+		}
+
+		if (!(num - i))
+			return 0;
+
+		new_backup_super = &blocks[i];
+		num -= i;
+	} else
+		new_backup_super = blocks;
+
+	ret = ocfs2_set_backup_super(fs, new_backup_super, num);
+	if (ret) {
+		com_err(opts.progname, ret, "while backing up superblock.");
+		goto bail;
+	}
+
+bail:
+	return ret;
+}
+
 int main(int argc, char **argv)
 {
 	errcode_t ret = 0;
@@ -1153,6 +1300,7 @@
 	int upd_blocks = 0;
 	int upd_mount = 0;
 	int upd_incompat = 0;
+	int upd_backup_super = 0;
 	char *tmpstr;
 	uint16_t tmp;
 	uint64_t def_jrnl_size = 0;
@@ -1230,7 +1378,8 @@
 
 	/* If operation requires touching the global bitmap, ensure it is good */
 	/* This is to handle failed resize */
-	if (opts.num_blocks || opts.num_slots || opts.jrnl_size) {
+	if (opts.num_blocks || opts.num_slots || opts.jrnl_size ||
+	    opts.backup_super) {
 		if (global_bitmap_check(fs)) {
 			com_err(opts.progname, 0, "Global bitmap check failed. "
 				"Run fsck.ocfs2 -f <device>.");
@@ -1238,6 +1387,14 @@
 		}
 	}
 
+	/* check whether the block for backup superblock are used. */
+	if (opts.backup_super) {
+		if (backup_super_check(fs))
+			goto unlock;
+		else
+			printf("Adding backup superblock for the volume\n");
+	}
+
 	/* validate volume label */
 	if (opts.vol_label) {
 		printf("Changing volume label from %s to %s\n",
@@ -1312,7 +1469,8 @@
 	}
 
 	if (!opts.vol_label && !opts.vol_uuid && !opts.num_slots &&
-	    !opts.jrnl_size && !opts.num_blocks && !opts.mount) {
+	    !opts.jrnl_size && !opts.num_blocks && !opts.mount &&
+	    !opts.backup_super) {
 		com_err(opts.progname, 0, "Nothing to do. Exiting.");
 		goto unlock;
 	}
@@ -1399,9 +1557,31 @@
 			printf("Resized volume\n");
 	}
 
+	/* update the backup superblock. */
+	if (opts.backup_super ||
+	    (opts.num_blocks &&
+	    OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+				     OCFS2_FEATURE_COMPAT_BACKUP_SB))) {
+		block_signals(SIG_BLOCK);
+		ret = update_backup_super(fs, opts.num_blocks);
+		block_signals(SIG_UNBLOCK);
+		if (ret) {
+			com_err(opts.progname, ret,
+				"while backuping superblock");
+			goto unlock;
+		}
+		OCFS2_SET_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+					 OCFS2_FEATURE_COMPAT_BACKUP_SB);
+
+		if (opts.backup_super) {
+			printf("Backed up Superblock.\n");
+			upd_backup_super = 1;
+		}
+	}
+
 	/* write superblock */
 	if (upd_label || upd_uuid || upd_slots || upd_blocks || upd_incompat ||
-	    upd_mount) {
+	    upd_mount || upd_backup_super) {
 		block_signals(SIG_BLOCK);
 		ret = ocfs2_write_super(fs);
 		if (ret) {
@@ -1410,6 +1590,32 @@
 		}
 		block_signals(SIG_UNBLOCK);
 		printf("Wrote Superblock\n");
+
+		/* superblock's information has changed.
+		 * We need to synchronize the backup blocks if needed.
+		 * We also have to admit that if upd_backup_super is set,
+		 * there is no need to refresh the backups since they are
+		 * written above by update_backup_super.
+		 */
+		if (!upd_backup_super &&
+		    OCFS2_HAS_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+					     OCFS2_FEATURE_COMPAT_BACKUP_SB)) {
+			block_signals(SIG_BLOCK);
+			ret = refresh_backup_super(fs);
+			block_signals(SIG_UNBLOCK);
+			if (ret) {
+				printf("Unable to refresh backup superblocks. "
+					"Please run fsck.ocfs2 before running "
+					"tunefs.ocfs2 to re-enable "
+					"backup superblocks.");
+				OCFS2_CLEAR_COMPAT_FEATURE(
+					OCFS2_RAW_SB(fs->fs_super),
+					OCFS2_FEATURE_COMPAT_BACKUP_SB);
+				block_signals(SIG_BLOCK);
+				ocfs2_write_super(fs);
+				block_signals(SIG_UNBLOCK);
+			}
+		}
 	}
 
 unlock:




More information about the Ocfs2-tools-commits mailing list