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