From: Ryusuke Konishi <ryusuke-sG5X7nlA6pw@public.gmane.org>
To: ninegua-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: linux-nilfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: Segment magic number invalid
Date: Sun, 16 May 2010 10:33:00 +0900 (JST) [thread overview]
Message-ID: <20100516.103300.124959052.ryusuke@osrg.net> (raw)
In-Reply-To: <AANLkTikEfAtM5PdXSFw5pA_8mDW5pcyxes3GTO3kMBPa-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[-- Attachment #1: Type: Text/Plain, Size: 3083 bytes --]
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
[-- Attachment #2: fsck0-for-2.0.18.patch --]
[-- Type: Text/Plain, Size: 29810 bytes --]
From b1a403ed4a7cb6987052729075255d5d893bebdb Mon Sep 17 00:00:00 2001
From: Ryusuke Konishi <konishi.ryusuke-Zyj7fXuS5i5L9jVzuh4AOg@public.gmane.org>
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-Zyj7fXuS5i5L9jVzuh4AOg@public.gmane.org>
---
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-sG5X7nlA6pw@public.gmane.org>
+ */
+#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
next prev parent reply other threads:[~2010-05-16 1:33 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-05-15 0:24 Segment magic number invalid Paul L
[not found] ` <AANLkTikEfAtM5PdXSFw5pA_8mDW5pcyxes3GTO3kMBPa-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-05-16 1:33 ` Ryusuke Konishi [this message]
[not found] ` <AANLkTinBFQYWRZRmVL69lqxRG1p0cBQ2AxBiFk4SlNE8@mail.gmail.com>
[not found] ` <AANLkTinBFQYWRZRmVL69lqxRG1p0cBQ2AxBiFk4SlNE8-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-05-16 18:32 ` Paul L
[not found] ` <AANLkTimHvMze3B8fQE383WW8WLbOYcZx0pz0sfjC2DhJ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-05-17 2:46 ` Ryusuke Konishi
[not found] ` <20100517.114657.112904526.ryusuke-sG5X7nlA6pw@public.gmane.org>
2010-05-17 4:14 ` Paul L
[not found] ` <AANLkTimfmJU5h4pS23eKpjLNV_c6d4YEBHNmBdcy-r4g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2010-05-17 7:26 ` Ryusuke Konishi
[not found] ` <20100517.162614.107743625.ryusuke-sG5X7nlA6pw@public.gmane.org>
2010-05-19 13:49 ` Jiro SEKIBA
[not found] ` <871vd8dms5.wl%jir-27yqGEOhnJbQT0dZR+AlfA@public.gmane.org>
2010-05-19 16:14 ` Ryusuke Konishi
[not found] ` <20100520.011455.32954390.ryusuke-sG5X7nlA6pw@public.gmane.org>
2010-05-27 7:27 ` [PATCH] nilfs2: sync super blokcs in turns Jiro SEKIBA
[not found] ` <87r5kx6bz9.wl%jir-27yqGEOhnJbQT0dZR+AlfA@public.gmane.org>
2010-05-27 16:59 ` Ryusuke Konishi
[not found] ` <20100528.015959.179492440.ryusuke-sG5X7nlA6pw@public.gmane.org>
2010-05-28 2:29 ` Jiro SEKIBA
[not found] ` <87zkzkpxn3.wl%jir-27yqGEOhnJbQT0dZR+AlfA@public.gmane.org>
2010-06-02 18:26 ` Ryusuke Konishi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100516.103300.124959052.ryusuke@osrg.net \
--to=ryusuke-sg5x7nla6pw@public.gmane.org \
--cc=linux-nilfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=ninegua-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox