linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] GSoC idea: implement HFS Plus Journal
@ 2011-03-24 16:29 Naohiro Aota
  0 siblings, 0 replies; 2+ messages in thread
From: Naohiro Aota @ 2011-03-24 16:29 UTC (permalink / raw)
  To: roman; +Cc: linux-fsdevel

[-- Attachment #1: Type: text/plain, Size: 990 bytes --]

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

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [RFC] GSoC idea: implement HFS Plus Journal
@ 2011-05-26  5:50 changcs
  0 siblings, 0 replies; 2+ messages in thread
From: changcs @ 2011-05-26  5:50 UTC (permalink / raw)
  To: naota; +Cc: linux-fsdevel

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@elisp.net (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

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-05-26  5:46 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-24 16:29 [RFC] GSoC idea: implement HFS Plus Journal Naohiro Aota
  -- strict thread matches above, loose matches on Subject: below --
2011-05-26  5:50 changcs

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).