Re: [RFC] GSoC idea: implement HFS Plus Journal

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

 



Hi Naohiro,

I noticed Netgear recently released an implementation of HFS Plus Journal.
Please check WNDRMAC-V1.0.0.18_gpl_src/linux/ directory of
ftp://downloads.netgear.com/files/GPL/WNDRMAC-V1.0.0.18_gpl_src.tar.bz2.zip

Attached please find the HFS-related patch when diffing with tag v2.6.15. Maybe
Netgear's implementation can help you implement HFS Plus Journal read/write
support on Linux.

Cheers,

changcs
---

 fs/Kconfig               |   71 +++++
 fs/hfsplus/Makefile      |    3 +-
 fs/hfsplus/extents.c     |   32 ++
 fs/hfsplus/hfsplus_fs.h  |   92 ++++++-
 fs/hfsplus/hfsplus_raw.h |   53 ++++
 fs/hfsplus/inode.c       |   49 +++
 fs/hfsplus/journal.c     |  775 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/hfsplus/super.c       |   87 +++++-
 8 files changed, 1158 insertions(+), 4 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index d5255e6..08af518 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -921,6 +921,12 @@ config HFSPLUS_FS
 	  data forks and creator codes, but it also has several UNIX
 	  style features such as file ownership and permissions.
 
+config HFSPLUS_JOURNAL
+	bool "HFSPLUS journal support"
+	depends on HFSPLUS_FS
+	help
+		Enabling journal support in HFSPlus file system.
+
 config BEFS_FS
 	tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
@@ -1151,6 +1157,71 @@ config CRAMFS
 
 	  If unsure, say N.
 
+config SQUASHFS
+	tristate "SquashFS 3.0 - Squashed file system support"
+	select ZLIB_INFLATE
+	help
+	  Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File
+	  System).  Squashfs is a highly compressed read-only filesystem for Linux.
+	  It uses zlib compression to compress both files, inodes and directories.
+	  Inodes in the system are very small and all blocks are packed to minimise
+	  data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
+	  SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full
+	  uid/gid information, hard links and timestamps.
+
+	  Squashfs is intended for general read-only filesystem use, for archival
+	  use (i.e. in cases where a .tar.gz file may be used), and in embedded
+	  systems where low overhead is needed.  Further information and filesystem tools
+	  are available from http://squashfs.sourceforge.net.
+
+	  If you want to compile this as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called squashfs.  Note that the root file system (the one
+	  containing the directory /) cannot be compiled as a module.
+
+	  If unsure, say N.
+
+config SQUASHFS_EMBEDDED
+
+	bool "Additional options for memory-constrained systems" 
+	depends on SQUASHFS
+	default n
+	help
+	  Saying Y here allows you to specify cache sizes and how Squashfs
+	  allocates memory.  This is only intended for memory constrained
+	  systems.
+
+	  If unsure, say N.
+
+config SQUASHFS_FRAGMENT_CACHE_SIZE
+	int "Number of fragments cached" if SQUASHFS_EMBEDDED
+	depends on SQUASHFS
+	default "3"
+	help
+	  By default SquashFS caches the last 3 fragments read from
+	  the filesystem.  Increasing this amount may mean SquashFS
+	  has to re-read fragments less often from disk, at the expense
+	  of extra system memory.  Decreasing this amount will mean
+	  SquashFS uses less memory at the expense of extra reads from disk.
+
+	  Note there must be at least one cached fragment.  Anything
+	  much more than three will probably not make much difference.
+
+config SQUASHFS_VMALLOC
+	bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
+	depends on SQUASHFS
+	default n
+	help
+	  By default SquashFS uses kmalloc to obtain fragment cache memory.
+	  Kmalloc memory is the standard kernel allocator, but it can fail
+	  on memory constrained systems.  Because of the way Vmalloc works,
+	  Vmalloc can succeed when kmalloc fails.  Specifying this option
+	  will make SquashFS always use Vmalloc to allocate the
+	  fragment cache memory.
+
+	  If unsure, say N.
+
 config VXFS_FS
 	tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
 	help
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
index 3cc0df7..34ed604 100644
--- a/fs/hfsplus/Makefile
+++ b/fs/hfsplus/Makefile
@@ -5,5 +5,4 @@
 obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
 
 hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
-		bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o
-
+		bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o journal.o
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index e3ff56a..1f5b733 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -504,3 +504,35 @@ out:
 	inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << sb->s_blocksize_bits);
 	mark_inode_dirty(inode);
 }
+
+#ifdef CONFIG_HFSPLUS_JOURNAL
+int hfsplus_journaled_get_block(struct page *page)
+{
+	struct inode * const inode = page->mapping->host;
+	struct super_block * const sb = inode->i_sb;
+	u32 ablock, res;
+	sector_t iblock;
+	s32 block_num = -1;
+
+	iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+	ablock = iblock >> HFSPLUS_SB(sb).fs_shift;
+	if (ablock < HFSPLUS_I(inode).first_blocks) {
+		block_num = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock);
+	}
+	else {
+		down(&HFSPLUS_I(inode).extents_lock);
+		res = hfsplus_ext_read_extent(inode, ablock);
+		if (!res) {
+			down(&HFSPLUS_I(inode).extents_lock);
+			block_num = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock -
+                    HFSPLUS_I(inode).cached_start);
+			up(&HFSPLUS_I(inode).extents_lock);
+		} else {
+			up(&HFSPLUS_I(inode).extents_lock);
+		}
+		up(&HFSPLUS_I(inode).extents_lock);
+	}
+
+	return block_num;
+}
+#endif /* CONFIG_HFSPLUS_JOURNAL */
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index df16fcb..c7e5d68 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -21,11 +21,27 @@
 #define DBG_SUPER	0x00000010
 #define DBG_EXTENT	0x00000020
 #define DBG_BITMAP	0x00000040
+#ifdef CONFIG_HFSPLUS_JOURNAL
+#define DBG_JOURNAL	0x00000080
+#define DBG_JREPLAY	0x00000100
+#define DBG_JTRANS	0x00000200
+#endif
 
 //#define DBG_MASK	(DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
 //#define DBG_MASK	(DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE)
 //#define DBG_MASK	(DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT)
-#define DBG_MASK	(0)
+//#define DBG_MASK	(0)
+#ifdef CONFIG_HFSPLUS_JOURNAL
+#define DBG_MASK		(DBG_JOURNAL)
+
+#define HFSPLUS_JOURNAL_PRESENT			1
+#define HFSPLUS_JOURNAL_CONSISTENT		0
+#define HFSPLUS_JOURNAL_INCONSISTENT	1
+#define HFSPLUS_JOURNAL_UIBYTE			0x5A /* Unimportant byte value */
+#define HFSPLUS_JOURNAL_SUCCESS			0
+#define HFSPLUS_JOURNAL_FAIL				1
+#define HFSPLUS_JOURNAL_SWAP				1
+#endif
 
 #define dprint(flg, fmt, args...) \
 	if (flg & DBG_MASK) printk(fmt , ## args)
@@ -98,6 +114,60 @@ struct hfs_bnode {
 #define HFS_BNODE_DIRTY		3
 #define HFS_BNODE_DELETED	4
 
+#ifdef CONFIG_HFSPLUS_JOURNAL
+/* An HFS+ Journal held in memory */
+struct hfsplus_journal;
+
+struct hfsplus_transaction {
+	unsigned char *tbuf; 
+	u32 tbuf_size;
+	struct hfsplus_block_list_header *blhdr;
+	struct hfsplus_block_info *binfo;
+	u32 num_blhdrs;
+	u32 total_bytes;
+	u32 num_flushed;
+	u32 num_killed;
+	u64 sector_number;
+	u64 journal_start;
+	u64 journal_end;
+	u32 sequence_num;
+	struct hfsplus_journal *jnl;
+	struct list_head list;
+};
+
+struct hfsplus_journal {
+	struct semaphore jnl_lock;
+	u32 journaled;
+	u32 flags;
+
+	/* Journal info block specific */
+	struct buffer_head *jib_bh;
+	struct hfsplus_journal_info_block *jibhdr;
+	u64 jib_offset;
+
+	/* Journal header specific */
+	struct buffer_head *jh_bh;
+	u32 jh_bh_size;
+	u64 jh_offset;
+	struct hfsplus_journal_header *jhdr;
+
+	/* Link list of meta-data transaction */
+	struct list_head tr_list;
+
+	/* Pointer to the last transaction */
+	struct hfsplus_transaction *active_tr;
+
+	/* block number of meta-data */
+	u32 ext_block;
+	u32 alloc_block;
+	u32 catalog_block;
+	u32 attr_block;
+
+	struct super_block *sbp;
+	u32 sequence_num;
+};
+#endif /* CONFIG_HFSPLUS_JOURNAL */
+
 /*
  * HFS+ superblock info (built from Volume Header on disk)
  */
@@ -121,6 +191,9 @@ struct hfsplus_sb_info {
 	int fs_shift;
 
 	/* Stuff in host order from Vol Header */
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	struct hfsplus_journal jnl;
+#endif
 	u32 alloc_blksz;
 	int alloc_blksz_shift;
 	u32 total_blocks;
@@ -325,6 +398,9 @@ void hfsplus_file_truncate(struct inode *);
 /* inode.c */
 extern struct address_space_operations hfsplus_aops;
 extern struct address_space_operations hfsplus_btree_aops;
+#ifdef CONFIG_HFSPLUS_JOURNAL
+extern struct address_space_operations hfsplus_journaled_btree_aops;
+#endif
 
 void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *);
 void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *);
@@ -362,6 +438,20 @@ int hfsplus_read_wrapper(struct super_block *);
 
 int hfs_part_find(struct super_block *, sector_t *, sector_t *);
 
+#ifdef CONFIG_HFSPLUS_JOURNAL
+/* journal.c */
+void hfsplus_journaled_init(struct super_block *, struct hfsplus_vh *);
+void hfsplus_journaled_deinit(struct super_block *);
+int hfsplus_journaled_create(struct super_block *);
+int hfsplus_journaled_check(struct super_block *);
+int hfsplus_journaled_start_transaction(struct page *, struct super_block *);
+void hfsplus_journaled_end_transaction(struct page *, struct super_block *);
+void print_volume_header(struct super_block *);
+
+/* extents.c */
+int hfsplus_journaled_get_block(struct page *page);
+#endif /* CONFIG_HFSPLUS_JOURNAL */
+
 /* access macros */
 /*
 static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
index b4fbed6..c5a121c 100644
--- a/fs/hfsplus/hfsplus_raw.h
+++ b/fs/hfsplus/hfsplus_raw.h
@@ -42,6 +42,9 @@
 #define HFSP_HFSPLUS_CREATOR	0x6866732b	/* 'hfs+' */
 
 #define HFSP_MOUNT_VERSION	0x482b4c78	/* 'H+Lx' */
+#ifdef CONFIG_HFSPLUS_JOURNAL
+#define HFSP_MOUNT_JOURNALED_VERSION 0x4846534A
+#endif /* CONFIG_HFSPLUS_JOURNAL */
 
 /* Structures used on disk */
 
@@ -91,7 +94,11 @@ struct hfsplus_vh {
 	__be16 version;
 	__be32 attributes;
 	__be32 last_mount_vers;
+#ifndef CONFIG_HFSPLUS_JOURNAL
 	u32 reserved;
+#else
+	__be32 journal_info_block;
+#endif
 
 	__be32 create_date;
 	__be32 modify_date;
@@ -325,4 +332,50 @@ typedef union {
 	struct hfsplus_ext_key ext;
 } __packed hfsplus_btree_key;
 
+#ifdef CONFIG_HFSPLUS_JOURNAL
+struct hfsplus_journal_info_block {
+	__be32 flags;
+	__be32 device_signature[8];
+	__be64 offset;
+	__be64 size;
+	u32 reserved[32];
+} __packed;
+
+/* Possible values of flags */
+#define HFSPLUS_JOURNAL_IN_FS		0x01
+#define HFSPLUS_JOURNAL_ON_OTHER_DEVICE	0x02
+#define HFSPLUS_JOURNAL_NEED_INIT	0x04
+
+struct hfsplus_journal_header {
+	__be32 magic;
+	__be32 endian;
+	__be64 start;
+	__be64 end;
+	__be64 size; /* This includes the journal header and the journal buffer */
+	__be32 blhdr_size;
+	__be32 checksum;
+	__be32 jhdr_size;
+} __packed;
+
+/* Valid magic and endian value */
+#define HFSPLUS_JOURNAL_HEADER_MAGIC	0x4A4E4C78
+#define HFSPLUS_JOURNAL_HEADER_ENDIAN	0x12345678
+
+struct hfsplus_block_info {
+	__be64 bnum;
+	__be32 bsize;
+	__be32 next;
+} __packed;
+
+struct hfsplus_block_list_header {
+	__be16 max_blocks;
+	__be16 num_blocks;
+	__be32 bytes_used;
+	__be32 checksum;
+	__be32 pad;
+	struct hfsplus_block_info binfo[1];
+} __packed;
+
+#endif /* CONFIG_HFSPLUS_JOURNAL */
+
 #endif
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index fc98583..2e9ee73 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -96,6 +96,30 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask)
 	return res ? try_to_free_buffers(page) : 0;
 }
 
+#ifdef CONFIG_HFSPLUS_JOURNAL
+static int hfsplus_journaled_writepage(struct page *page, struct writeback_control *wbc)
+{
+	int jnl_ret, ret = 0;
+	struct inode * const inode = page->mapping->host;
+	loff_t i_size = i_size_read(inode);
+	const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
+
+   /* Is the page fully inside i_size? */
+   if (page->index < end_index) {
+		jnl_ret = hfsplus_journaled_start_transaction(page, NULL);
+
+		ret = block_write_full_page(page, hfsplus_get_block, wbc);
+
+		if (jnl_ret == HFSPLUS_JOURNAL_SUCCESS && !ret)
+			hfsplus_journaled_end_transaction(page, NULL);
+	}
+	else
+		ret = block_write_full_page(page, hfsplus_get_block, wbc);
+
+	return ret;
+}
+#endif
+
 static int hfsplus_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks,
 			      struct buffer_head *bh_result, int create)
 {
@@ -120,9 +144,34 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb,
 static int hfsplus_writepages(struct address_space *mapping,
 			      struct writeback_control *wbc)
 {
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	struct inode * const inode = mapping->host;
+	int ret;
+	u32 block_num = be32_to_cpu(HFSPLUS_I(inode).first_extents[0].start_block);
+
+	if (block_num == 1)
+		return mpage_writepages(mapping, wbc, NULL);
+	else
+		return mpage_writepages(mapping, wbc, hfsplus_get_block);
+
+	return ret;
+#else
 	return mpage_writepages(mapping, wbc, hfsplus_get_block);
+#endif
 }
 
+#ifdef CONFIG_HFSPLUS_JOURNAL
+struct address_space_operations hfsplus_journaled_btree_aops = {
+	.readpage	= hfsplus_readpage,
+	.writepage	= hfsplus_journaled_writepage,
+	.sync_page	= block_sync_page,
+	.prepare_write	= hfsplus_prepare_write,
+	.commit_write	= generic_commit_write,
+	.bmap		= hfsplus_bmap,
+	.releasepage	= hfsplus_releasepage,
+};
+#endif
+
 struct address_space_operations hfsplus_btree_aops = {
 	.readpage	= hfsplus_readpage,
 	.writepage	= hfsplus_writepage,
diff --git a/fs/hfsplus/journal.c b/fs/hfsplus/journal.c
new file mode 100644
index 0000000..956411a
--- /dev/null
+++ b/fs/hfsplus/journal.c
@@ -0,0 +1,775 @@
+/*
+* HFSPlus journal implementation Tathagata Das 2010
+*/
+
+#ifdef CONFIG_HFSPLUS_JOURNAL
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+
+#include <asm/current.h>
+#include <asm/unaligned.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Calculate chesum of ptr of size len */
+static int calc_checksum(unsigned char *ptr, int len)
+{
+	int i, chksum = 0;
+
+	for (i=0; i<len; i++, ptr++)
+		chksum = (chksum << 8) ^ (chksum + *ptr);
+
+	return (~chksum);
+}
+
+static void swap_block_list_header(struct hfsplus_block_list_header *blhdr)
+{
+	int i;
+
+	blhdr->num_blocks = swab16(blhdr->num_blocks);
+	blhdr->bytes_used = swab32(blhdr->bytes_used);
+	blhdr->checksum = swab32(blhdr->checksum);
+
+	for (i=1; i<blhdr->num_blocks; i++) {
+		blhdr->binfo[i].bnum = swab64(blhdr->binfo[i].bnum);
+		blhdr->binfo[i].bsize = swab32(blhdr->binfo[i].bsize);
+	}
+}
+
+static void swap_journal_header(struct hfsplus_journal_header *jh)
+{
+	jh->magic      = swab32(jh->magic);
+	jh->endian     = swab32(jh->endian);
+	jh->start      = swab64(jh->start);
+	jh->end        = swab64(jh->end);
+	jh->size       = swab64(jh->size);
+	jh->blhdr_size = swab32(jh->blhdr_size);
+	jh->checksum   = swab32(jh->checksum);
+	jh->jhdr_size  = swab32(jh->jhdr_size);
+}
+
+void print_volume_header(struct super_block *sb)
+{
+	int i;
+	unsigned char *vh_ptr = (unsigned char *)HFSPLUS_SB(sb).s_vhdr;
+
+	dprint(DBG_JOURNAL, "VOLUME HEADER\n");
+	for (i=0; i<102; i++)
+		dprint(DBG_JOURNAL, "%x ", vh_ptr[i]);
+	dprint(DBG_JOURNAL, "\n");
+}
+
+static void print_journal_header(struct hfsplus_journal_header *jh)
+{
+	dprint(DBG_JOURNAL, "HFS+-fs: magic: %x\n endian: %x\n start: %llx\n end: %llx\n size: %llx\n blhdr_size: %x\n checksum: %x\n jhdr_size: %x\n", jh->magic, jh->endian, jh->start, jh->end, jh->size, jh->blhdr_size, jh->checksum, jh->jhdr_size);
+}
+
+static int map_journal_header(struct super_block *sb)
+{
+	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
+	u32 jh_block_number;
+
+	jnl->jh_offset = be64_to_cpu(jnl->jibhdr->offset);
+	jh_block_number = jnl->jh_offset >> sb->s_blocksize_bits;
+	dprint(DBG_JOURNAL, "HFS+-fs: jh_block_number: %x\n", jh_block_number);
+	jnl->jh_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jh_block_number);
+	if (!jnl->jh_bh) {
+		printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+	jnl->jhdr = (struct hfsplus_journal_header *)(jnl->jh_bh->b_data);
+
+	return HFSPLUS_JOURNAL_SUCCESS;
+}
+
+/* Write journal header during replay */
+static int hfsplus_replay_write_journal_header(struct super_block *sb)
+{
+	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
+
+	if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP) {
+		swap_journal_header(jh);
+		jh->checksum = 0;
+		jh->checksum = swab32(calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)));
+	}
+	else {
+		jh->checksum = 0;
+		jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
+	}
+
+	/* Write it to disk */
+	mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh);
+	sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jh_bh);
+
+	if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP)
+		swap_journal_header(jh);
+
+	return HFSPLUS_JOURNAL_SUCCESS;
+}
+
+static int hfsplus_write_journal_header(struct super_block *sb)
+{
+	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
+
+	jh->checksum = 0;
+	jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
+
+	/* Write it to disk */
+	mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh);
+
+	return HFSPLUS_JOURNAL_SUCCESS;
+}
+
+/* Create journal header, journal buffer and initialize them 
+ * Assume that presence of journal is already verified
+*/
+int hfsplus_journaled_create(struct super_block *sb)
+{
+	struct hfsplus_journal_header *jhdr; 
+	u64 jibsize = be64_to_cpu(HFSPLUS_SB(sb).jnl.jibhdr->offset);
+
+	dprint(DBG_JOURNAL, "sb->s_blocksize: %lx, jibsize: %llx\n", sb->s_blocksize, jibsize);
+
+	/* Journal size is not aligned */
+	if (((jibsize >> sb->s_blocksize_bits) << sb->s_blocksize_bits) != jibsize) {
+		printk("HFS+-fs: journal size is not aligned\n");
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+
+	if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) {
+		printk("HFS+-fs: Error in mapping journal header\n");
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+
+	jhdr = (struct hfsplus_journal_header *)HFSPLUS_SB(sb).jnl.jhdr;
+
+	/* Populate journal header and write it to the disk */
+	jhdr->magic = HFSPLUS_JOURNAL_HEADER_MAGIC;
+	jhdr->endian = HFSPLUS_JOURNAL_HEADER_ENDIAN;
+	jhdr->start = sb->s_blocksize; /* First block is for journal header itself */
+	jhdr->end = sb->s_blocksize; /* Initially journal buffer is empty */
+	jhdr->size = jibsize;
+	jhdr->blhdr_size = sb->s_blocksize;
+	jhdr->jhdr_size = sb->s_blocksize; /* Assign first block for journal header */
+
+	if (jhdr->start != jhdr->end) {
+		printk("HFS+-fs: hfsplus_write_journal_header fail: Journal is not empty\n");
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+
+	return hfsplus_write_journal_header(sb);
+}
+
+/* Allocate block list header for a new transaction.
+ * Assume that journal header is already initialized.
+*/
+static int hfsplus_journaled_write_transaction(struct super_block *sb, struct hfsplus_journal *jnl, struct page *page, void *vbuf, u64 sector_num, u32 bufsize)
+{
+	struct hfsplus_transaction *tr;
+	u32 total_size = jnl->jhdr->blhdr_size + bufsize, tr_sector_number, *tr_buf, i;
+	u64 tr_offset;
+	struct buffer_head *tr_bh = NULL;
+
+	/* Total size should be mulitple of sector size */
+	if (((total_size >> HFSPLUS_SECTOR_SHIFT) << HFSPLUS_SECTOR_SHIFT) != total_size) {
+      printk("HFS+-fs: total size is not aligned\n");
+      return HFSPLUS_JOURNAL_FAIL;
+   }
+
+	tr = kmalloc(sizeof(struct hfsplus_transaction), GFP_KERNEL);
+	if (tr == NULL) {
+		printk("HFS+-fs: No memory of size %#x\n", sizeof(struct hfsplus_transaction));
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+
+	INIT_LIST_HEAD(&tr->list);
+
+	/* Allocate memory for block list header, one block info and one data block of bufsize */
+	tr->tbuf = kmalloc(total_size, GFP_KERNEL);
+	if (tr->tbuf == NULL) {
+		printk("HFS+-fs: No memory of size: %#x\n", total_size);
+		goto tbuf_alloc_fail;
+	}
+	memset(tr->tbuf, 0, sizeof(struct hfsplus_block_list_header));
+	/* Initialize the buffer (except block list header) with unimportant bytes */
+	memset(tr->tbuf + sizeof(struct hfsplus_block_list_header), HFSPLUS_JOURNAL_UIBYTE, total_size - sizeof(struct hfsplus_block_list_header)); 
+
+	/* Populate block list header */
+	tr->blhdr = (struct hfsplus_block_list_header *)tr->tbuf;
+	tr->blhdr->max_blocks = (jnl->jhdr->blhdr_size / sizeof(struct hfsplus_block_info)) - 1;
+	tr->blhdr->num_blocks = 2;      /* One is for header and another is for the data */
+	tr->blhdr->bytes_used = total_size;
+	tr->blhdr->binfo[0].next = 0;
+
+	/* Populate second block info */
+	tr->binfo = (struct hfsplus_block_info *)(tr->tbuf + sizeof(struct hfsplus_block_list_header));
+	tr->binfo->bnum = sector_num;
+	tr->binfo->bsize = bufsize;
+	tr->binfo->next = 0;
+
+	if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP) {
+		tr->binfo->bnum = swab64(tr->binfo->bnum);
+		tr->binfo->bsize = swab32(tr->binfo->bsize);
+		tr->blhdr->max_blocks = swab16(tr->blhdr->max_blocks);
+		tr->blhdr->num_blocks = swab16(tr->blhdr->num_blocks);
+		tr->blhdr->bytes_used = swab32(tr->blhdr->bytes_used);
+		tr->blhdr->checksum = 0;
+		tr->blhdr->checksum = swab32(calc_checksum((unsigned char *)tr->blhdr, sizeof(struct hfsplus_block_list_header)));
+	}
+	else {
+		tr->blhdr->checksum = 0;
+		tr->blhdr->checksum = calc_checksum((unsigned char *)tr->blhdr, sizeof(struct hfsplus_block_list_header));
+	}
+
+	/* Copy actual meta-data */
+	if (page != NULL) {
+		void *pbuf = kmap(page);
+		if (pbuf) {
+			memcpy(tr->tbuf + jnl->jhdr->blhdr_size, pbuf, bufsize);
+			kunmap(pbuf);
+		}
+		else {
+			printk("HFS+-fs Line=%d: Error in kmap\n", __LINE__);
+			goto tr_bh_alloc_fail;
+		}
+	}
+	else
+		memcpy(tr->tbuf + jnl->jhdr->blhdr_size, (unsigned char *)vbuf, bufsize);
+
+	/* Write transaction into the disk */
+	tr_offset = jnl->jhdr->end + jnl->jh_offset;
+	for (i=0; i < total_size / HFSPLUS_SECTOR_SIZE; i++) {
+		tr_sector_number = tr_offset >> HFSPLUS_SECTOR_SHIFT;
+		dprint(DBG_JTRANS, "tr_offset: %llx, tr_sector_number: %x\n", tr_offset, tr_sector_number);
+
+		tr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + tr_sector_number, tr_buf);
+		if (tr_bh == NULL) {
+			printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
+			goto tr_bh_alloc_fail;
+		}
+
+		memcpy(tr_buf, tr->tbuf + i*HFSPLUS_SECTOR_SIZE, HFSPLUS_SECTOR_SIZE);
+		mark_buffer_dirty(tr_bh);
+#if 0
+		sync_dirty_buffer(tr_bh);
+#endif
+
+		/* Free buffer heads */
+		brelse(tr_bh);
+
+		tr_offset += HFSPLUS_SECTOR_SIZE;
+
+		/* Check tr_offset reaches at the end of journal buffer */
+		if (tr_offset == (jnl->jh_offset + jnl->jhdr->size)) {
+			dprint(DBG_JTRANS, "tr_offset: %llx, jnl->jhdr->size: %llx, jh_offset: %llx\n", tr_offset, jnl->jhdr->size, jnl->jh_offset);
+			tr_offset = jnl->jh_offset + jnl->jhdr->jhdr_size; /* Set to the beginning of the journal buffer */
+		}
+	}
+
+	tr->journal_start = jnl->jhdr->start;
+	tr->journal_end = tr_offset - jnl->jh_offset;
+	tr->sequence_num = ++jnl->sequence_num;
+	tr->num_blhdrs  = 1;
+	tr->total_bytes = total_size;
+	tr->jnl         = jnl;
+	tr->tbuf_size = bufsize; 
+	tr->sector_number = sector_num;
+	dprint(DBG_JTRANS, "end: %llx, start: %llx, sector_number: %llx\n", tr->journal_start, tr->journal_end, tr->sector_number);
+
+	kfree(tr->tbuf);
+	tr->tbuf = NULL;
+
+	jnl->active_tr = tr;
+
+	list_add_tail(&tr->list, &jnl->tr_list);
+
+	return HFSPLUS_JOURNAL_SUCCESS;
+
+tr_bh_alloc_fail:
+	kfree(tr->tbuf);
+	tr->tbuf = NULL;
+tbuf_alloc_fail:
+	list_del(&tr->list);
+	kfree(tr);
+	tr = NULL;
+	return HFSPLUS_JOURNAL_FAIL;
+}
+
+/* Write a transaction into journal buffer before writing it to its original location. 
+*/
+int hfsplus_journaled_start_transaction(struct page *page, struct super_block *sbp)
+{
+	struct inode *inode = NULL;
+	struct super_block *sb = NULL;
+	s32 block_num = -1, ret = HFSPLUS_JOURNAL_FAIL, bufsize = 0;
+	u64 sector_num = 0, tr_size;
+	u32 total_size;
+	struct hfsplus_journal *jnl;
+
+	dprint(DBG_JTRANS, "Entering into %s()\n", __FUNCTION__);
+
+	if (sbp == NULL) {
+		inode = page->mapping->host;
+		sb = inode->i_sb;
+	} else
+		sb = sbp;
+
+	jnl =	&HFSPLUS_SB(sb).jnl;
+	if (jnl->journaled != HFSPLUS_JOURNAL_PRESENT) {
+		dprint(DBG_JTRANS, "%s: Not a journaled volume, return\n", __func__);
+		return HFSPLUS_JOURNAL_SUCCESS;
+	}
+
+	down(&jnl->jnl_lock);
+
+	/* Write one block into the journal log.
+	 * Find out the correct transaction for this block and
+	 * add that block into that block list header.
+	*/
+	if (sbp == NULL) {
+		block_num = hfsplus_journaled_get_block(page);
+		if (block_num == -1) {
+			printk("HFS+-fs: Error in getting block for page index: %lx\n", page->index);
+			up(&jnl->jnl_lock);
+			return HFSPLUS_JOURNAL_FAIL;
+		}
+
+		/* Set sector number and buffer size
+	 	* Check with Allocation, Extent, Catalog and Attribute file.
+		* This is compared in order of the fequency of their occurance.
+		*/
+		dprint(DBG_JTRANS, "Need to write block number: %x to journal log\n", block_num);
+		if ((block_num >= jnl->catalog_block) && (block_num <= jnl->catalog_block + HFSPLUS_I(inode).first_blocks)) {
+			sector_num = (block_num * sb->s_blocksize) / HFSPLUS_SECTOR_SIZE;
+			bufsize = PAGE_SIZE;
+		} else if ((block_num >= jnl->alloc_block) && (block_num <= jnl->alloc_block + HFSPLUS_I(inode).first_blocks)) {
+			sector_num = (block_num * sb->s_blocksize) / HFSPLUS_SECTOR_SIZE;
+			bufsize = PAGE_SIZE;
+		} else if ((block_num >= jnl->ext_block) && (block_num <= jnl->ext_block + HFSPLUS_I(inode).first_blocks)) {
+			sector_num = (block_num * sb->s_blocksize) / HFSPLUS_SECTOR_SIZE;
+			bufsize = PAGE_SIZE;
+		} else if ((block_num >= jnl->attr_block) && (block_num <= jnl->attr_block + HFSPLUS_I(inode).first_blocks)) {
+			sector_num = (block_num * sb->s_blocksize) / HFSPLUS_SECTOR_SIZE;
+			bufsize = PAGE_SIZE;
+		}
+	} else {
+	/* Must be Volume Header */
+		sector_num = HFSPLUS_VOLHEAD_SECTOR;
+		bufsize = HFSPLUS_SECTOR_SIZE;
+	}
+
+	dprint(DBG_JTRANS, "sector number: %llx, bufsize: %x\n", sector_num, bufsize);
+	if (sector_num == 0 || bufsize == 0) {
+		printk("HFS+-fs: Wrong sector number: %llx or buffer size: %x. Block number: %x\n", sector_num, bufsize, block_num);
+		up(&jnl->jnl_lock);
+		return HFSPLUS_JOURNAL_FAIL;
+	}
+
+	/* Check space in journal log for new transaction */
+	total_size = jnl->jhdr->blhdr_size + bufsize;
+	if (jnl->jhdr->end > jnl->jhdr->start)
+		tr_size = jnl->jhdr->end - jnl->jhdr->start;
+	else
+		tr_size = jnl->jhdr->start - jnl->jhdr->end;
+
+	if ((tr_size + (u64)total_size) > (jnl->jhdr->size - (u64)jnl->jhdr->jhdr_size)) {
+		/* TODO: Free some memory from journal buffer */
+		printk("Not enough free memory for writing this transaction\n");
+		up(&jnl->jnl_lock);
+		return HFSPLUS_JOURNAL_FAIL;
+	} 
+
+	/* Prepare and write buffer for this transaction */
+	if (sbp == NULL) {
+		ret = hfsplus_journaled_write_transaction(sb, jnl, page, NULL, sector_num, bufsize);
+	} else
+		ret = hfsplus_journaled_write_transaction(sb, jnl, NULL, HFSPLUS_SB(sb).s_vhdr, sector_num, bufsize);
+	if (ret == HFSPLUS_JOURNAL_FAIL) {
+		printk("HFS+-fs: Error in hfsplus_journaled_write_transaction\n");
+		up(&jnl->jnl_lock);
+		return ret;
+	}
+
+	jnl->jhdr->end = jnl->active_tr->journal_end;
+	ret = hfsplus_write_journal_header(sb);
+
+	dprint(DBG_JTRANS, "HFS+-fs: New transaction number: %d\n", jnl->sequence_num);
+
+	up(&jnl->jnl_lock);
+	return ret;
+}
+
+void hfsplus_journaled_end_transaction(struct page *page, struct super_block *sbp)
+{
+	struct inode *inode;
+	struct super_block *sb;
+	struct hfsplus_journal *jnl;
+	struct list_head *liter = NULL, *tliter = NULL;
+
+	dprint(DBG_JTRANS, "Entering into %s()\n", __FUNCTION__);
+
+	if (sbp == NULL) {
+		inode = page->mapping->host;
+		sb = inode->i_sb;
+	} else
+		sb = sbp;
+
+	jnl =	&HFSPLUS_SB(sb).jnl;
+	if (jnl->journaled != HFSPLUS_JOURNAL_PRESENT) {
+		dprint(DBG_JOURNAL, "%s: Not a journaled volume, return\n", __func__);
+		return;
+	}
+
+	down(&jnl->jnl_lock);
+
+	/* FIXME: Remove the oldest transaction.
+	 * Assuming that start and end transactions are called in same order of transaction.
+	 */
+	list_for_each_safe(liter, tliter, &jnl->tr_list) {
+		struct hfsplus_transaction *tr = list_entry(liter, struct hfsplus_transaction, list);
+		dprint(DBG_JTRANS, "start: %llx, end: %llx, sequence_num: %d, sector_num: %llx\n", tr->journal_start, tr->journal_end, tr->sequence_num, tr->sector_number);  
+		jnl->jhdr->start = tr->journal_end;
+		hfsplus_write_journal_header(sb);
+		list_del(&tr->list);
+		kfree(tr);
+		break;
+	}
+
+	up(&jnl->jnl_lock);
+
+	return;
+}
+
+/* If the journal consists transaction then write them to disk.
+ * Return success if it brings the file system into consistent state.
+ * Otherwise return fail.
+*/
+static int hfsplus_journal_replay(struct super_block *sb)
+{
+	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
+	struct buffer_head *blhdr_bh = NULL, *tr_bh = NULL, *disk_bh = NULL;
+	struct hfsplus_block_list_header *blhdr;
+	u32 start_sector_number, tr_sector_number, disk_sector_number, i, ret = HFSPLUS_JOURNAL_FAIL; 
+	u64 tr_offset, disk_offset;
+	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
+	unsigned char *tr_buf, *disk_buf;
+	__be32 bufsize;
+
+	if (jh->start == jh->end) {
+		dprint(DBG_JREPLAY, "HFS+-fs: Journal is empty, nothing to replay\n");
+		ret = hfsplus_replay_write_journal_header(sb);
+		return ret;
+	}
+
+	if ((jh->start > jh->size) || (jh->end > jh->size)) {
+		printk("HFS+-fs: Wrong start or end offset, start: %llx, end: %llx, jh_offset: %llx, size: %llx\n", jh->start, jh->end, jnl->jh_offset, jh->size);
+		return ret;
+	}
+
+	if (jh->start == jh->size)
+		jh->start = jh->jhdr_size; 
+
+	down(&jnl->jnl_lock);
+	/* Go through each transaction */
+	while (jh->start != jh->end) {
+		if (blhdr_bh)
+			brelse(blhdr_bh);
+
+		start_sector_number = (jh->start + jnl->jh_offset) >> HFSPLUS_SECTOR_SHIFT;
+		dprint(DBG_JREPLAY, "start: %llx, start_sector_number: %x\n", jh->start, start_sector_number);
+		/* TODO: Wrap around */
+		blhdr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + start_sector_number, blhdr);
+		if (!blhdr_bh) {
+			printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
+			up(&jnl->jnl_lock);
+			return ret;
+		}
+
+		if (jnl->flags == HFSPLUS_JOURNAL_SWAP)
+			swap_block_list_header(blhdr);
+
+		dprint(DBG_JREPLAY, "HFS+-fs: num_blocks: %x, bytes_used: %x\n", blhdr->num_blocks, blhdr->bytes_used);
+		/* Point to the second block in the Volume, first block is already in block list header */
+		tr_offset = jnl->jh_offset + jh->start + jh->blhdr_size;
+
+		for (i=1; i<blhdr->num_blocks; i++) {
+			bufsize = blhdr->binfo[i].bsize;
+			disk_offset = blhdr->binfo[i].bnum << HFSPLUS_SECTOR_SHIFT;
+
+			dprint(DBG_JREPLAY, "[i:%x] bnum: %llx, bsize: %x, bufsize: %x\n", i, blhdr->binfo[i].bnum, blhdr->binfo[i].bsize, bufsize);
+
+			while (bufsize > 0) {
+				/* Read one block */
+				tr_sector_number = tr_offset >> HFSPLUS_SECTOR_SHIFT; 
+				dprint(DBG_JREPLAY, "[i:%x] tr_sector_number: %x, tr_offset: %llx\n", i, tr_sector_number, tr_offset); 
+				tr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + tr_sector_number, tr_buf);
+				if (!tr_bh) {
+					printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
+					if (blhdr_bh)
+						brelse(blhdr_bh);
+					up(&jnl->jnl_lock);
+					return ret;
+				}
+
+				disk_sector_number = disk_offset >> HFSPLUS_SECTOR_SHIFT;
+				dprint(DBG_JREPLAY, "[i:%x] disk_sector_number: %x, disk_offset: %llx, bufsize: %x\n", i, disk_sector_number, disk_offset, bufsize); 
+				/* Read the same sector from the Volume */
+				disk_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + disk_sector_number, disk_buf);
+				if (!disk_bh) {
+					printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
+					if (blhdr_bh)
+						brelse(blhdr_bh);
+					if (tr_bh)
+						brelse(tr_bh);
+					up(&jnl->jnl_lock);
+					return ret;
+				}
+
+				/* Write transaction block to the disk block in sector wise */
+				memcpy(disk_buf, tr_buf, HFSPLUS_SECTOR_SIZE);
+				mark_buffer_dirty(disk_bh);
+				sync_dirty_buffer(disk_bh);
+
+				/* Free buffer heads */
+				brelse(disk_bh);
+				brelse(tr_bh);
+
+				tr_offset += HFSPLUS_SECTOR_SIZE;
+				disk_offset += HFSPLUS_SECTOR_SIZE;
+				bufsize -= HFSPLUS_SECTOR_SIZE;
+
+				/* Check tr_offset reaches at the end of journal buffer */
+				if (tr_offset == (jnl->jh_offset + jh->size)) {
+					printk("tr_offset: %llx, jh->size: %llx, jh_offset: %llx\n", tr_offset, jh->size, jnl->jh_offset);
+					tr_offset = jnl->jh_offset + jh->jhdr_size; /* Set to the beginning of journal buffer */
+				}
+			}
+		}
+
+		/* Check position of start index, wrap around if necessary */
+		if ((jh->start + blhdr->bytes_used) >= jh->size) {
+			printk("start: %llx, jh->size: %llx, blhdr->bytes_used: %x\n", jh->start, jh->size, blhdr->bytes_used);
+			jh->start = jh->jhdr_size + (jh->start + blhdr->bytes_used) - jh->size;
+		} else 
+			jh->start += blhdr->bytes_used;
+	}
+
+	if (blhdr_bh)
+		brelse(blhdr_bh);
+
+	if (jh->start == jh->end) {
+		ret = hfsplus_replay_write_journal_header(sb);
+	} else {
+		printk("HFS+-fs: %s Error in journal replay\n", __func__);
+	}
+
+	/* Populate Volume Header with new values */
+	if (ret == HFSPLUS_JOURNAL_SUCCESS && HFSPLUS_SB(sb).s_vhdr) {
+		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+		struct buffer_head *bh;
+
+		dprint(DBG_JREPLAY, "Populate Volume Header again\n");
+		HFSPLUS_SB(sb).s_vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
+		mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+		sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
+
+		if (HFSPLUS_SB(sb).s_vhbh)
+			brelse(HFSPLUS_SB(sb).s_vhbh);
+
+		bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + HFSPLUS_VOLHEAD_SECTOR, vhdr);
+		if (!bh) {
+			printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
+			HFSPLUS_SB(sb).s_vhdr = NULL;
+			up(&jnl->jnl_lock);
+			return HFSPLUS_JOURNAL_FAIL;
+		}
+
+		/* should still be the same... */
+		if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) {
+			printk("Volume header signature (%x) is wrong\n", be16_to_cpu(vhdr->signature));
+			brelse(bh);
+			HFSPLUS_SB(sb).s_vhdr = NULL;
+			up(&jnl->jnl_lock);
+			return HFSPLUS_JOURNAL_FAIL;
+		}
+
+		HFSPLUS_SB(sb).s_vhbh = bh;
+		HFSPLUS_SB(sb).s_vhdr = vhdr;
+	}
+
+	up(&jnl->jnl_lock);
+	return ret;
+}
+
+/* Check consistency of journal log file in hfsplus volume 
+*/
+int hfsplus_journaled_check(struct super_block *sb)
+{
+	struct hfsplus_journal_info_block *jib;
+	struct hfsplus_journal_header *jh;
+	u32 checksum, org_checksum;
+
+	print_volume_header(sb);
+
+	if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) {
+		printk("HFS+-fs: Journal is not present\n");
+		return HFSPLUS_JOURNAL_CONSISTENT;
+	}
+
+	jib = (struct hfsplus_journal_info_block *)(HFSPLUS_SB(sb).jnl.jibhdr);
+	dprint(DBG_JOURNAL, "HFS+-fs: be32_to_cpu(jib->flags): %x\n", be32_to_cpu(jib->flags));
+
+	/* Journal is on another volume, and the "on this volume" flag
+	* isn't set 
+	*/
+	if(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_ON_OTHER_DEVICE &&
+		!(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_IN_FS)) {
+		printk("HFS+-fs: Unable to access the journal.\n");
+		return HFSPLUS_JOURNAL_INCONSISTENT;
+	}
+
+	/* Journal should be created in initialization. 
+	* Mark inconsistent if the journal is still not created yet 
+	*/
+	if (be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_NEED_INIT) {
+		printk("HFS+-fs: Error, journal is not created\n");
+		return HFSPLUS_JOURNAL_INCONSISTENT;
+	}
+
+	dprint(DBG_JOURNAL, "HFS+-fs: Found Info Block and verified successfully.\n");
+	jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
+
+	org_checksum = jh->checksum;
+	jh->checksum = 0;
+
+	if (jh->magic == swab32(HFSPLUS_JOURNAL_HEADER_MAGIC)) {
+		org_checksum = swab32(org_checksum);
+		checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
+		swap_journal_header(jh);
+		HFSPLUS_SB(sb).jnl.flags = HFSPLUS_JOURNAL_SWAP;
+	}
+	else
+		checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
+
+	print_journal_header(jh);
+
+	/* Verify the journal header */
+	if(jh->magic != HFSPLUS_JOURNAL_HEADER_MAGIC || jh->endian != HFSPLUS_JOURNAL_HEADER_ENDIAN){
+		printk("HFS+-fs: Journal header verification failed.\n");
+		return HFSPLUS_JOURNAL_INCONSISTENT;
+	}
+
+	if (checksum != org_checksum) {
+		jh->checksum = checksum;
+		printk("HFS+-fs: Error in journal header checksum checksum: %x, org_checksum: %x\n", checksum, org_checksum);
+		return HFSPLUS_JOURNAL_INCONSISTENT;
+	}
+	jh->checksum = checksum;
+
+	dprint(DBG_JOURNAL, "HFS+-fs: No problem in magic number, endian and checksum\n");
+
+	/* Compare start to end */
+	if(jh->start == jh->end) {
+		/* If they're the same, we can mount, it's clean */
+		printk("HFS+-fs: Journal is empty means consistent\n");
+		return HFSPLUS_JOURNAL_CONSISTENT;
+	} else {
+		/* Replay journal and bring the file system in consistent state */
+		if (hfsplus_journal_replay(sb) == HFSPLUS_JOURNAL_FAIL) {
+			/* Unable to replay */
+			printk("HFS+-fs: Journal is non empty means inconsistent, please run fsck.hfsplus\n");
+			return HFSPLUS_JOURNAL_INCONSISTENT;
+		} else
+			dprint(DBG_JOURNAL, "HFS+-fs: Journal replay done\n");
+	}
+
+	return HFSPLUS_JOURNAL_CONSISTENT;
+}
+
+/* Check journal present or not and initialize hfsplus_journal accordingly 
+ * Assume that super block and volume header are already initialized
+*/
+void hfsplus_journaled_init(struct super_block *sb, struct hfsplus_vh *vhdr)
+{
+	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
+	u32 jib_flags;
+
+	jnl->journaled = !HFSPLUS_JOURNAL_PRESENT; /* Initialize as non-journaled */
+	jnl->sbp = NULL;
+	jnl->jh_bh = NULL;
+	jnl->alloc_block = be32_to_cpu(vhdr->alloc_file.extents[0].start_block);
+	jnl->ext_block = be32_to_cpu(vhdr->ext_file.extents[0].start_block);
+	jnl->catalog_block = be32_to_cpu(vhdr->cat_file.extents[0].start_block);
+	dprint(DBG_JOURNAL, "alloc_block: %x, ext_block: %x, catalog_block: %x\n", jnl->alloc_block, jnl->ext_block, jnl->catalog_block);
+
+	if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+		dprint(DBG_JOURNAL,"HFS+-fs: Journaled filesystem\n");
+		jnl->jib_offset = be32_to_cpu(vhdr->journal_info_block);
+		/* Check the journal info block to find the block # of the journal */
+		jnl->jib_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jnl->jib_offset);
+		if (!jnl->jib_bh) {
+			printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
+			return;
+		}
+		jnl->jibhdr = (struct hfsplus_journal_info_block *)(jnl->jib_bh->b_data);
+		jib_flags = be32_to_cpu(jnl->jibhdr->flags);
+		dprint(DBG_JOURNAL, "HFS+-fs: jib_flags: %x\n", jib_flags);
+		if ((jib_flags & HFSPLUS_JOURNAL_ON_OTHER_DEVICE) && !(jib_flags & HFSPLUS_JOURNAL_IN_FS))
+			goto init_fail;
+ 
+		if (jib_flags & HFSPLUS_JOURNAL_NEED_INIT) {
+			dprint(DBG_JOURNAL, "HFS+-fs: Journal is not created\n");
+			if (hfsplus_journaled_create(sb) == 0) {
+				HFSPLUS_SB(sb).jnl.jibhdr->flags &= be32_to_cpu(~HFSPLUS_JOURNAL_NEED_INIT);
+				/* write it to disk */
+				mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jib_bh);
+				sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jib_bh);
+			} else {
+				printk("HFS+-fs: Fail to create journal\n");
+				goto init_fail;
+			}
+		}
+
+		/* Check already initialize in journal create */
+		if (jnl->jh_bh == NULL) {
+			if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) {
+				printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
+				goto init_fail; 
+			}
+		}
+
+		jnl->sequence_num = 0;
+		init_MUTEX(&jnl->jnl_lock);
+		INIT_LIST_HEAD(&jnl->tr_list);
+		jnl->sbp = sb;
+		jnl->flags = !HFSPLUS_JOURNAL_SWAP;
+		jnl->journaled = HFSPLUS_JOURNAL_PRESENT;
+	}
+
+	return;
+
+init_fail:
+	printk("HFS+-fs: Journal initialization fails\n");
+	if (jnl->jib_bh)
+		brelse(jnl->jib_bh);
+}
+
+/* Deinitialize journal if it is present */
+void hfsplus_journaled_deinit(struct super_block *sb)
+{
+	if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) {
+		return;
+	}
+
+	hfsplus_journal_replay(sb);
+
+	if (HFSPLUS_SB(sb).jnl.jib_bh)
+		brelse(HFSPLUS_SB(sb).jnl.jib_bh);
+
+	if (HFSPLUS_SB(sb).jnl.jh_bh)
+		brelse(HFSPLUS_SB(sb).jnl.jh_bh);
+}
+#endif /* CONFIG_HFSPLUS_JOURNAL */
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 8093351..33887a0 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -68,10 +68,20 @@ static void hfsplus_read_inode(struct inode *inode)
 		goto read_inode;
 	case HFSPLUS_EXT_CNID:
 		hfsplus_inode_read_fork(inode, &vhdr->ext_file);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+			inode->i_mapping->a_ops = &hfsplus_journaled_btree_aops;
+		} else
+#endif
 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
 		break;
 	case HFSPLUS_CAT_CNID:
 		hfsplus_inode_read_fork(inode, &vhdr->cat_file);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+			inode->i_mapping->a_ops = &hfsplus_journaled_btree_aops;
+		} else
+#endif
 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
 		break;
 	case HFSPLUS_ALLOC_CNID:
@@ -83,6 +93,11 @@ static void hfsplus_read_inode(struct inode *inode)
 		break;
 	case HFSPLUS_ATTR_CNID:
 		hfsplus_inode_read_fork(inode, &vhdr->attr_file);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+			inode->i_mapping->a_ops = &hfsplus_journaled_btree_aops;
+		} else
+#endif
 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
 		break;
 	default:
@@ -207,6 +222,9 @@ static void hfsplus_write_super(struct super_block *sb)
 
 static void hfsplus_put_super(struct super_block *sb)
 {
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	int jnl_ret;
+#endif
 	dprint(DBG_SUPER, "hfsplus_put_super\n");
 	if (!sb->s_fs_info)
 		return;
@@ -216,14 +234,25 @@ static void hfsplus_put_super(struct super_block *sb)
 		vhdr->modify_date = hfsp_now2mt();
 		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
 		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+		jnl_ret = hfsplus_journaled_start_transaction(NULL, sb);
+#endif
 		mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
 		sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+		if (jnl_ret == HFSPLUS_JOURNAL_SUCCESS)
+			hfsplus_journaled_end_transaction(NULL, sb);
+#endif
 	}
 
 	hfs_btree_close(HFSPLUS_SB(sb).cat_tree);
 	hfs_btree_close(HFSPLUS_SB(sb).ext_tree);
 	iput(HFSPLUS_SB(sb).alloc_file);
 	iput(HFSPLUS_SB(sb).hidden_dir);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	hfsplus_journaled_deinit(sb);
+	if (HFSPLUS_SB(sb).s_vhbh)
+#endif
 	brelse(HFSPLUS_SB(sb).s_vhbh);
 	if (HFSPLUS_SB(sb).nls)
 		unload_nls(HFSPLUS_SB(sb).nls);
@@ -259,20 +288,35 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
 			return -EINVAL;
 
 		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+#if 0
 			printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
 			       "running fsck.hfsplus is recommended.  leaving read-only.\n");
 			sb->s_flags |= MS_RDONLY;
 			*flags |= MS_RDONLY;
-		} else if (sbi.flags & HFSPLUS_SB_FORCE) {
+#else
+			printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+					"running fsck.hfsplus is recommended.\n");
+#endif
+		} else 
+		if (sbi.flags & HFSPLUS_SB_FORCE) {
 			/* nothing */
 		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
 			printk("HFS+-fs: Filesystem is marked locked, leaving read-only.\n");
 			sb->s_flags |= MS_RDONLY;
 			*flags |= MS_RDONLY;
 		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+#ifndef CONFIG_HFSPLUS_JOURNAL /* TODO: place of this function should be above unmount check */
 			printk("HFS+-fs: Filesystem is marked journaled, leaving read-only.\n");
 			sb->s_flags |= MS_RDONLY;
 			*flags |= MS_RDONLY;
+#else
+			if (hfsplus_journaled_check(sb)) {
+				printk("HFS+-fs: Filesystem is marked journaled, leaving read-only.\n");
+				sb->s_flags |= MS_RDONLY;
+				*flags |= MS_RDONLY;
+			} else
+				printk("HFS+-fs: Able to mount journaled hfsplus volume in read-write mode\n");
+#endif
 		}
 	}
 	return 0;
@@ -301,6 +345,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 	struct qstr str;
 	struct nls_table *nls = NULL;
 	int err = -EINVAL;
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	int jnl_ret;
+#endif
 
 	sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
 	if (!sbi)
@@ -342,6 +389,23 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 			printk("HFS+-fs: wrong filesystem version\n");
 		goto cleanup;
 	}
+
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	hfsplus_journaled_init(sb, vhdr);
+	if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) {
+		if (hfsplus_journaled_check(sb)) {
+			if (!silent)
+				printk("HFS+-fs: Error in journal, use the force option at your own risk, mounting read-only.\n");
+			if (HFSPLUS_SB(sb).s_vhdr == NULL) {
+				printk("HFS+-fs: Error in Volume Header\n");
+				goto cleanup;
+			}
+			sb->s_flags |= MS_RDONLY;
+		} else
+			dprint(DBG_JOURNAL, "HFS+-fs: No problem in journal. Should be able to mount hfsplus volume in read-write mode\n");
+	}
+#endif /* CONFIG_HFSPLUS_JOURNAL */
+
 	HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks);
 	HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks);
 	HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc);
@@ -360,10 +424,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_maxbytes = MAX_LFS_FILESIZE;
 
 	if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+#if 0
 		if (!silent)
 			printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
 			       "running fsck.hfsplus is recommended.  mounting read-only.\n");
 		sb->s_flags |= MS_RDONLY;
+#else
+		if (!silent)
+			printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+					"running fsck.hfsplus is recommended.\n");
+#endif
 	} else if (sbi->flags & HFSPLUS_SB_FORCE) {
 		/* nothing */
 	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
@@ -371,10 +441,12 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 			printk("HFS+-fs: Filesystem is marked locked, mounting read-only.\n");
 		sb->s_flags |= MS_RDONLY;
 	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
+#ifndef CONFIG_HFSPLUS_JOURNAL
 		if (!silent)
 			printk("HFS+-fs: write access to a jounaled filesystem is not supported, "
 			       "use the force option at your own risk, mounting read-only.\n");
 		sb->s_flags |= MS_RDONLY;
+#endif /* CONFIG_HFSPLUS_JOURNAL */
 	}
 	sbi->flags &= ~HFSPLUS_SB_FORCE;
 
@@ -429,13 +501,26 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 	/* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
 	 * all three are registered with Apple for our use
 	 */
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) {
+		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_JOURNALED_VERSION);
+	}
+	else
+#endif
 	vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
 	vhdr->modify_date = hfsp_now2mt();
 	vhdr->write_count = cpu_to_be32(be32_to_cpu(vhdr->write_count) + 1);
 	vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
 	vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	jnl_ret = hfsplus_journaled_start_transaction(NULL, sb);
+#endif
 	mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
 	sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
+#ifdef CONFIG_HFSPLUS_JOURNAL
+	if (jnl_ret == HFSPLUS_JOURNAL_SUCCESS)
+		hfsplus_journaled_end_transaction(NULL, sb);
+#endif
 
 	if (!HFSPLUS_SB(sb).hidden_dir) {
 		printk("HFS+: create hidden dir...\n");


On Thu, Mar 24, 2011 at 16:29:22, naota@xxxxxxxxx (Naohiro) writes:
> Hi,
> 
> I'd like to take part in Google Summer of Code [0][1] with some Linux
> Kernel related works.
> 
> I have MacBookPro booting both MacOSX and Linux. Now Linux can write the
> filesystem safely only when HFS Plus journal is off. My life would be
> more better if Linux have complete HFS Plus filesystem read/write
> support.
> 
> So I'm thinking of implementing HFS Plus Journal support on Linux. I've
> searched and found a technote about HFS Plus format describe its Journal
> [2].
> 
> To prepare and write my application form, I'd like to hear some comments
> or suggestion about this idea: something like difficulty (too easy, too
> hard, moderate, ...), some suggested articles and so on.
> 
> I have some patches applied for the kernel before so I have basic
> knowledge of Linux kernel development.
> 
> Regards,
> Naohiro
> [0] http://code.google.com/soc/
> [1] http://www.linuxfoundation.org/collaborate/workgroups/gsoc/google-summer-code-2011
> [2] http://developer.apple.com/library/mac/technotes/tn/tn1150.html#Journal
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux