Re: Segment magic number invalid

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,
On Fri, 14 May 2010 20:24:02 -0400, Paul L wrote:
> I have my home directory mounted as a nilfs2 partition. Today what
> happened was that first I noticed google-chrome reporting it cannot
> load user profile, I initially thought it was a google-chrome error.
> At the time I was still able to view and modify my home directory. But
> then after rebooting the system, my home partition no longer mounts.
> I'm using nilfs-2.0.19 and nilfs-utils-2.0.18 with Linux kernel
> 2.6.28.
> 
> Here is the error message from dmesg (after turning on debugging
> message for nilfs2):
> 
> NILFS nilfs_fill_super: start(silent=0)
> NILFS(recovery) nilfs_search_super_root: looking segment
> (seg_start=1607680, seg_end=1609727, segnum=785, seg_seq=307637)
> NILFS(recovery) load_segment_summary: checking segment
> (pseg_start=1608334, full_check=0)
> NILFS(recovery) load_segment_summary: done (ret=3)
> NILFS(recovery) nilfs_search_super_root: strayed: scan_newer=0, ret=3
> NILFS warning: Segment magic number invalid
> NILFS: error searching super root.
> NILFS nilfs_fill_super: aborted
> NILFS put_nilfs: the_nilfs on bdev mmcblk0p1 was freed
> 
> I then dumped the first and last (backup) copy of the nilfs2 super
> block, they are identical, and given below:
> 
> 00000400   02 00 00 00 00 00 34 34  00 01 00 00 A1 6A E9 71  ......44.....j.q
> 00000410   A3 F1 DD BE 02 00 00 00  AF 07 00 00 00 00 00 00  ................
> 00000420   00 E0 BF D7 03 00 00 00  01 00 00 00 00 00 00 00  ................
> 00000430   00 08 00 00 05 00 00 00  7C 3D 0A 00 00 00 00 00  ........|=......
> 00000440   8E 8A 18 00 00 00 00 00  B5 B1 04 00 00 00 00 00  ................
> 00000450   00 B8 23 00 00 00 00 00  B9 AF F3 4A 00 00 00 00  ..#........J....
> 00000460   D9 E1 D6 4B 00 00 00 00  49 8F ED 4B 00 00 00 00  ...K....I..K....
> 00000470   37 00 32 00 03 00 01 00  B9 AF F3 4A 00 00 00 00  7.2........J....elp
> 00000480   00 4E ED 00 00 00 00 00  00 00 00 00 0B 00 00 00  .N..............
> 00000490   80 00 20 00 C0 00 10 00  13 1C FC 11 D7 43 4C 09  .. ..........CL.
> 000004A0   81 64 93 0A F4 54 CF 5E  48 4F 4D 45 00 00 00 00  .d...T.^HOME....
> 
> 
> I wonder if there is a fsck tool to help me recover the file system.
> Any help is greatly appreciated!
>
> PS: last time I had a different problem of losing partition info, and
> later successfully recovered with the help from people on the list. So
> thanks! Now I'm actually backing up my files every two weeks, but
> it'll still be great if it can recover and even better if we can trace
> the problem.

Your filesystem seems to have lost the latest log according to the
report.

The attached patch may help to recover it.  It is revised scan tool
for nilfs-utils-2.0.18.

After compiling the tool, you can use it like:

 # cd nilfs-utils-2.0.18
 # sbin/fsck/fsck0 <device>

The tool will confirm whether to update super blocks if it finds the
latest log.

You may need to do

 $ aclocal && autoheader && libtoolize -c --foce && automake -a -c && autoconf
 $ ./configure

before build the tool.

With regards,
Ryusuke Konishi
>From b1a403ed4a7cb6987052729075255d5d893bebdb Mon Sep 17 00:00:00 2001
From: Ryusuke Konishi <konishi.ryusuke@xxxxxxxxxxxxx>
Date: Sat, 15 May 2010 12:33:27 +0900
Subject: [PATCH] nilfs2-utils: add test tool to correct log pointer in super block

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@xxxxxxxxxxxxx>
---
 configure.ac             |    1 +
 sbin/Makefile.am         |    2 +-
 sbin/fsck/Makefile.am    |    7 +
 sbin/fsck/fsck0.nilfs2.c | 1150 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1159 insertions(+), 1 deletions(-)
 create mode 100644 sbin/fsck/Makefile.am
 create mode 100644 sbin/fsck/fsck0.nilfs2.c

diff --git a/configure.ac b/configure.ac
index 2b2dfd7..30e91eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -86,6 +86,7 @@ AC_CONFIG_FILES([Makefile
                  man/Makefile
                  sbin/Makefile
                  sbin/cleanerd/Makefile
+		 sbin/fsck/Makefile
                  sbin/mkfs/Makefile
 		 sbin/mount/Makefile])
 AC_OUTPUT
diff --git a/sbin/Makefile.am b/sbin/Makefile.am
index 6038b8f..7f2c6db 100644
--- a/sbin/Makefile.am
+++ b/sbin/Makefile.am
@@ -1,3 +1,3 @@
 ## Makefile.am
 
-SUBDIRS = cleanerd mkfs mount
+SUBDIRS = cleanerd mkfs mount fsck
diff --git a/sbin/fsck/Makefile.am b/sbin/fsck/Makefile.am
new file mode 100644
index 0000000..4a2f380
--- /dev/null
+++ b/sbin/fsck/Makefile.am
@@ -0,0 +1,7 @@
+## Makefile.am
+
+sbin_PROGRAMS = fsck0.nilfs2
+
+fsck0_nilfs2_SOURCES = fsck0.nilfs2.c ../../lib/crc32.c ../mkfs/mkfs.h
+fsck0_nilfs2_CFLAGS = -Wall
+fsck0_nilfs2_CPPFLAGS = -I$(top_srcdir)/include
diff --git a/sbin/fsck/fsck0.nilfs2.c b/sbin/fsck/fsck0.nilfs2.c
new file mode 100644
index 0000000..2d3a435
--- /dev/null
+++ b/sbin/fsck/fsck0.nilfs2.c
@@ -0,0 +1,1150 @@
+/*
+ * fsck0.nilfs2.c - correct inconsistencies of nilfs2 volume
+ *
+ * Licensed under GPLv2: the complete text of the GNU General Public License
+ * can be found in COPYING file of the nilfs-utils package.
+ *
+ * Copyright (C) 2008-2010 Nippon Telegraph and Telephone Corporation.
+ * Written by Ryusuke Konishi <ryusuke@xxxxxxxx>
+ */
+#define _LARGEFILE64_SOURCE
+#define _XOPEN_SOURCE 600
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif	/* HAVE_CONFIG_H */
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif	/* HAVE_SYS_TYPES_H */
+
+#if HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif	/* HAVE_LINUX_TYPES_H */
+
+#include <stdio.h>
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif	/* HAVE_STDLIB_H */
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif	/* HAVE_FCNTL_H */
+
+#include <errno.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif	/* HAVE_UNISTD_H */
+
+#include <malloc.h>
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif	/* HAVE_SYS_IOCTL_H */
+
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif	/* HAVE_SYS_STRINGS_H */
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif	/* HAVE_SYS_STRING_H */
+
+#include <endian.h>
+#include <byteswap.h>
+
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+
+#include "../mkfs/mkfs.h"
+
+#define MOUNTS			"/etc/mtab"
+#define LINE_BUFFER_SIZE	256  /* Line buffer size for reading mtab */
+#define MAX_SCAN_SEGMENT	50   /* Maximum number of segments which are
+					tested for the latest segment search */
+#define SCAN_INDICATOR_SPEED	3    /* Indicator speed (smaller value for
+					higher speed) */
+#define SCAN_SEGMENT_MASK	((1U << SCAN_INDICATOR_SPEED) - 1)
+
+#define NILFS_MAX_SB_SIZE	1024 /* Maximum size of super block in bytes */
+#define NILFS_SB_BLOCK_SIZE_SHIFT	10
+
+/* fsck return codes */
+#define EXIT_OK			0
+#define EXIT_NONDESTRUCT	1
+#define EXIT_DESTRUCT		2
+#define EXIT_UNCORRECTED	4
+#define EXIT_ERROR		8
+#define EXIT_USAGE		16
+#define EXIT_CANCEL		32
+#define EXIT_LIBRARY		128
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+char *progname = NULL;
+
+struct nilfs_log_ref {
+	__u64 blocknr;   /* start blocknumber */
+	__u64 seqnum;    /* sequence number */
+	__u64 cno;       /* checkpoint number */
+	__u64 ctime;     /* creation time */
+};
+
+static int show_version_only = 0;
+static int force = 0;
+static int verbose = 0;
+
+static int devfd = -1;
+static int blocksize;
+static __u32 crc_seed;
+static __u32 blocks_per_segment;
+static __u64 first_data_block;
+static __u64 nsegments;
+static __u16 checkpoint_size;
+static __u16 sb_bytes;
+
+static int first_checkpoint_offset;
+static int ncheckpoints_per_block;
+
+/*
+ * Generic routines
+ */
+void die(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	fprintf(stderr, "%s: ", progname);
+	vfprintf(stderr, fmt, args);
+	fprintf(stderr, "\n");
+	va_end(args);
+
+	if (devfd >= 0)
+		close(devfd);
+	exit(EXIT_ERROR);
+}
+
+static void (*nilfs_shrink)(void) = NULL;
+
+void *nilfs_malloc(size_t size)
+{
+	void *p = malloc(size);
+	if (!p) {
+		if (nilfs_shrink)
+			nilfs_shrink();
+		p = malloc(size);
+		if (!p)
+			die("memory allocation failure");
+	}
+	return p;
+}
+
+static inline void *nilfs_zalloc(size_t size)
+{
+	void *p = nilfs_malloc(size);
+	memset(p, 0, size);
+	return p;
+}
+
+/*
+ * Block buffer
+ */
+static void *block_buffer = NULL;
+
+static void destroy_block_buffer(void)
+{
+	if (block_buffer) {
+		free(block_buffer);
+		block_buffer = NULL;
+	}
+}
+
+static void init_block_buffer(void)
+{
+	block_buffer = nilfs_malloc(blocksize);
+	atexit(destroy_block_buffer);
+}
+
+static void read_block(int fd, __u64 blocknr, void *buf,
+		       unsigned long size)
+{
+	if (lseek64(fd, blocknr * blocksize, SEEK_SET) < 0 ||
+	    read(fd, buf, size) < size)
+		die("cannot read block (blocknr = %llu)",
+		    (unsigned long long)blocknr);
+}
+
+static inline __u64 segment_start_blocknr(unsigned long segnum)
+{
+	return segnum > 0 ? blocks_per_segment * segnum : first_data_block;
+}
+
+static int log_is_valid(int fd, __u64 log_start,
+			struct nilfs_segment_summary *ss)
+{
+	__u32 crc, sum;
+	int offset = sizeof(ss->ss_datasum);
+	int nblocks = le32_to_cpu(ss->ss_nblocks);
+	__u64 blocknr = log_start;
+
+	if (le32_to_cpu(ss->ss_magic) != NILFS_SEGSUM_MAGIC)
+		return 0;
+
+	if (nblocks == 0 || nblocks > blocks_per_segment)
+		return 0;
+
+	sum = le32_to_cpu(ss->ss_datasum);
+
+	read_block(fd, blocknr++, block_buffer, blocksize);
+	crc = nilfs_crc32(crc_seed, block_buffer + offset, blocksize - offset);
+	while (--nblocks > 0) {
+		read_block(fd, blocknr++, block_buffer, blocksize);
+		crc = nilfs_crc32(crc, block_buffer, blocksize);
+	}
+	return crc == sum;
+}
+
+/*
+ * Routines to handle log (partial segment) list
+ */
+struct nilfs_list {  /* use struct list_head in kernel land */
+	struct nilfs_list *prev;
+	struct nilfs_list *next;
+};
+
+static inline void nilfs_list_init(struct nilfs_list *list)
+{
+	list->prev = list->next = list;
+}
+
+static inline int nilfs_list_empty(struct nilfs_list *list)
+{
+	return list->next == list;
+}
+
+static inline void nilfs_list_del(struct nilfs_list *list)
+{
+	struct nilfs_list *p = list->prev, *n = list->next;
+
+	p->next = n;
+	n->prev = p;
+	list->prev = list->next = list;
+}
+
+static inline void nilfs_list_add(struct nilfs_list *list,
+				  struct nilfs_list *item)
+{
+	struct nilfs_list *p = list->prev;
+
+	item->prev = p;
+	item->next = list;
+	p->next = list->prev = item;
+}
+
+/* log (partial segment) information */
+struct nilfs_log_info {
+	struct nilfs_list list;
+	__u64 log_start;  /* start blocknr */
+	__u32 nblocks;
+	struct nilfs_segment_summary segsum;  /* on-disk log header */
+	__u16 flags;
+};
+
+static inline struct nilfs_log_info *nilfs_log_list_entry(struct nilfs_list *p)
+{
+	return (void *)p - offsetof(struct nilfs_log_info, list);
+}
+
+struct nilfs_log_info *new_log_info(__u64 blocknr)
+{
+	struct nilfs_log_info *loginfo = nilfs_zalloc(sizeof(*loginfo));
+
+	loginfo->log_start = blocknr;
+	nilfs_list_init(&loginfo->list);
+	return loginfo;
+}
+
+static void dispose_log_list(struct nilfs_list *list)
+{
+	struct nilfs_list *p, *n;
+
+	for (p = list->next; n = p->next, p != list; p = n) {
+		nilfs_list_del(p);
+		free(nilfs_log_list_entry(p));
+	}
+}
+
+/*
+ * Segment information
+ */
+struct nilfs_segment_info {
+	struct nilfs_list list;
+	struct nilfs_list log_list;	/* partial segment list */
+	__u64 seg_start;		/* start blocknr of the segment */
+	__u64 next;			/* pointer to the next segment */
+	__u64 segseq;			/* sequence number of the segment */
+	unsigned long segnum;		/* the number of the segment */
+	int nlogs;			/* number of logs */
+	int refcnt;
+};
+
+static struct nilfs_list segment_cache;
+
+static struct nilfs_segment_info *
+nilfs_segment_list_entry(struct nilfs_list *p)
+{
+	return (void *)p - offsetof(struct nilfs_segment_info, list);
+}
+
+struct nilfs_segment_info *new_segment_info(unsigned long segnum)
+{
+	struct nilfs_segment_info *seginfo;
+
+	seginfo = nilfs_zalloc(sizeof(*seginfo));
+	seginfo->segnum = segnum;
+	seginfo->seg_start = segment_start_blocknr(segnum);
+	seginfo->refcnt = 1;
+
+	nilfs_list_init(&seginfo->log_list);
+	nilfs_list_add(&segment_cache, &seginfo->list);
+	return seginfo;
+}
+
+void destroy_segment_info(struct nilfs_segment_info *seginfo)
+{
+	nilfs_list_del(&seginfo->list);
+	dispose_log_list(&seginfo->log_list);
+	free(seginfo);
+}
+
+static inline struct nilfs_segment_info *
+get_segment_info(struct nilfs_segment_info *seginfo)
+{
+	seginfo->refcnt++;
+	return seginfo;
+}
+
+static inline void put_segment_info(struct nilfs_segment_info *seginfo)
+{
+	assert(seginfo->refcnt > 0);
+	seginfo->refcnt--;
+}
+
+/*
+ * Segment cache
+ */
+void destroy_segment_cache(void)
+{
+	struct nilfs_list *p, *n;
+
+	for (p = segment_cache.next; n = p->next, p != &segment_cache; p = n) {
+		destroy_segment_info(nilfs_segment_list_entry(p));
+	}
+}
+
+void shrink_segment_cache(void)
+{
+	struct nilfs_list *p, *n;
+	struct nilfs_segment_info *seginfo;
+
+	for (p = segment_cache.next; n = p->next, p != &segment_cache; p = n) {
+		seginfo = nilfs_segment_list_entry(p);
+		if (seginfo->refcnt == 0)
+			destroy_segment_info(seginfo);
+	}
+}
+
+void init_segment_cache(void)
+{
+	nilfs_list_init(&segment_cache);
+	nilfs_shrink = shrink_segment_cache;
+	atexit(destroy_segment_cache);
+}
+
+struct nilfs_segment_info *lookup_segment(unsigned long segnum)
+{
+	struct nilfs_segment_info *seginfo;
+	struct nilfs_list *p;
+
+	for (p = segment_cache.next; p != &segment_cache; p = p->next) {
+		seginfo = nilfs_segment_list_entry(p);
+		if (seginfo->segnum == segnum) {
+			get_segment_info(seginfo);
+			return seginfo;
+		}
+	}
+	return NULL;
+}
+
+struct nilfs_segment_info *load_segment(int fd, unsigned long segnum)
+{
+	struct nilfs_segment_info *seginfo;
+	struct nilfs_log_info *loginfo;
+	struct nilfs_segment_summary *ss;
+	__u64 blocknr, end;
+
+	seginfo = lookup_segment(segnum);
+	if (seginfo)
+		return seginfo;
+
+	seginfo = new_segment_info(segnum);
+	blocknr = seginfo->seg_start;
+
+	posix_fadvise(fd, blocknr * blocksize, blocks_per_segment * blocksize,
+		      POSIX_FADV_WILLNEED);
+
+	loginfo = new_log_info(blocknr);
+	nilfs_list_add(&seginfo->log_list, &loginfo->list);
+
+	ss = &loginfo->segsum;
+	read_block(fd, blocknr, ss, sizeof(*ss));
+
+	if (!log_is_valid(fd, blocknr, ss)) {
+		put_segment_info(seginfo);
+		fprintf(stderr, "empty or bad segment: "
+			"segnum = %lu, blocknr = %llu\n", segnum,
+			(unsigned long long)segment_start_blocknr(segnum));
+		return NULL; /* no valid partial segment found */
+	}
+
+	seginfo->segseq = le64_to_cpu(ss->ss_seq);
+	seginfo->next = le64_to_cpu(ss->ss_next);
+
+	end = blocknr + blocks_per_segment;
+	do {
+		seginfo->nlogs++;
+
+		loginfo->nblocks = le32_to_cpu(ss->ss_nblocks);
+		loginfo->flags = le16_to_cpu(ss->ss_flags);
+
+		blocknr += loginfo->nblocks;
+		if (blocknr >= end)
+			return seginfo;
+
+		loginfo = new_log_info(blocknr);
+		nilfs_list_add(&seginfo->log_list, &loginfo->list);
+
+		ss = &loginfo->segsum;
+		read_block(fd, blocknr, ss, sizeof(*ss));
+
+	} while (log_is_valid(fd, blocknr, ss) &&
+		 le64_to_cpu(ss->ss_seq) == seginfo->segseq);
+
+	nilfs_list_del(&loginfo->list);
+	free(loginfo);
+
+	return seginfo;
+}
+
+/*
+ * Operations on segment_info structure
+ */
+struct nilfs_log_info *lookup_log(struct nilfs_segment_info *seginfo,
+				  __u64 blocknr)
+{
+	struct nilfs_log_info *loginfo;
+	struct nilfs_list *p;
+
+	for (p = seginfo->log_list.next; p != &seginfo->log_list;
+	     p = p->next) {
+		loginfo = nilfs_log_list_entry(p);
+		if (loginfo->log_start == blocknr)
+			return loginfo;
+	}
+	return NULL;
+}
+
+struct nilfs_log_info *first_log(struct nilfs_segment_info *seginfo)
+{
+	return nilfs_list_empty(&seginfo->log_list) ? NULL :
+		nilfs_log_list_entry(seginfo->log_list.next);
+}
+
+struct nilfs_log_info *last_log(struct nilfs_segment_info *seginfo)
+{
+	return nilfs_list_empty(&seginfo->log_list) ? NULL :
+		nilfs_log_list_entry(seginfo->log_list.prev);
+}
+
+struct nilfs_log_info *next_log(struct nilfs_segment_info *seginfo,
+				struct nilfs_log_info *loginfo)
+{
+	return loginfo->list.next == &seginfo->log_list ? NULL :
+		nilfs_log_list_entry(loginfo->list.next);
+}
+
+struct nilfs_log_info *prev_log(struct nilfs_segment_info *seginfo,
+				struct nilfs_log_info *loginfo)
+{
+	return loginfo->list.prev == &seginfo->log_list ? NULL :
+		nilfs_log_list_entry(loginfo->list.prev);
+}
+
+struct nilfs_log_info *
+lookup_last_super_root(struct nilfs_segment_info *seginfo)
+{
+	struct nilfs_log_info *loginfo;
+
+	for (loginfo = last_log(seginfo); loginfo != NULL;
+	     loginfo = prev_log(seginfo, loginfo)) {
+		if (loginfo->flags & NILFS_SS_SR)
+			return loginfo;
+	}
+	return NULL;
+}
+
+unsigned long log_length(struct nilfs_segment_info *seginfo)
+{
+	return nilfs_list_empty(&seginfo->log_list) ? 0 :
+		nilfs_log_list_entry(seginfo->log_list.prev)->log_start -
+		seginfo->seg_start +
+		nilfs_log_list_entry(seginfo->log_list.prev)->nblocks;
+}
+
+/*
+ * Routines to get latest checkpoint number
+ */
+static __u64 find_latest_checkpoint(int fd, __u64 cpblocknr, __u64 blkoff)
+{
+	struct nilfs_checkpoint *cp;
+	int i, ncp;
+	__u64 cno = 0;
+
+	read_block(fd, cpblocknr, block_buffer, blocksize);
+	if (blkoff == 0) {
+		cp = block_buffer + first_checkpoint_offset * checkpoint_size;
+		ncp = ncheckpoints_per_block - first_checkpoint_offset;
+	} else {
+		cp = block_buffer;
+		ncp = ncheckpoints_per_block;
+	}
+
+	for (i = 0; i < ncp; i++, cp = (void *)cp + checkpoint_size) {
+		if (!nilfs_checkpoint_invalid(cp) &&
+		    le64_to_cpu(cp->cp_cno) > cno)
+			cno = le64_to_cpu(cp->cp_cno);
+	}
+	return cno;
+}
+
+static void *next_ss_entry(int fd, __u64 *blocknrp,
+			   unsigned *offsetp, unsigned entry_size)
+{
+	void *p;
+
+	if (*offsetp + entry_size > blocksize) {
+		(*blocknrp)++;
+		read_block(fd, *blocknrp, block_buffer, blocksize);
+		*offsetp = 0;
+	}
+	p = block_buffer + *offsetp;
+	(*offsetp) += entry_size;
+	return p;
+}
+
+static __u64 get_latest_cno(int fd, __u64 log_start)
+{
+	struct nilfs_segment_summary *ss;
+	struct nilfs_finfo *finfo;
+	__u32 nfinfo;
+	__u32 nblocks, ndatablk, nnodeblk;
+	__u64 ino;
+	__u64 latest_cno = 0, cno;
+	__u64 blocknr = log_start, fblocknr;
+	unsigned offset;
+	int i, j;
+
+	read_block(fd, blocknr, block_buffer, blocksize);
+	ss = block_buffer;
+	nfinfo = le32_to_cpu(ss->ss_nfinfo);
+	offset = le16_to_cpu(ss->ss_bytes);
+	fblocknr = blocknr + DIV_ROUND_UP(le32_to_cpu(ss->ss_sumbytes),
+					  blocksize);
+
+	for (i = 0; i < nfinfo; i++) {
+		finfo = next_ss_entry(fd, &blocknr, &offset, sizeof(*finfo));
+
+		nblocks = le32_to_cpu(finfo->fi_nblocks);
+		ndatablk = le32_to_cpu(finfo->fi_ndatablk);
+		nnodeblk = nblocks - ndatablk;
+		ino = le64_to_cpu(finfo->fi_ino);
+
+		if (ino == NILFS_DAT_INO) {
+			__le64 *blkoff;
+			struct nilfs_binfo_dat *binfo_dat;
+
+			for (j = 0; j < ndatablk; j++, fblocknr++) {
+				blkoff = next_ss_entry(fd, &blocknr,
+						       &offset,
+						       sizeof(*blkoff));
+			}
+			for (j = 0; j < nnodeblk; j++, fblocknr++) {
+				binfo_dat = next_ss_entry(fd, &blocknr,
+							  &offset,
+							  sizeof(*binfo_dat));
+			}
+		} else {
+			struct nilfs_binfo_v *binfo_v;
+			__le64 *vblocknr;
+
+			for (j = 0; j < ndatablk; j++, fblocknr++) {
+				binfo_v = next_ss_entry(fd, &blocknr,
+							&offset,
+							sizeof(*binfo_v));
+			}
+			if (ino == NILFS_CPFILE_INO && ndatablk > 0) {
+				cno = find_latest_checkpoint(
+					fd, fblocknr - 1,
+					le64_to_cpu(binfo_v->bi_blkoff));
+				if (cno > latest_cno)
+					latest_cno = cno;
+			}
+			for (j = 0; j < nnodeblk; j++, fblocknr++) {
+				vblocknr = next_ss_entry(fd, &blocknr,
+							 &offset,
+							 sizeof(*vblocknr));
+			}
+		}
+	}
+
+	return latest_cno;
+}
+
+__u64 find_latest_cno_in_logical_segment(int fd,
+					 struct nilfs_segment_info *seginfo,
+					 struct nilfs_log_info *start)
+{
+	struct nilfs_log_info *loginfo = start ? : last_log(seginfo);
+	__u64 cno, latest_cno = 0;
+	__u64 seq;
+	int i = 0;
+
+	if (loginfo == NULL)
+		return 0;
+
+	get_segment_info(seginfo);
+	do {
+		cno = get_latest_cno(fd, loginfo->log_start);
+		if (cno > latest_cno)
+			latest_cno = cno;
+
+		if (loginfo->flags & NILFS_SS_LOGBGN)
+			break;
+
+		loginfo = prev_log(seginfo, loginfo);
+		if (loginfo == NULL) {
+			unsigned long segnum = seginfo->segnum;
+
+			if (++i > MAX_SCAN_SEGMENT)
+				break;
+			segnum = (segnum == 0) ? nsegments - 1 : segnum - 1;
+			seq = seginfo->segseq;
+
+			put_segment_info(seginfo);
+			seginfo = load_segment(fd, segnum);
+
+			if (!seginfo || seginfo->segseq != seq - 1)
+				break;
+			loginfo = last_log(seginfo);
+		}
+	} while (loginfo != NULL && !(loginfo->flags & NILFS_SS_LOGEND));
+
+	if (seginfo)
+		put_segment_info(seginfo);
+	return latest_cno;
+}
+
+void print_log_message(const struct nilfs_log_ref *log_ref,
+		       const char *fmt, ...)
+{
+	const char *cp;
+	va_list args;
+
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	fprintf(stderr, ": blocknr = %llu\n",
+		(unsigned long long)log_ref->blocknr);
+
+	for (cp = fmt; *cp == ' '; cp++)
+		fputc(' ', stderr);
+	fprintf(stderr, "    segnum = %lu, seq = %llu, cno=%llu\n",
+		(unsigned long)log_ref->blocknr / blocks_per_segment,
+		(unsigned long long)log_ref->seqnum,
+		(unsigned long long)log_ref->cno);
+	if (log_ref->ctime) {
+		char tmbuf[LINE_BUFFER_SIZE];
+		struct tm tm;
+		time_t t = (time_t)le64_to_cpu(log_ref->ctime);
+
+		localtime_r(&t, &tm);
+		strftime(tmbuf, LINE_BUFFER_SIZE, "%F %T", &tm);
+		for (cp = fmt; *cp == ' '; cp++)
+			fputc(' ', stderr);
+		fprintf(stderr, "    creation time = %s\n", tmbuf);
+	}
+	va_end(args);
+}
+
+struct nilfs_log_info *
+find_latest_super_root(int fd, unsigned long segnum, __u64 blocknr,
+		       struct nilfs_segment_info **seginfop)
+{
+	struct nilfs_segment_info *seginfo;
+	struct nilfs_segment_info *seginfo_sr = NULL;
+		/* seginfo which has the last super root */
+	struct nilfs_log_info *log_sr = NULL;
+	int cont = 0, invert = 0;
+	int i;
+
+	seginfo = load_segment(fd, segnum);
+	if (seginfo) {
+		log_sr = lookup_last_super_root(seginfo);
+		if (log_sr)
+			seginfo_sr = get_segment_info(seginfo);
+
+		if (blocknr < seginfo->seg_start + log_length(seginfo))
+			cont = 1;
+	}
+
+	for (i = 0; i < MAX_SCAN_SEGMENT; i++) {
+		struct nilfs_segment_info *seginfo2;
+
+		/*
+		 * Look into the previous segment.
+		 *
+		 * This code depends on the current GC policy; discontinuously
+		 * allocated segments are not supported.
+		 */
+		if (!(i & SCAN_SEGMENT_MASK))
+			fputc('.', stderr);
+		segnum = (segnum == 0) ? nsegments - 1 : segnum - 1;
+
+		seginfo2 = load_segment(fd, segnum);
+		if (!seginfo2) {
+			if (log_sr && cont) {
+				log_sr = NULL;
+				put_segment_info(seginfo_sr);
+				seginfo_sr = NULL;
+			}
+			cont = 0;
+			if (seginfo) {
+				put_segment_info(seginfo);
+				seginfo = NULL;
+			}
+			continue;
+		}
+
+		if (!seginfo) {
+			seginfo = seginfo2;
+			seginfo2 = NULL;
+
+			if (log_sr)
+				put_segment_info(seginfo_sr);
+			log_sr = lookup_last_super_root(seginfo);
+			if (log_sr)
+				seginfo_sr = get_segment_info(seginfo);
+			continue;
+		}
+
+		if (seginfo2->segseq + 1 != seginfo->segseq)
+			cont = 0;
+
+		if (seginfo2->segseq > seginfo->segseq) {
+			invert++;
+			if (log_sr) {
+				log_sr = NULL;
+				put_segment_info(seginfo_sr);
+				seginfo_sr = NULL;
+			}
+		}
+		if (invert && !log_sr) {
+			log_sr = lookup_last_super_root(seginfo2);
+			if (log_sr) {
+				put_segment_info(seginfo);
+				*seginfop = seginfo2;
+				fputc('\n', stderr);
+				return log_sr; /* latest segment was found */
+			}
+		}
+
+		if (!cont && !log_sr) {
+			log_sr = lookup_last_super_root(seginfo2);
+			if (log_sr)
+				seginfo_sr = get_segment_info(seginfo2);
+		}
+
+		put_segment_info(seginfo);
+		seginfo = seginfo2;
+		seginfo2 = NULL;
+	}
+	fputc('\n', stderr);
+	if (seginfo)
+		put_segment_info(seginfo);
+
+	if (log_sr && !cont) {
+		*seginfop = seginfo_sr;
+		return log_sr; /* regard second-ranking candidate
+				   as the latest segment */
+	}
+	if (seginfo_sr)
+		put_segment_info(seginfo_sr);
+	return NULL;
+}
+
+static void check_mount(int fd, const char *device)
+{
+	FILE *fp;
+	char line[LINE_BUFFER_SIZE];
+
+	fp = fopen(MOUNTS, "r");
+	if (fp == NULL)
+		die("cannot open %s!", MOUNTS);
+
+	while (fgets(line, LINE_BUFFER_SIZE, fp) != NULL) {
+		if (strncmp(strtok(line, " "), device, strlen(device)) == 0) {
+			fclose(fp);
+			die("%s is currently mounted.", device);
+		}
+	}
+	fclose(fp);
+}
+
+static int nilfs_sb_is_valid(struct nilfs_super_block *sbp, int check_crc)
+{
+	__le32 sum;
+	__u32 seed, crc;
+
+	if (le16_to_cpu(sbp->s_magic) != NILFS_SUPER_MAGIC)
+		return 0;
+	if (le16_to_cpu(sbp->s_bytes) > NILFS_MAX_SB_SIZE)
+		return 0;
+	if (!check_crc)
+		return 1;
+
+	seed = le32_to_cpu(sbp->s_crc_seed);
+	sum = sbp->s_sum;
+	sbp->s_sum = 0;
+	crc = crc32_le(seed, (unsigned char *)sbp, le16_to_cpu(sbp->s_bytes));
+	sbp->s_sum = sum;
+	return crc == le32_to_cpu(sum);
+}
+
+static struct nilfs_super_block *nilfs_read_super_block(int fd)
+{
+	struct nilfs_super_block *sbp[2];
+	__u64 devsize, sb2_offset;
+
+	sbp[0] = malloc(NILFS_MAX_SB_SIZE);
+	sbp[1] = malloc(NILFS_MAX_SB_SIZE);
+	if (sbp[0] == NULL || sbp[1] == NULL)
+		goto failed;
+
+	if (ioctl(fd, BLKGETSIZE64, &devsize) != 0)
+		goto failed;
+
+	if (lseek64(fd, NILFS_SB_OFFSET_BYTES, SEEK_SET) < 0 ||
+	    read(fd, sbp[0], NILFS_MAX_SB_SIZE) < 0 ||
+	    !nilfs_sb_is_valid(sbp[0], 0)) {
+		free(sbp[0]);
+		sbp[0] = NULL;
+	}
+
+	sb2_offset = NILFS_SB2_OFFSET_BYTES(devsize);
+	if (lseek64(fd, sb2_offset, SEEK_SET) < 0 ||
+	    read(fd, sbp[1], NILFS_MAX_SB_SIZE) < 0 ||
+	    !nilfs_sb_is_valid(sbp[1], 0))
+		goto sb2_failed;
+
+	if (sb2_offset <
+	    (le64_to_cpu(sbp[1]->s_nsegments) *
+	     le32_to_cpu(sbp[1]->s_blocks_per_segment)) <<
+	    (le32_to_cpu(sbp[1]->s_log_block_size) +
+	     NILFS_SB_BLOCK_SIZE_SHIFT))
+		goto sb2_failed;
+
+ sb2_done:
+	if (!sbp[0]) {
+		sbp[0] = sbp[1];
+		sbp[1] = NULL;
+	}
+
+	if (sbp[1] &&
+	    le64_to_cpu(sbp[1]->s_last_cno) > le64_to_cpu(sbp[0]->s_last_cno)) {
+		free(sbp[0]);
+		return sbp[1];
+	} else if (sbp[0]) {
+		free(sbp[1]);
+		return sbp[0];
+	}
+
+ failed:
+	free(sbp[0]);  /* free(NULL) is just ignored */
+	free(sbp[1]);
+	return NULL;
+
+ sb2_failed:
+	free(sbp[1]);
+	sbp[1] = NULL;
+	goto sb2_done;
+}
+
+static void read_sb_info(struct nilfs_super_block *sbp)
+{
+	char tmbuf[LINE_BUFFER_SIZE];
+	struct tm tm;
+	time_t t;
+
+	fprintf(stderr, "Super-block:\n");
+
+	crc_seed = le32_to_cpu(sbp->s_crc_seed);
+
+	fprintf(stderr, "    revision = %d.%d\n",
+		le32_to_cpu(sbp->s_rev_level),
+		le16_to_cpu(sbp->s_minor_rev_level));
+
+	blocksize = 1 << (le32_to_cpu(sbp->s_log_block_size) + 10);
+	blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment);
+	first_data_block = le64_to_cpu(sbp->s_first_data_block);
+	nsegments = le64_to_cpu(sbp->s_nsegments);
+	checkpoint_size = le16_to_cpu(sbp->s_checkpoint_size);
+	sb_bytes = le16_to_cpu(sbp->s_bytes);
+
+	first_checkpoint_offset =
+		DIV_ROUND_UP(sizeof(struct nilfs_cpfile_header),
+			     checkpoint_size);
+	ncheckpoints_per_block = blocksize / checkpoint_size;
+
+	t = (time_t)le64_to_cpu(sbp->s_wtime);
+	localtime_r(&t, &tm);
+	strftime(tmbuf, LINE_BUFFER_SIZE, "%F %T", &tm);
+
+	fprintf(stderr, "    blocksize = %d\n", blocksize);
+	fprintf(stderr, "    write time = %s\n", tmbuf);
+}
+
+static void commit_super_block(struct nilfs_super_block *sbp,
+			       const struct nilfs_log_ref *log_ref)
+{
+	__u32 sbsum;
+
+	sbp->s_last_pseg = cpu_to_le64(log_ref->blocknr);
+	sbp->s_last_seq = cpu_to_le64(log_ref->seqnum);
+	sbp->s_last_cno = cpu_to_le64(log_ref->cno);
+
+	sbp->s_wtime = cpu_to_le64(time(NULL));
+	sbp->s_state = cpu_to_le16(le16_to_cpu(sbp->s_state) & ~NILFS_VALID_FS);
+
+	/* fill in crc */
+	sbp->s_sum = 0;
+	sbsum = nilfs_crc32(crc_seed, (unsigned char *)sbp, sb_bytes);
+	sbp->s_sum = cpu_to_le32(sbsum);
+}
+
+static int nilfs_write_super_block(int fd, struct nilfs_super_block *sbp)
+{
+	__u64 devsize, sb2_offset;
+	int ret = -1;
+
+	if (ioctl(fd, BLKGETSIZE64, &devsize) != 0)
+		return -1;
+
+	if (lseek64(fd, NILFS_SB_OFFSET_BYTES, SEEK_SET) < 0 ||
+	    write(fd, sbp, sb_bytes) < sb_bytes ||
+	    fsync(fd) < 0)
+		fprintf(stderr, "failed to write primary super block");
+	else
+		ret = 0;
+
+	sb2_offset = NILFS_SB2_OFFSET_BYTES(devsize);
+	if (sb2_offset < (__u64)nsegments * blocks_per_segment * blocksize)
+		return ret;
+
+	if (lseek64(fd, sb2_offset, SEEK_SET) < 0 ||
+	    write(fd, sbp, sb_bytes) <  sb_bytes ||
+	    fsync(fd) < 0)
+		fprintf(stderr,
+			"failed to write secondary super block");
+	else
+		ret = 0;
+
+	return ret;
+}
+
+static int test_latest_log(int fd, struct nilfs_log_ref *log_ref)
+{
+	struct nilfs_segment_info *seginfo;
+	struct nilfs_log_info *loginfo;
+	unsigned long segnum;
+	int ret = -1;
+
+	/*
+	 * check the log the super block points to
+	 */
+	segnum = log_ref->blocknr / blocks_per_segment;
+	seginfo = load_segment(fd, segnum);
+	if (seginfo) {
+		loginfo = lookup_log(seginfo, log_ref->blocknr);
+		if (loginfo && seginfo->segseq == log_ref->seqnum &&
+		    loginfo->flags & NILFS_SS_SR) {
+			log_ref->ctime =
+				le64_to_cpu(loginfo->segsum.ss_create);
+			print_log_message(log_ref,
+					  "A valid log is pointed to by "
+					  "superblock (No change needed)");
+			ret = 0;
+		}
+	}
+	put_segment_info(seginfo);
+	return ret;
+}
+
+static void nilfs_do_rollback(int fd, struct nilfs_log_ref *log_ref)
+{
+	struct nilfs_segment_info *seginfo;
+	struct nilfs_log_info *loginfo;
+	unsigned long segnum;
+
+	/*
+	 * check logs in the current and prior full segments.
+	 */
+	segnum = log_ref->blocknr / blocks_per_segment;
+	loginfo = find_latest_super_root(fd, segnum, log_ref->blocknr,
+					 &seginfo);
+	if (!loginfo)
+		die("Cannot find super root");
+
+	log_ref->blocknr = loginfo->log_start;
+	log_ref->seqnum = seginfo->segseq;
+	log_ref->ctime = le64_to_cpu(loginfo->segsum.ss_create);
+
+	fprintf(stderr, "Searching the latest checkpoint.\n");
+	log_ref->cno = find_latest_cno_in_logical_segment(fd, seginfo, loginfo);
+	if (log_ref->cno == 0)
+		die("Cannot identify the latest checkpoint");
+
+	print_log_message(log_ref, "Selected log");
+}
+
+static void nilfs_fsck(const char *device)
+{
+	struct nilfs_super_block *sbp;
+	struct nilfs_log_ref log_ref;
+	int clean, ret;
+	int c;
+
+	if ((devfd = open(device, O_RDONLY | O_LARGEFILE)) < 0)
+		die("cannot open device %s", device);
+
+	check_mount(devfd, device);
+
+	sbp = nilfs_read_super_block(devfd);
+	if (!sbp)
+		die("cannot read super block (device=%s)", device);
+
+	read_sb_info(sbp);
+
+	log_ref.blocknr = le64_to_cpu(sbp->s_last_pseg);
+	log_ref.seqnum = le64_to_cpu(sbp->s_last_seq);
+	log_ref.cno = le64_to_cpu(sbp->s_last_cno);
+	log_ref.ctime = 0;
+	print_log_message(&log_ref, "    indicated log");
+	fputc('\n', stderr);
+
+	if (le16_to_cpu(sbp->s_state) & NILFS_VALID_FS) {
+		fprintf(stderr, "Clean FS.\n");
+		clean = 1;
+	} else {
+		fprintf(stderr, "Unclean FS.\n");
+		clean = 0;
+	}
+
+	init_block_buffer();
+	init_segment_cache();
+
+	ret = test_latest_log(devfd, &log_ref);
+	if (ret < 0) {
+		fprintf(stderr, "The latest log is lost. "
+			"Trying rollback recovery..\n");
+		clean = 0;
+		nilfs_do_rollback(devfd, &log_ref);
+	}
+	destroy_segment_cache();
+	destroy_block_buffer();
+
+	if (!ret)
+		goto out;
+
+	/*
+	 * Reopen device to update superblock
+	 */
+	close(devfd);
+	devfd = -1;
+	if ((devfd = open(device, O_RDWR | O_LARGEFILE)) < 0)
+		die("cannot open device %s in read/write mode", device);
+
+	fprintf(stderr, "Do you wish to overwrite super block (y/N)? ");
+	if ((c = getchar()) == 'y' || c == 'Y') {
+		commit_super_block(sbp, &log_ref);
+		if (nilfs_write_super_block(devfd, sbp) < 0)
+			die("couldn't update super block (device=%s)", device);
+	}
+ out:
+	if (!clean)
+		fprintf(stderr, "Recovery will complete on mount.\n");
+	free(sbp);
+	close(devfd);
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: %s [-fv] device\n", progname);
+	exit(EXIT_USAGE);
+}
+
+static void parse_options(int argc, char *argv[])
+{
+	int c;
+
+	while ((c = getopt(argc, argv, "fvV")) != EOF) {
+		switch (c) {
+		case 'f':
+			force = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'V':
+			show_version_only = 1;
+			break;
+		default:
+			usage();
+		}
+	}
+	if (show_version_only)
+		return;
+	if (optind == argc)
+		usage();
+}
+
+int main(int argc, char *argv[])
+{
+	char *device;
+
+	if ((progname = strrchr(argv[0], '/')) != NULL)
+		progname++;
+	else
+		progname = argv[0];
+
+	parse_options(argc, argv);
+	if (show_version_only) {
+		fprintf(stderr, "%s version %s\n", progname, PACKAGE_VERSION);
+		exit(EXIT_OK);
+	}
+	device = argv[optind];
+	nilfs_fsck(device);
+
+	exit(EXIT_OK);
+}
-- 
1.6.3.4


[Index of Archives]     [Linux Filesystem Development]     [Linux BTRFS]     [Linux CIFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux