Hi,
On Thu, 4 Dec 2008 14:04:31 +0100, Adrian Ulrich wrote:
> > Wait for a moment. I'm now writing an impromptu tool
> > to correct this kind of problem.
>
> Oh, great. I'll be glad to test it ;-)
Sorry to keep you waiting. ;-)
I will attach a patch against nilfs2-utils-2.0.6, which adds the
tool 'fsck0.nilfs2'.
# fsck0.nilfs2 <device name>
will, hopefully, correct your partition.
(I'm not sure because it's not yet tested enough)
Regards,
Ryusuke Konishi
---
>From ce7bac99115bdf1fdadc002ba2b14bcb1ae4c19c Mon Sep 17 00:00:00 2001
From: Ryusuke Konishi <[EMAIL PROTECTED]>
Date: Fri, 5 Dec 2008 00:53:21 +0900
Subject: [PATCH] nilfs2-utils: add test tool to correct log pointer in super
block
Signed-off-by: Ryusuke Konishi <[EMAIL PROTECTED]>
---
sbin/mkfs/Makefile.am | 6 +-
sbin/mkfs/fsck0.nilfs2.c | 958 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 963 insertions(+), 1 deletions(-)
create mode 100644 sbin/mkfs/fsck0.nilfs2.c
diff --git a/sbin/mkfs/Makefile.am b/sbin/mkfs/Makefile.am
index 965094e..5cbe02a 100644
--- a/sbin/mkfs/Makefile.am
+++ b/sbin/mkfs/Makefile.am
@@ -4,13 +4,17 @@
##
## Written by Koji Sato <[EMAIL PROTECTED]>.
-sbin_PROGRAMS = mkfs.nilfs2
+sbin_PROGRAMS = mkfs.nilfs2 fsck0.nilfs2
mkfs_nilfs2_SOURCES = mkfs.c bitops.c ../../lib/crc32.c mkfs.h
mkfs_nilfs2_CFLAGS = -Wall
mkfs_nilfs2_CPPFLAGS = -I$(top_srcdir)/include
mkfs_nilfs2_LDADD = -luuid
+fsck0_nilfs2_SOURCES = fsck0.nilfs2.c ../../lib/crc32.c mkfs.h
+fsck0_nilfs2_CFLAGS = -Wall
+fsck0_nilfs2_CPPFLAGS = -I$(top_srcdir)/include
+
install-exec-hook:
list='$(sbin_PROGRAMS)'; \
for p in $$list; do \
diff --git a/sbin/mkfs/fsck0.nilfs2.c b/sbin/mkfs/fsck0.nilfs2.c
new file mode 100644
index 0000000..d3d335f
--- /dev/null
+++ b/sbin/mkfs/fsck0.nilfs2.c
@@ -0,0 +1,958 @@
+/*
+ * 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 nilfs2-utils package.
+ *
+ * Copyright (C) 2008 Nippon Telegraph and Telephone Corporation.
+ * Written by Ryusuke Konishi <[EMAIL PROTECTED]>
+ */
+#define _LARGEFILE64_SOURCE
+#define _XOPEN_SOURCE 600
+
+#include <sys/types.h>
+#include <linux/types.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#include "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 DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+char *progname = NULL;
+
+struct nilfs_pseg_ref {
+ __u64 blocknr; /* start blocknumber */
+ __u64 seqnum; /* sequence number */
+ __u64 cno; /* checkpoint number */
+ __u64 ctime; /* creation time */
+};
+
+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 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(1);
+}
+
+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 pseg_valid(int fd, __u64 pseg_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 = pseg_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);
+
+ posix_fadvise(fd, pseg_start * blocksize, nblocks * blocksize,
+ POSIX_FADV_SEQUENTIAL);
+
+ 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 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;
+}
+
+/* partial segment information */
+struct nilfs_pseg_info {
+ struct nilfs_list list;
+ __u64 pseg_start; /* start blocknr */
+ __u32 nblocks;
+ struct nilfs_segment_summary segsum; /* on-disk log header */
+ __u16 flags;
+};
+
+static inline struct nilfs_pseg_info *
+nilfs_pseg_list_entry(struct nilfs_list *p)
+{
+ return (void *)p - offsetof(struct nilfs_pseg_info, list);
+}
+
+struct nilfs_pseg_info *new_pseg_info(__u64 blocknr)
+{
+ struct nilfs_pseg_info *pseginfo = nilfs_zalloc(sizeof(*pseginfo));
+
+ pseginfo->pseg_start = blocknr;
+ nilfs_list_init(&pseginfo->list);
+ return pseginfo;
+}
+
+static void dispose_pseg_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_pseg_list_entry(p));
+ }
+}
+
+/*
+ * Segment information
+ */
+struct nilfs_segment_info {
+ struct nilfs_list list;
+ struct nilfs_list pseg_list; /* partial segment list */
+ __u64 seg_start; /* start blocknr of the segment */
+ __u64 next; /* pointer to the next full segment */
+ __u64 segseq; /* sequence number of the segment */
+ unsigned long segnum; /* the number of the segment */
+ int npsegs; /* number of partial segments */
+ int refcnt;
+};
+
+static struct nilfs_list segment_cache;
+
+static inline 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->pseg_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_pseg_list(&seginfo->pseg_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_pseg_info *pseginfo;
+ 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;
+
+ pseginfo = new_pseg_info(blocknr);
+ nilfs_list_add(&seginfo->pseg_list, &pseginfo->list);
+
+ ss = &pseginfo->segsum;
+ read_block(fd, blocknr, ss, sizeof(*ss));
+
+ if (!pseg_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->npsegs++;
+
+ pseginfo->nblocks = le32_to_cpu(ss->ss_nblocks);
+ pseginfo->flags = le16_to_cpu(ss->ss_flags);
+
+ blocknr += pseginfo->nblocks;
+ if (blocknr >= end)
+ return seginfo;
+
+ pseginfo = new_pseg_info(blocknr);
+ nilfs_list_add(&seginfo->pseg_list, &pseginfo->list);
+
+ ss = &pseginfo->segsum;
+ read_block(fd, blocknr, ss, sizeof(*ss));
+
+ } while (pseg_valid(fd, blocknr, ss) &&
+ le64_to_cpu(ss->ss_seq) == seginfo->segseq);
+
+ nilfs_list_del(&pseginfo->list);
+ free(pseginfo);
+
+ return seginfo;
+}
+
+/*
+ * Operations on segment_info structure
+ */
+struct nilfs_pseg_info *lookup_pseg(struct nilfs_segment_info *seginfo,
+ __u64 blocknr)
+{
+ struct nilfs_pseg_info *pseginfo;
+ struct nilfs_list *p;
+
+ for (p = seginfo->pseg_list.next; p != &seginfo->pseg_list;
+ p = p->next) {
+ pseginfo = nilfs_pseg_list_entry(p);
+ if (pseginfo->pseg_start == blocknr)
+ return pseginfo;
+ }
+ return NULL;
+}
+
+struct nilfs_pseg_info *first_pseg(struct nilfs_segment_info *seginfo)
+{
+ return nilfs_list_empty(&seginfo->pseg_list) ? NULL :
+ nilfs_pseg_list_entry(seginfo->pseg_list.next);
+}
+
+struct nilfs_pseg_info *last_pseg(struct nilfs_segment_info *seginfo)
+{
+ return nilfs_list_empty(&seginfo->pseg_list) ? NULL :
+ nilfs_pseg_list_entry(seginfo->pseg_list.prev);
+}
+
+struct nilfs_pseg_info *next_pseg(struct nilfs_segment_info *seginfo,
+ struct nilfs_pseg_info *pseginfo)
+{
+ return pseginfo->list.next == &seginfo->pseg_list ? NULL :
+ nilfs_pseg_list_entry(pseginfo->list.next);
+}
+
+struct nilfs_pseg_info *prev_pseg(struct nilfs_segment_info *seginfo,
+ struct nilfs_pseg_info *pseginfo)
+{
+ return pseginfo->list.prev == &seginfo->pseg_list ? NULL :
+ nilfs_pseg_list_entry(pseginfo->list.prev);
+}
+
+struct nilfs_pseg_info *
+lookup_last_super_root(struct nilfs_segment_info *seginfo)
+{
+ struct nilfs_pseg_info *pseginfo;
+
+ for (pseginfo = last_pseg(seginfo); pseginfo != NULL;
+ pseginfo = prev_pseg(seginfo, pseginfo)) {
+ if (pseginfo->flags & NILFS_SS_SR)
+ return pseginfo;
+ }
+ return NULL;
+}
+
+unsigned long log_length(struct nilfs_segment_info *seginfo)
+{
+ return nilfs_list_empty(&seginfo->pseg_list) ? 0 :
+ nilfs_pseg_list_entry(seginfo->pseg_list.prev)->pseg_start -
+ seginfo->seg_start +
+ nilfs_pseg_list_entry(seginfo->pseg_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 pseg_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 = pseg_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_pseg_info *start)
+{
+ struct nilfs_pseg_info *pseginfo = start ? : last_pseg(seginfo);
+ __u64 cno, latest_cno = 0;
+ __u64 seq;
+ int i = 0;
+
+ if (pseginfo == NULL)
+ return 0;
+
+ get_segment_info(seginfo);
+ do {
+ cno = get_latest_cno(fd, pseginfo->pseg_start);
+ if (cno > latest_cno)
+ latest_cno = cno;
+
+ if (pseginfo->flags & NILFS_SS_LOGBGN)
+ break;
+
+ pseginfo = prev_pseg(seginfo, pseginfo);
+ if (pseginfo == 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;
+ pseginfo = last_pseg(seginfo);
+ }
+ } while (pseginfo != NULL && !(pseginfo->flags & NILFS_SS_LOGEND));
+
+ if (seginfo)
+ put_segment_info(seginfo);
+ return latest_cno;
+}
+
+void print_pseg_message(struct nilfs_pseg_ref *pseg_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)pseg_ref->blocknr);
+
+ for (cp = fmt; *cp == ' '; cp++)
+ fputc(' ', stderr);
+ fprintf(stderr, " segnum = %lu, seq = %llu, cno=%llu\n",
+ (unsigned long)pseg_ref->blocknr / blocks_per_segment,
+ (unsigned long long)pseg_ref->seqnum,
+ (unsigned long long)pseg_ref->cno);
+ if (pseg_ref->ctime) {
+ char tmbuf[LINE_BUFFER_SIZE];
+ struct tm tm;
+ time_t t = (time_t)le64_to_cpu(pseg_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_pseg_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_pseg_info *pseg_sr = NULL;
+ int cont = 0, invert = 0;
+ int i;
+
+ seginfo = load_segment(fd, segnum);
+ if (seginfo) {
+ pseg_sr = lookup_last_super_root(seginfo);
+ if (pseg_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 (pseg_sr && cont) {
+ pseg_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 (pseg_sr)
+ put_segment_info(seginfo_sr);
+ pseg_sr = lookup_last_super_root(seginfo);
+ if (pseg_sr)
+ seginfo_sr = get_segment_info(seginfo);
+ continue;
+ }
+
+ if (seginfo2->segseq + 1 != seginfo->segseq)
+ cont = 0;
+
+ if (seginfo2->segseq > seginfo->segseq) {
+ invert++;
+ if (pseg_sr) {
+ pseg_sr = NULL;
+ put_segment_info(seginfo_sr);
+ seginfo_sr = NULL;
+ }
+ }
+ if (invert && !pseg_sr) {
+ pseg_sr = lookup_last_super_root(seginfo2);
+ if (pseg_sr) {
+ put_segment_info(seginfo);
+ *seginfop = seginfo2;
+ fputc('\n', stderr);
+ return pseg_sr; /* latest segment was found */
+ }
+ }
+
+ if (!cont && !pseg_sr) {
+ pseg_sr = lookup_last_super_root(seginfo2);
+ if (pseg_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 (pseg_sr && !cont) {
+ *seginfop = seginfo_sr;
+ return pseg_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 void check_super_block(struct nilfs_super_block *sb)
+{
+ char tmbuf[LINE_BUFFER_SIZE];
+ struct tm tm;
+ time_t t;
+ __u32 sbsum, crc;
+ int sb_bytes = le16_to_cpu(sb->s_bytes);
+
+ if (le16_to_cpu(sb->s_magic) != NILFS_SUPER_MAGIC)
+ die("Not nilfs2 partition");
+
+ fprintf(stderr, "Super-block:\n");
+
+ crc_seed = le32_to_cpu(sb->s_crc_seed);
+ sbsum = le32_to_cpu(sb->s_sum);
+
+ sb->s_sum = 0;
+ crc = nilfs_crc32(crc_seed, (unsigned char *)sb, sb_bytes);
+ sb->s_sum = cpu_to_le32(sb->s_sum);
+
+ fprintf(stderr, " revision = %d.%d, checksum = %s\n",
+ le32_to_cpu(sb->s_rev_level),
+ le16_to_cpu(sb->s_minor_rev_level),
+ crc == sbsum ? "OK" : "error");
+
+ blocksize = 1 << (le32_to_cpu(sb->s_log_block_size) + 10);
+ blocks_per_segment = le32_to_cpu(sb->s_blocks_per_segment);
+ first_data_block = le64_to_cpu(sb->s_first_data_block);
+ nsegments = le64_to_cpu(sb->s_nsegments);
+ checkpoint_size = le16_to_cpu(sb->s_checkpoint_size);
+
+ 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(sb->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 *sb,
+ struct nilfs_pseg_ref *pseg_ref)
+{
+ __u32 sbsum;
+ int sb_bytes = le16_to_cpu(sb->s_bytes);
+
+ sb->s_last_pseg = cpu_to_le64(pseg_ref->blocknr);
+ sb->s_last_seq = cpu_to_le64(pseg_ref->seqnum);
+ sb->s_last_cno = cpu_to_le64(pseg_ref->cno);
+
+ sb->s_state = cpu_to_le16(le16_to_cpu(sb->s_state) & ~NILFS_VALID_FS);
+
+ /* fill in crc */
+ sb->s_sum = 0;
+ sbsum = nilfs_crc32(crc_seed, (unsigned char *)sb, sb_bytes);
+ sb->s_sum = cpu_to_le32(sbsum);
+}
+
+static int nilfs_do_recovery(int fd, struct nilfs_pseg_ref *pseg_ref)
+{
+ struct nilfs_pseg_info *pseginfo;
+ struct nilfs_segment_info *seginfo;
+ unsigned long segnum;
+ int ret = 0;
+
+ init_block_buffer();
+ init_segment_cache();
+
+ /*
+ * check the partial segment pointed by superblock.
+ */
+ segnum = pseg_ref->blocknr / blocks_per_segment;
+ seginfo = load_segment(fd, segnum);
+ if (seginfo) {
+ pseginfo = lookup_pseg(seginfo, pseg_ref->blocknr);
+ if (pseginfo &&
+ seginfo->segseq == pseg_ref->seqnum &&
+ pseginfo->flags & NILFS_SS_SR) {
+ pseg_ref->ctime =
le64_to_cpu(pseginfo->segsum.ss_create);
+
+ print_pseg_message(pseg_ref,
+ "Valid segment is pointed by "
+ "superblock (No change needed)");
+ goto out;
+ }
+ put_segment_info(seginfo);
+ }
+
+ /*
+ * check logs in the current and prior full segments.
+ */
+ fprintf(stderr, "The latest segment is lost. "
+ "Trying rollback recovery..\n");
+
+ pseginfo = find_latest_super_root(fd, segnum, pseg_ref->blocknr,
+ &seginfo);
+ if (!pseginfo)
+ die("Cannot find super root");
+
+ pseg_ref->blocknr = pseginfo->pseg_start;
+ pseg_ref->seqnum = seginfo->segseq;
+ pseg_ref->ctime = le64_to_cpu(pseginfo->segsum.ss_create);
+
+ fprintf(stderr, "Searching the latest checkpoint.\n");
+ pseg_ref->cno = find_latest_cno_in_logical_segment(
+ fd, seginfo, pseginfo);
+ if (pseg_ref->cno == 0)
+ die("Cannot identify the latest checkpoint");
+
+ print_pseg_message(pseg_ref, "Selected segment");
+ ret = 1;
+ out:
+ destroy_segment_cache();
+ destroy_block_buffer();
+ return ret;
+}
+
+static void nilfs_fsck(const char *device)
+{
+ struct nilfs_super_block sb;
+ struct nilfs_pseg_ref pseg_ref;
+ ssize_t size;
+ int c;
+
+ if ((devfd = open(device, O_RDONLY | O_LARGEFILE)) < 0)
+ die("cannot open device %s", device);
+
+ size = pread(devfd, &sb, sizeof(sb), NILFS_SB_OFFSET_BYTES);
+ if (size < sizeof(sb))
+ die("cannot read super block (device=%s)", device);
+
+ check_mount(devfd, device);
+ check_super_block(&sb);
+
+ pseg_ref.blocknr = le64_to_cpu(sb.s_last_pseg);
+ pseg_ref.seqnum = le64_to_cpu(sb.s_last_seq);
+ pseg_ref.cno = le64_to_cpu(sb.s_last_cno);
+ pseg_ref.ctime = 0;
+ print_pseg_message(&pseg_ref, " indicated partial segment");
+ fputc('\n', stderr);
+
+ if (le16_to_cpu(sb.s_state) & NILFS_VALID_FS) {
+ fprintf(stderr, "Clean FS.\n");
+ goto out_clean;
+ }
+
+ if (nilfs_do_recovery(devfd, &pseg_ref) == 0)
+ 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') {
+ commit_super_block(&sb, &pseg_ref);
+ size = pwrite(devfd, &sb, sizeof(sb), NILFS_SB_OFFSET_BYTES);
+ if (size < sizeof(sb))
+ die("cannot write out super block (device=%s)", device);
+ fsync(devfd);
+ }
+ out:
+ fprintf(stderr, "Recovery will complete on mount.\n");
+
+ out_clean:
+ close(devfd);
+}
+
+int main(int argc, char *argv[])
+{
+ char *device;
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <device>\n", progname);
+ exit(0);
+ }
+
+ device = argv[1];
+ nilfs_fsck(device);
+
+ return 0;
+}
--
1.5.6.5
_______________________________________________
users mailing list
[email protected]
https://www.nilfs.org/mailman/listinfo/users