[Cluster-devel] [GFS2 PATCH][userland] bz 291551: gfs2_fsck clears journals without asking.

Bob Peterson rpeterso at redhat.com
Tue Oct 9 17:59:06 UTC 2007


Hi,

This patch is for bugzilla bug 291551: gfs2_fsck clears journals
without asking.  It allows gfs2_fsck to detect dirty journals, then
replay them or clear them based on user responses.  I'm posting it
here for your review.  That may not ordinarily be necessary, but due
to the size of the patch, I'd like a few eyeballs to see it.  Most
of the code was pilfered from the gfs2 kernel's recovery.c and
adapted to userland.  Some of the code got put into libgfs2 because
I want to eventually use it for future gfs2_edit enhancements.

Most of the code has been unit-tested with gdb, but I'm not done
testing it.

Regards,

Bob Peterson
Red Hat Cluster Suite
---
Index: fsck/fs_recovery.c
===================================================================
RCS file: /cvs/cluster/cluster/gfs2/fsck/fs_recovery.c,v
retrieving revision 1.5
diff -w -u -p -p -u -r1.5 fs_recovery.c
--- fsck/fs_recovery.c	12 Feb 2007 19:28:49 -0000	1.5
+++ fsck/fs_recovery.c	9 Oct 2007 17:39:19 -0000
@@ -11,6 +11,7 @@
 *******************************************************************************
 ******************************************************************************/
 
+#include <errno.h>
 #include <inttypes.h>
 #include <linux_endian.h>
 #include <stdlib.h>
@@ -18,12 +19,378 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "fsck.h"
+#include "fs_recovery.h"
 #include "libgfs2.h"
 #include "util.h"
-#include "fs_recovery.h"
 
 #define RANDOM(values) ((values) * (random() / (RAND_MAX + 1.0)))
 
+unsigned int sd_found_jblocks = 0, sd_replayed_jblocks = 0;
+unsigned int sd_found_metablocks = 0, sd_replayed_metablocks = 0;
+unsigned int sd_found_revokes = 0;
+osi_list_t sd_revoke_list;
+unsigned int sd_replay_tail;
+
+struct gfs2_revoke_replay {
+	osi_list_t rr_list;
+	uint64_t rr_blkno;
+	unsigned int rr_where;
+};
+
+int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
+{
+	osi_list_t *tmp, *head = &sd_revoke_list;
+	struct gfs2_revoke_replay *rr;
+	int found = 0;
+
+	osi_list_foreach(tmp, head) {
+		rr = osi_list_entry(tmp, struct gfs2_revoke_replay, rr_list);
+		if (rr->rr_blkno == blkno) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		rr->rr_where = where;
+		return 0;
+	}
+
+	rr = malloc(sizeof(struct gfs2_revoke_replay));
+	if (!rr)
+		return -ENOMEM;
+
+	rr->rr_blkno = blkno;
+	rr->rr_where = where;
+	osi_list_add(&rr->rr_list, head);
+	return 1;
+}
+
+int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
+{
+	osi_list_t *tmp;
+	struct gfs2_revoke_replay *rr;
+	int wrap, a, b, revoke;
+	int found = 0;
+
+	osi_list_foreach(tmp, &sd_revoke_list) {
+		rr = osi_list_entry(tmp, struct gfs2_revoke_replay, rr_list);
+		if (rr->rr_blkno == blkno) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return 0;
+
+	wrap = (rr->rr_where < sd_replay_tail);
+	a = (sd_replay_tail < where);
+	b = (where < rr->rr_where);
+	revoke = (wrap) ? (a || b) : (a && b);
+	return revoke;
+}
+
+void gfs2_revoke_clean(struct gfs2_sbd *sdp)
+{
+	osi_list_t *head = &sd_revoke_list;
+	struct gfs2_revoke_replay *rr;
+
+	while (!osi_list_empty(head)) {
+		rr = osi_list_entry(head->next, struct gfs2_revoke_replay, rr_list);
+		osi_list_del(&rr->rr_list);
+		free(rr);
+	}
+}
+
+static int buf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				struct gfs2_log_descriptor *ld, __be64 *ptr,
+				int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh_log, *bh_ip;
+	uint64_t blkno;
+	int error = 0;
+	enum update_flags if_modified;
+
+	if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_METADATA)
+		return 0;
+
+	gfs2_replay_incr_blk(ip, &start);
+
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		uint32_t check_magic;
+
+		sd_found_metablocks++;
+
+		blkno = be64_to_cpu(*ptr++);
+		if (gfs2_revoke_check(sdp, blkno, start))
+			continue;
+
+		error = gfs2_replay_read_block(ip, start, &bh_log);
+		if (error)
+			return error;
+
+		bh_ip = bget(sdp, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+
+		check_magic = ((struct gfs2_meta_header *)
+			       (bh_ip->b_data))->mh_magic;
+		check_magic = be32_to_cpu(check_magic);
+		if (check_magic != GFS2_MAGIC) {
+			if_modified = not_updated;
+			error = -EIO;
+		} else
+			if_modified = updated;
+
+		brelse(bh_log, not_updated);
+		brelse(bh_ip, if_modified);
+		if (error)
+			break;
+
+		sd_replayed_metablocks++;
+	}
+	return error;
+}
+
+static int revoke_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				   struct gfs2_log_descriptor *ld, __be64 *ptr,
+				   int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_length);
+	unsigned int revokes = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh;
+	unsigned int offset;
+	uint64_t blkno;
+	int first = 1;
+	int error;
+
+	if (pass != 0 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_REVOKE)
+		return 0;
+
+	offset = sizeof(struct gfs2_log_descriptor);
+
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		error = gfs2_replay_read_block(ip, start, &bh);
+		if (error)
+			return error;
+
+		if (!first) {
+			if (gfs2_check_meta(bh, GFS2_METATYPE_LB))
+				continue;
+		}
+		while (offset + sizeof(uint64_t) <= sdp->sd_sb.sb_bsize) {
+			blkno = be64_to_cpu(*(__be64 *)(bh->b_data + offset));
+			error = gfs2_revoke_add(sdp, blkno, start);
+			if (error < 0)
+				return error;
+			else if (error)
+				sd_found_revokes++;
+
+			if (!--revokes)
+				break;
+			offset += sizeof(uint64_t);
+		}
+
+		brelse(bh, updated);
+		offset = sizeof(struct gfs2_meta_header);
+		first = 0;
+	}
+	return 0;
+}
+
+static int databuf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				    struct gfs2_log_descriptor *ld,
+				    __be64 *ptr, int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh_log, *bh_ip;
+	uint64_t blkno;
+	uint64_t esc;
+	int error = 0;
+
+	if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_JDATA)
+		return 0;
+
+	gfs2_replay_incr_blk(ip, &start);
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		blkno = be64_to_cpu(*ptr++);
+		esc = be64_to_cpu(*ptr++);
+
+		sd_found_jblocks++;
+
+		if (gfs2_revoke_check(sdp, blkno, start))
+			continue;
+
+		error = gfs2_replay_read_block(ip, start, &bh_log);
+		if (error)
+			return error;
+
+		bh_ip = bget(sdp, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+
+		/* Unescape */
+		if (esc) {
+			__be32 *eptr = (__be32 *)bh_ip->b_data;
+			*eptr = cpu_to_be32(GFS2_MAGIC);
+		}
+
+		brelse(bh_log, not_updated);
+		brelse(bh_ip, updated);
+		if (error)
+			break;
+
+		sd_replayed_jblocks++;
+	}
+	return error;
+}
+
+/**
+ * foreach_descriptor - go through the active part of the log
+ * @ip: the journal incore inode
+ * @start: the first log header in the active region
+ * @end: the last log header (don't process the contents of this entry))
+ *
+ * Call a given function once for every log descriptor in the active
+ * portion of the log.
+ *
+ * Returns: errno
+ */
+
+int foreach_descriptor(struct gfs2_inode *ip, unsigned int start,
+		       unsigned int end, int pass)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_log_descriptor *ld;
+	int error = 0;
+	uint32_t length;
+	__be64 *ptr;
+	unsigned int offset = sizeof(struct gfs2_log_descriptor);
+	offset += sizeof(__be64) - 1;
+	offset &= ~(sizeof(__be64) - 1);
+
+	while (start != end) {
+		uint32_t check_magic;
+
+		error = gfs2_replay_read_block(ip, start, &bh);
+		if (error)
+			return error;
+		check_magic = ((struct gfs2_meta_header *)
+			       (bh->b_data))->mh_magic;
+		check_magic = be32_to_cpu(check_magic);
+		if (check_magic != GFS2_MAGIC) {
+			brelse(bh, updated);
+			return -EIO;
+		}
+		ld = (struct gfs2_log_descriptor *)bh->b_data;
+		length = be32_to_cpu(ld->ld_length);
+
+		if (be32_to_cpu(ld->ld_header.mh_type) == GFS2_METATYPE_LH) {
+			struct gfs2_log_header lh;
+
+			error = get_log_header(ip, start, &lh);
+			if (!error) {
+				gfs2_replay_incr_blk(ip, &start);
+				brelse(bh, updated);
+				continue;
+			}
+			if (error == 1)
+				error = -EIO;
+			brelse(bh, updated);
+			return error;
+		} else if (gfs2_check_meta(bh, GFS2_METATYPE_LD)) {
+			brelse(bh, updated);
+			return -EIO;
+		}
+		ptr = (__be64 *)(bh->b_data + offset);
+		error = databuf_lo_scan_elements(ip, start, ld, ptr, pass);
+		error = buf_lo_scan_elements(ip, start, ld, ptr, pass);
+		error = revoke_lo_scan_elements(ip, start, ld, ptr, pass);
+		if (error) {
+			brelse(bh, updated);
+			return error;
+		}
+
+		while (length--)
+			gfs2_replay_incr_blk(ip, &start);
+
+		brelse(bh, updated);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs2_recover_journal - recovery a given journal
+ * @ip: the journal incore inode
+ *
+ * Acquire the journal's lock, check to see if the journal is clean, and
+ * do recovery if necessary.
+ *
+ * Returns: errno
+ */
+
+int gfs2_recover_journal(struct gfs2_inode *ip, int j)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_log_header head;
+	unsigned int pass;
+	int error;
+
+	log_info("jid=%u: Looking at journal...\n", j);
+
+	osi_list_init(&sd_revoke_list);
+	error = gfs2_find_jhead(ip, &head);
+	if (error)
+		goto out;
+
+	if (head.lh_flags & GFS2_LOG_HEAD_UNMOUNT) {
+		log_info("jid=%u: Journal is clean.\n", j);
+		return 0;
+	}
+	if (query(&opts, "\nJournal #%d (\"journal%d\") is dirty.  Okay to replay it? (y/n)",
+		    j+1, j)) {
+		log_info("jid=%u: Replaying journal...\n", j);
+
+		sd_found_jblocks = sd_replayed_jblocks = 0;
+		sd_found_metablocks = sd_replayed_metablocks = 0;
+		sd_found_revokes = 0;
+		sd_replay_tail = head.lh_tail;
+		for (pass = 0; pass < 2; pass++) {
+			error = foreach_descriptor(ip, head.lh_tail,
+						   head.lh_blkno, pass);
+			if (error)
+				goto out;
+		}
+		log_info("jid=%u: Found %u revoke tags\n", j,
+			 sd_found_revokes);
+		gfs2_revoke_clean(sdp);
+		error = clean_journal(ip, &head);
+		if (error)
+			goto out;
+		log_err("jid=%u: Replayed %u of %u journaled data blocks\n",
+			j, sd_replayed_jblocks, sd_found_jblocks);
+		log_err("jid=%u: Replayed %u of %u metadata blocks\n",
+			j, sd_replayed_metablocks, sd_found_metablocks);
+	} else {
+		if (query(&opts, "Do you want to clear the dirty journal instead? (y/n)")) {
+			write_journal(sdp, sdp->md.journal[j], j,
+				      sdp->md.journal[j]->i_di.di_size /
+				      sdp->sd_sb.sb_bsize);
+			
+		} else
+			log_err("jid=%u: Dirty journal not replayed or cleared.\n", j);
+	}
+
+out:
+	log_info("jid=%u: %s\n", j, (error) ? "Failed" : "Done");
+	return error;
+}
+
 /*
  * reconstruct_journals - write fresh journals
  * sdp: the super block
@@ -38,16 +405,12 @@
 int reconstruct_journals(struct gfs2_sbd *sdp){
 	int i;
 
-	log_notice("Clearing journals (this may take a while)");
+	log_notice("Recovering journals (this may take a while)");
 	for(i=0; i < sdp->md.journals; i++) {
-		/* Journal replay seems to have slowed down quite a bit in
-		 * the gfs2_fsck */
 		if((i % 2) == 0)
 			log_at_notice(".");
-		write_journal(sdp, sdp->md.journal[i], i,
-					  sdp->md.journal[i]->i_di.di_size / sdp->sd_sb.sb_bsize);
-		/* Can't use d_di.di_blocks because that also includes metadata. */
+		gfs2_recover_journal(sdp->md.journal[i], i);
 	}
-	log_notice("\nJournals cleared.\n");
+	log_notice("\nJournal recovery complete.\n");
 	return 0;
 }
Index: libgfs2/Makefile
===================================================================
RCS file: /cvs/cluster/cluster/gfs2/libgfs2/Makefile,v
retrieving revision 1.11
diff -w -u -p -p -u -r1.11 Makefile
--- libgfs2/Makefile	28 Aug 2007 04:35:43 -0000	1.11
+++ libgfs2/Makefile	9 Oct 2007 17:39:19 -0000
@@ -31,6 +31,7 @@ OBJS=	bitmap.o \
 	gfs2_log.o \
 	misc.o \
 	ondisk.o \
+	recovery.o \
 	size.o \
 	structures.o \
 	super.o \
Index: libgfs2/libgfs2.h
===================================================================
RCS file: /cvs/cluster/cluster/gfs2/libgfs2/libgfs2.h,v
retrieving revision 1.19
diff -w -u -p -p -u -r1.19 libgfs2.h
--- libgfs2/libgfs2.h	17 Sep 2007 17:08:20 -0000	1.19
+++ libgfs2/libgfs2.h	9 Oct 2007 17:39:19 -0000
@@ -514,6 +514,22 @@ void mount_gfs2_meta(struct gfs2_sbd *sd
 void lock_for_admin(struct gfs2_sbd *sdp);
 void cleanup_metafs(struct gfs2_sbd *sdp);
 
+/* recovery.c */
+void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk);
+int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+			   struct gfs2_buffer_head **bh);
+int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where);
+int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno,
+		      unsigned int where);
+void gfs2_revoke_clean(struct gfs2_sbd *sdp);
+int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+		   struct gfs2_log_header *head);
+int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+		 struct gfs2_log_header *head);
+int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head);
+int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head);
+int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head);
+
 /* rgrp.c */
 int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_list *rgd);
 struct rgrp_list *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk);
Index: libgfs2/recovery.c
===================================================================
RCS file: libgfs2/recovery.c
diff -N libgfs2/recovery.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libgfs2/recovery.c	9 Oct 2007 17:39:19 -0000
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License version 2.
+ *
+ * NOTE:
+ *
+ * This code was pilfered from the gfs2 kernel and adapted to userland.
+ * If you change this part, you should evaluate whether the upstream kernel
+ * version of recovery.c should be changed as well.  Likewise, if the
+ * upstream version changes, this part should be kept in sync.
+ * 
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "libgfs2.h"
+
+void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk)
+{
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+        if (++*blk == jd_blocks)
+                *blk = 0;
+}
+
+int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+			   struct gfs2_buffer_head **bh)
+{
+	int new = 0;
+	uint64_t dblock;
+	uint32_t extlen;
+
+	block_map(ip, blk, &new, &dblock, &extlen, FALSE, not_updated);
+	if (!dblock)
+		return -EIO;
+
+	*bh = bread(ip->i_sbd, dblock);
+	return 0;
+}
+
+/**
+ * get_log_header - read the log header for a given segment
+ * @ip: the journal incore inode
+ * @blk: the block to look at
+ * @lh: the log header to return
+ *
+ * Read the log header for a given segement in a given journal.  Do a few
+ * sanity checks on it.
+ *
+ * Returns: 0 on success,
+ *          1 if the header was invalid or incomplete,
+ *          errno on error
+ */
+
+int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+		   struct gfs2_log_header *head)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_log_header lh, *tmp;
+	uint32_t hash, saved_hash;
+	int error;
+
+	error = gfs2_replay_read_block(ip, blk, &bh);
+	if (error)
+		return error;
+
+	tmp = (struct gfs2_log_header *)bh->b_data;
+	saved_hash = tmp->lh_hash;
+	tmp->lh_hash = 0;
+	hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header));
+	tmp->lh_hash = saved_hash;
+	gfs2_log_header_in(&lh, bh->b_data);
+	brelse(bh, not_updated);
+
+	if (error || lh.lh_blkno != blk || lh.lh_hash != hash)
+		return 1;
+
+	*head = lh;
+
+	return 0;
+}
+
+/**
+ * find_good_lh - find a good log header
+ * @ip: the journal incore inode
+ * @blk: the segment to start searching from
+ * @lh: the log header to fill in
+ * @forward: if true search forward in the log, else search backward
+ *
+ * Call get_log_header() to get a log header for a segment, but if the
+ * segment is bad, either scan forward or backward until we find a good one.
+ *
+ * Returns: errno
+ */
+
+int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+		 struct gfs2_log_header *head)
+{
+	unsigned int orig_blk = *blk;
+	int error;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+	for (;;) {
+		error = get_log_header(ip, *blk, head);
+		if (error <= 0)
+			return error;
+
+		if (++*blk == jd_blocks)
+			*blk = 0;
+
+		if (*blk == orig_blk)
+			return -EIO;
+	}
+}
+
+/**
+ * jhead_scan - make sure we've found the head of the log
+ * @jd: the journal
+ * @head: this is filled in with the log descriptor of the head
+ *
+ * At this point, seg and lh should be either the head of the log or just
+ * before.  Scan forward until we find the head.
+ *
+ * Returns: errno
+ */
+
+int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int blk = head->lh_blkno;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	struct gfs2_log_header lh;
+	int error;
+
+	for (;;) {
+		if (++blk == jd_blocks)
+			blk = 0;
+
+		error = get_log_header(ip, blk, &lh);
+		if (error < 0)
+			return error;
+		if (error == 1)
+			continue;
+
+		if (lh.lh_sequence == head->lh_sequence)
+			return -EIO;
+		if (lh.lh_sequence < head->lh_sequence)
+			break;
+
+		*head = lh;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs2_find_jhead - find the head of a log
+ * @jd: the journal
+ * @head: the log descriptor for the head of the log is returned here
+ *
+ * Do a binary search of a journal and find the valid log entry with the
+ * highest sequence number.  (i.e. the log head)
+ *
+ * Returns: errno
+ */
+
+int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	struct gfs2_log_header lh_1, lh_m;
+	uint32_t blk_1, blk_2, blk_m;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	int error;
+
+	blk_1 = 0;
+	blk_2 = jd_blocks - 1;
+
+	for (;;) {
+		blk_m = (blk_1 + blk_2) / 2;
+
+		error = find_good_lh(ip, &blk_1, &lh_1);
+		if (error)
+			return error;
+
+		error = find_good_lh(ip, &blk_m, &lh_m);
+		if (error)
+			return error;
+
+		if (blk_1 == blk_m || blk_m == blk_2)
+			break;
+
+		if (lh_1.lh_sequence <= lh_m.lh_sequence)
+			blk_1 = blk_m;
+		else
+			blk_2 = blk_m;
+	}
+
+	error = jhead_scan(ip, &lh_1);
+	if (error)
+		return error;
+
+	*head = lh_1;
+
+	return error;
+}
+
+/**
+ * clean_journal - mark a dirty journal as being clean
+ * @sdp: the filesystem
+ * @jd: the journal
+ * @head: the head journal to start from
+ *
+ * Returns: errno
+ */
+
+int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int lblock;
+	struct gfs2_log_header *lh;
+	uint32_t hash, extlen;
+	struct gfs2_buffer_head *bh;
+	int error, new = 0;
+	uint64_t dblock;
+
+	lblock = head->lh_blkno;
+	gfs2_replay_incr_blk(ip, &lblock);
+	block_map(ip, lblock, &new, &dblock, &extlen, 0, not_updated);
+	if (!dblock)
+		return -EIO;
+
+	bh = bread(ip->i_sbd, dblock);
+	memset(bh->b_data, 0, bh->b_size);
+
+	lh = (struct gfs2_log_header *)bh->b_data;
+	memset(lh, 0, sizeof(struct gfs2_log_header));
+	lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+	lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH);
+	lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH);
+	lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1);
+	lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT);
+	lh->lh_blkno = cpu_to_be32(lblock);
+	hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header));
+	lh->lh_hash = cpu_to_be32(hash);
+
+	brelse(bh, updated);
+
+	return error;
+}
+





More information about the Cluster-devel mailing list