1/*
2* HFSPlus journal implementation Tathagata Das 2010
3*/
4
5#include <linux/fs.h>
6#include <linux/blkdev.h>
7#include <linux/pagemap.h>
8#include <linux/slab.h>
9
10#include <asm/current.h>
11#include <asm/unaligned.h>
12
13#include "hfsplus_jbd.h"
14#include "hfsplus_fs.h"
15#include "hfsplus_raw.h"
16
17/* Calculate chesum of ptr of size len */
18static int calc_checksum(unsigned char *ptr, int len)
19{
20	int i, chksum = 0;
21
22	for (i=0; i<len; i++, ptr++)
23		chksum = (chksum << 8) ^ (chksum + *ptr);
24
25	return (~chksum);
26}
27
28void swap_block_list_header(struct hfsplus_blhdr *blhdr)
29{
30	int i;
31
32	blhdr->num_blocks = swab16(blhdr->num_blocks);
33	blhdr->bytes_used = swab32(blhdr->bytes_used);
34	blhdr->checksum = swab32(blhdr->checksum);
35
36	for (i=1; i<blhdr->num_blocks; i++) {
37		blhdr->binfo[i].bnum = swab64(blhdr->binfo[i].bnum);
38		blhdr->binfo[i].bsize = swab32(blhdr->binfo[i].bsize);
39	}
40}
41
42static void swap_journal_header(struct hfsplus_journal_header *jh)
43{
44	jh->magic      = swab32(jh->magic);
45	jh->endian     = swab32(jh->endian);
46	jh->start      = swab64(jh->start);
47	jh->end        = swab64(jh->end);
48	jh->size       = swab64(jh->size);
49	jh->blhdr_size = swab32(jh->blhdr_size);
50	jh->checksum   = swab32(jh->checksum);
51	jh->jhdr_size  = swab32(jh->jhdr_size);
52}
53
54void print_volume_header(struct super_block *sb)
55{
56	int i;
57	unsigned char *vh_ptr = (unsigned char *)HFSPLUS_SB(sb).s_vhdr;
58
59	dprint(DBG_JOURNAL, "VOLUME HEADER\n");
60	for (i=0; i<102; i++)
61		dprint(DBG_JOURNAL, "%x ", vh_ptr[i]);
62	dprint(DBG_JOURNAL, "\n");
63}
64
65static void print_journal_header(struct hfsplus_journal_header *jh)
66{
67	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);
68}
69
70static int map_journal_header(struct super_block *sb)
71{
72	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
73	u32 jh_block_number;
74
75	jnl->jh_offset = be64_to_cpu(jnl->jibhdr->offset);
76	jh_block_number = jnl->jh_offset >> sb->s_blocksize_bits;
77	dprint(DBG_JOURNAL, "HFS+-fs: jh_block_number: %x\n", jh_block_number);
78	jnl->jh_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jh_block_number);
79	if (!jnl->jh_bh) {
80		printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
81		return HFSPLUS_JOURNAL_FAIL;
82	}
83	jnl->jhdr = (struct hfsplus_journal_header *)(jnl->jh_bh->b_data);
84
85	return HFSPLUS_JOURNAL_SUCCESS;
86}
87
88/* START: JBD specfic functions */
89static void hfsplus_abort(struct super_block * sb, const char * function,
90		 const char * fmt, ...)
91{
92	va_list args;
93
94	printk (KERN_ERR "hfsplus_abort called.\n");
95
96	va_start(args, fmt);
97	printk(KERN_ERR "HFS+-fs error (device %s): %s: ",sb->s_id, function);
98	vprintk(fmt, args);
99	printk("\n");
100	va_end(args);
101
102	if (sb->s_flags & MS_RDONLY)
103		return;
104
105	printk(KERN_ERR "Remounting filesystem read-only\n");
106	sb->s_flags |= MS_RDONLY;
107	hfsplus_jbd_abort(HFSPLUS_SB(sb).jnl.s_journal, -EIO);
108}
109
110static void hfsplus_journal_abort_handle(const char *err_no, struct buffer_head *bh, hfsplus_handle_t *hfsplus_handle)
111{
112	if (hfsplus_jbd_is_handle_aborted(hfsplus_handle->handle))
113		return;
114
115	printk(KERN_ERR "HFS+-fs: Aborting transaction\n");
116	hfsplus_jbd_abort_handle(hfsplus_handle->handle);
117}
118
119int hfsplus_journal_get_write_access(const char *err_fn, hfsplus_handle_t *hfsplus_handle, struct buffer_head *bh)
120{
121	int err;
122
123	if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT)
124		return HFSPLUS_JOURNAL_SUCCESS;
125
126	err = hfsplus_jbd_get_write_access(hfsplus_handle->handle, bh);
127	if (err) {
128		printk(KERN_ERR "HFS+-fs: %s() unable to get journal write access\n", err_fn);
129		hfsplus_journal_abort_handle(err_fn, bh, hfsplus_handle);
130	}
131
132	return err;
133}
134
135int hfsplus_journal_dirty_metadata(const char *err_fn, struct buffer_head *bh, hfsplus_handle_t *hfsplus_handle)
136{
137	int err;
138
139	if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT)
140		return HFSPLUS_JOURNAL_SUCCESS;
141
142	err = hfsplus_jbd_dirty_metadata(hfsplus_handle->handle, bh);
143	if (err) {
144		if (!hfsplus_handle->handle->h_err)
145			hfsplus_handle->handle->h_err = err;
146
147		if (hfsplus_jbd_is_handle_aborted(hfsplus_handle->handle))
148			return err;
149
150		printk(KERN_ERR "HFS+-fs: %s() aborting transaction:\n", err_fn);
151
152		hfsplus_jbd_abort_handle(hfsplus_handle->handle);
153	} else
154		hfsplus_handle->hcnt++;
155
156	return err;
157}
158
159/*
160 * Force the running and committing transactions to commit,
161 * and wait on the commit.
162 */
163int hfsplus_force_commit(struct super_block *sb)
164{
165	hfsplus_jbd_t *journal;
166	int ret;
167
168	if (sb->s_flags & MS_RDONLY)
169		return 0;
170
171	journal = HFSPLUS_SB(sb).jnl.s_journal;
172	sb->s_dirt = 0;
173	ret = hfsplus_jbd_force_commit(journal);
174	return ret;
175}
176
177int hfsplus_journal_start(const char *err_fn, struct super_block *sb, hfsplus_handle_t *hfsplus_handle)
178{
179	hfsplus_jbd_t *journal = HFSPLUS_SB(sb).jnl.s_journal;
180
181	hfsplus_handle->journaled = HFSPLUS_SB(sb).jnl.journaled;
182	if ((hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) || (journal == NULL)) {
183		hfsplus_handle->handle = NULL;
184		dprint(DBG_JTRANS, "HFS+-fs: Journal was not loaded\n");
185		return HFSPLUS_JOURNAL_SUCCESS;
186	}
187
188	if (is_hfsplus_jbd_aborted(journal)) {
189		hfsplus_abort(sb, __FUNCTION__, "Detected aborted journal");
190		return -EROFS;
191	}
192
193	hfsplus_handle->hcnt = 0;
194	hfsplus_handle->maxblock = HFSPLUS_SB(sb).jnl.journal_maxblock;
195	hfsplus_handle->handle = hfsplus_jbd_start(journal, HFSPLUS_SB(sb).jnl.journal_maxblock, hfsplus_handle);
196	if (IS_ERR(hfsplus_handle->handle)) {
197		printk(KERN_ERR "HFS+fs: journal cannot be started from %s. Error number: %ld\n", err_fn, PTR_ERR(hfsplus_handle->handle));
198		return PTR_ERR(hfsplus_handle->handle);
199	}
200
201	return HFSPLUS_JOURNAL_SUCCESS;
202}
203
204int hfsplus_journal_stop(hfsplus_handle_t *hfsplus_handle)
205{
206	if ((hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) || (hfsplus_handle->handle == NULL)) {
207		return HFSPLUS_JOURNAL_SUCCESS;
208	}
209
210	return hfsplus_jbd_stop(hfsplus_handle->handle);
211}
212
213/* Fill in the initial static fields in the new journal superblock */
214static int hfsplus_journal_superblock_init(hfsplus_jbd_t *journal, int start)
215{
216	hfsplus_jbd_superblock_t *jsb;
217	struct buffer_head *bh;
218	unsigned long blocknr;
219	int i;
220
221	if (journal->j_superblock == NULL)
222		return HFSPLUS_JOURNAL_FAIL;
223
224	jsb = journal->j_superblock;
225	memset(jsb->mac_padding, 0, sizeof(jsb->mac_padding));
226
227	if (jsb->s_header.h_magic == cpu_to_be32(JFS_MAGIC_NUMBER)) {
228		dprint(DBG_JOURNAL, "HFS+-fs: Journal superblock is already initialized\n");
229	}
230
231	dprint(DBG_JOURNAL, "HFS+-fs: Zeroing out journal blocks...\n");
232   for (i = start; i < journal->j_maxlen; i++) {
233		blocknr = i;
234      bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
235      lock_buffer(bh);
236		if (i == start)
237      	memset(bh->b_data + HFSPLUS_SECTOR_SIZE, 0, journal->j_blocksize - HFSPLUS_SECTOR_SIZE);
238		else
239      	memset(bh->b_data, 0, journal->j_blocksize);
240      mark_buffer_dirty(bh);
241      set_buffer_uptodate(bh);
242      unlock_buffer(bh);
243      __brelse(bh);
244   }
245
246   sync_blockdev(journal->j_dev);
247   dprint(DBG_JOURNAL, "HFS+-fs: journal cleared.\n");
248
249	jsb->s_header.h_magic  = cpu_to_be32(JFS_MAGIC_NUMBER);
250	jsb->s_header.h_blocktype = cpu_to_be32(JFS_SUPERBLOCK_V2);
251
252	jsb->s_blocksize   = cpu_to_be32(journal->j_blocksize);
253	jsb->s_maxlen   = cpu_to_be32(journal->j_maxlen);
254	jsb->s_first = cpu_to_be32(start+1); /* First block is for Mac's journal header and JBD superblock */
255
256	journal->j_transaction_sequence = 1;
257
258	journal->j_flags &= ~JFS_ABORT;
259	journal->j_format_version = 2;
260
261	/* Update journal superblock on disk and wait for the IO to complete.*/
262	hfsplus_jbd_update_superblock(journal, 1);
263
264	return HFSPLUS_JOURNAL_SUCCESS;
265}
266
267hfsplus_jbd_t * hfsplus_get_dev_journal(struct super_block *sb)
268{
269	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
270	int start, len, blocksize;
271	hfsplus_jbd_t *journal = NULL;
272	struct hfsplus_journal_header *jh;
273
274	jnl->journal_maxblock = (jnl->jhdr->blhdr_size / sizeof(struct hfsplus_block_info)) - 1;
275	jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
276	blocksize = sb->s_blocksize;
277	start = (int)(jnl->jh_offset)/blocksize;
278	len = (int)(jh->size + jnl->jh_offset)/blocksize;
279	jnl->journal_maxblock = 512;
280	dprint(DBG_JOURNAL, "start: %d, len: %d, blocksize: %d, journal_maxblock: %d\n", start, len, blocksize, jnl->journal_maxblock);
281	journal = hfsplus_jbd_init_dev(sb->s_bdev, sb->s_bdev, start, len, blocksize);
282
283	if (journal) {
284		dprint(DBG_JOURNAL, "HFS+-fs: Able to create device journal\n");
285		journal->j_private = sb;
286		ll_rw_block(READ, 1, &journal->j_sb_buffer);
287		wait_on_buffer(journal->j_sb_buffer);
288		if (!buffer_uptodate(journal->j_sb_buffer)) {
289			printk(KERN_ERR "HFS+-fs: I/O error on journal device\n");
290			goto out_journal;
291		}
292
293		/* Initialize journal superblock for the first time.
294		 * Otherwise hfsplus_jbd_load will fail.
295		*/
296		if (hfsplus_journal_superblock_init(journal, start) == HFSPLUS_JOURNAL_SUCCESS) {
297			dprint(DBG_JOURNAL, "HFS+-fs: success in initializeing journal superblock.\n");
298		} else {
299			printk(KERN_ERR "HFS+-fs: error in initializeing journal superblock.\n");
300			goto out_journal;
301		}
302	}
303
304	return journal;
305
306out_journal:
307	hfsplus_jbd_destroy(journal);
308	return NULL;
309}
310
311/* Caller should lock the page before calling this function */
312static int hfsplus_journalled_write_full_page(hfsplus_handle_t *hfsplus_handle, struct page *page)
313{
314	int err, ret;
315
316	ret = block_prepare_write(page, 0, PAGE_CACHE_SIZE, hfsplus_get_block);
317	if (ret) {
318		printk("%s: Error in block_prepare_write\n", __FUNCTION__);
319		return ret;
320	}
321
322	ret = hfsplus_walk_page_buffers(hfsplus_handle, page_buffers(page), 0,
323		PAGE_CACHE_SIZE, NULL, hfsplus_do_journal_get_write_access);
324
325	err = hfsplus_walk_page_buffers(hfsplus_handle, page_buffers(page), 0,
326			PAGE_CACHE_SIZE, NULL, hfsplus_commit_write_fn);
327	if (ret == 0)
328		ret = err;
329
330	return ret;
331}
332
333int hfsplus_journalled_set_page_dirty(hfsplus_handle_t *hfsplus_handle, struct page *page)
334{
335	if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) {
336		set_page_dirty(page);
337		return HFSPLUS_JOURNAL_SUCCESS;
338	}
339
340	/* Page contains metadata or data */
341	if (page->mapping->a_ops == &hfsplus_journalled_btree_aops) {
342		if (hfsplus_handle->hcnt < hfsplus_handle->maxblock) {
343			int ret;
344
345			lock_page(page);
346			ret = hfsplus_journalled_write_full_page(hfsplus_handle, page);
347			unlock_page(page);
348			return ret;
349		} else {
350			printk("BTREE: HFS+-fs: Count exceeds (count %d, maxblock: %d)\n", hfsplus_handle->hcnt++, hfsplus_handle->maxblock);
351			set_page_dirty(page);
352			return HFSPLUS_JOURNAL_SUCCESS;
353		}
354	}
355	else if (page->mapping->a_ops == &hfsplus_journalled_aops) {
356      if (hfsplus_handle->hcnt < hfsplus_handle->maxblock) {
357         int ret;
358
359         lock_page(page);
360         ret = hfsplus_journalled_write_full_page(hfsplus_handle, page);
361         unlock_page(page);
362         return ret;
363      } else {
364			printk("HFS+-fs: Count exceeds (count %d, maxblock: %d)\n", hfsplus_handle->hcnt++, hfsplus_handle->maxblock);
365         set_page_dirty(page);
366         return HFSPLUS_JOURNAL_SUCCESS;
367      }
368   } else {
369		set_page_dirty(page);
370		return HFSPLUS_JOURNAL_SUCCESS;
371	}
372}
373
374int hfsplus_journalled_mark_inode_dirty(const char *err_fn, hfsplus_handle_t *hfsplus_handle, struct inode *inode)
375{
376	struct hfsplus_vh *vhdr;
377	int ret = 0;
378
379	if (hfsplus_handle->journaled != HFSPLUS_JOURNAL_PRESENT) {
380		mark_inode_dirty(inode);
381		return HFSPLUS_JOURNAL_SUCCESS;
382	}
383
384	dprint(DBG_JTRANS, "hfsplus_journalled_mark_inode_dirty: %lu, called from: %s\n", inode->i_ino, err_fn);
385	hfsplus_ext_write_extent(hfsplus_handle, inode);
386	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
387		return hfsplus_cat_write_inode(hfsplus_handle, inode);
388	}
389	vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
390	switch (inode->i_ino) {
391	case HFSPLUS_ROOT_CNID:
392		ret = hfsplus_cat_write_inode(hfsplus_handle, inode);
393		break;
394	case HFSPLUS_EXT_CNID:
395		if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) {
396			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
397			inode->i_sb->s_dirt = 1;
398		}
399		hfsplus_inode_write_fork(inode, &vhdr->ext_file);
400		hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).ext_tree);
401		break;
402	case HFSPLUS_CAT_CNID:
403		if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) {
404			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
405			inode->i_sb->s_dirt = 1;
406		}
407		hfsplus_inode_write_fork(inode, &vhdr->cat_file);
408		hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).cat_tree);
409		break;
410	case HFSPLUS_ALLOC_CNID:
411		if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) {
412			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
413			inode->i_sb->s_dirt = 1;
414		}
415		hfsplus_inode_write_fork(inode, &vhdr->alloc_file);
416		break;
417	case HFSPLUS_START_CNID:
418		if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) {
419			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
420			inode->i_sb->s_dirt = 1;
421		}
422		hfsplus_inode_write_fork(inode, &vhdr->start_file);
423		break;
424	case HFSPLUS_ATTR_CNID:
425		if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) {
426			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
427			inode->i_sb->s_dirt = 1;
428		}
429		hfsplus_inode_write_fork(inode, &vhdr->attr_file);
430		hfs_btree_write(hfsplus_handle, HFSPLUS_SB(inode->i_sb).attr_tree);
431		break;
432	}
433	if (ret)
434		printk(KERN_ERR "HFS+-fs: [%s] unable to do hfsplus_journalled_mark_inode_dirty()\n", err_fn);
435	return ret;
436}
437
438void hfsplus_jhdr_checksum_calculate(hfsplus_jbd_t *journal)
439{
440	struct hfsplus_journal_header *jh = &(journal->j_superblock->mac_jh);
441
442	jh->checksum = 0;
443	jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
444}
445
446void hfsplus_journal_header_start_update(hfsplus_jbd_t *journal, unsigned long freed)
447{
448	struct super_block *sb = journal->j_private;
449	hfsplus_jbd_superblock_t *jsb = journal->j_superblock;
450	struct hfsplus_journal_header *jh = &(jsb->mac_jh);
451	__be32 mac_jh_start_blocknum = (jh->start + HFSPLUS_SB(sb).jnl.jh_offset) >> sb->s_blocksize_bits;
452
453	dprint(DBG_JCHKPT, "jh->start:%llx, jh_offset: %llx, mac_jh_start_blocknum: %x, freed: %lx, last: %lx, j_first: %lx\n",
454		jh->start, HFSPLUS_SB(sb).jnl.jh_offset, mac_jh_start_blocknum, freed, journal->j_last, journal->j_first);
455	if ((mac_jh_start_blocknum + freed) < journal->j_last) {
456		jh->start += freed * sb->s_blocksize;
457	}
458	else {
459		jh->start = (journal->j_first + (freed - (journal->j_last - mac_jh_start_blocknum)))*sb->s_blocksize;
460	}
461}
462
463void inline hfsplus_journal_header_end_update(hfsplus_jbd_t *journal, struct hfsplus_journal_header *jh)
464{
465	struct super_block *sb = journal->j_private;
466
467	jh->end = ((journal->j_head - journal->j_first) * sb->s_blocksize) + jh->blhdr_size;
468}
469
470void hfsplus_journal_mark_journal_empty(hfsplus_jbd_t *journal)
471{
472	struct hfsplus_journal_header *jh = &(journal->j_superblock->mac_jh);
473
474	jh->start = jh->end;
475	jh->checksum = 0;
476	jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
477}
478/* END: JBD specfic functions */
479
480
481/* Write journal header during replay */
482static int hfsplus_replay_write_journal_header(struct super_block *sb)
483{
484	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
485
486	jh->start = sb->s_blocksize;
487	jh->end = sb->s_blocksize;
488	jh->blhdr_size = sb->s_blocksize;
489
490	if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP) {
491		swap_journal_header(jh);
492		jh->checksum = 0;
493		jh->checksum = swab32(calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header)));
494	}
495	else {
496		jh->checksum = 0;
497		jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
498	}
499
500	/* Write it to disk */
501	mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh);
502	sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jh_bh);
503
504	if (HFSPLUS_SB(sb).jnl.flags == HFSPLUS_JOURNAL_SWAP)
505		swap_journal_header(jh);
506
507	return HFSPLUS_JOURNAL_SUCCESS;
508}
509
510static int hfsplus_write_journal_header(struct super_block *sb)
511{
512	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
513
514	jh->checksum = 0;
515	jh->checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
516
517	/* Write it to disk */
518	mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jh_bh);
519
520	return HFSPLUS_JOURNAL_SUCCESS;
521}
522
523/* Create journal header, journal buffer and initialize them
524 * Assume that presence of journal is already verified
525*/
526int hfsplus_journalled_create(struct super_block *sb)
527{
528	struct hfsplus_journal_header *jhdr;
529	u64 jibsize = be64_to_cpu(HFSPLUS_SB(sb).jnl.jibhdr->offset);
530
531	dprint(DBG_JOURNAL, "sb->s_blocksize: %lx, jibsize: %llx\n", sb->s_blocksize, jibsize);
532
533	/* Journal size is not aligned */
534	if (((jibsize >> sb->s_blocksize_bits) << sb->s_blocksize_bits) != jibsize) {
535		printk("HFS+-fs: journal size is not aligned\n");
536		return HFSPLUS_JOURNAL_FAIL;
537	}
538
539	if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) {
540		printk("HFS+-fs: Error in mapping journal header\n");
541		return HFSPLUS_JOURNAL_FAIL;
542	}
543
544	jhdr = (struct hfsplus_journal_header *)HFSPLUS_SB(sb).jnl.jhdr;
545
546	/* Populate journal header and write it to the disk */
547	jhdr->magic = HFSPLUS_JOURNAL_HEADER_MAGIC;
548	jhdr->endian = HFSPLUS_JOURNAL_HEADER_ENDIAN;
549	jhdr->start = sb->s_blocksize; /* First block is for journal header itself */
550	jhdr->end = sb->s_blocksize; /* Initially journal buffer is empty */
551	jhdr->size = 0x800000; /* Default size of journal log is 8MB */
552	jhdr->blhdr_size = sb->s_blocksize;
553	jhdr->jhdr_size = sb->s_blocksize; /* Assign first block for journal header */
554
555	if (jhdr->start != jhdr->end) {
556		printk("HFS+-fs: hfsplus_write_journal_header fail: Journal is not empty\n");
557		return HFSPLUS_JOURNAL_FAIL;
558	}
559
560	return hfsplus_write_journal_header(sb);
561}
562
563void hfsplus_test_block_list_header(const char *func, struct hfsplus_journal_header *jh, struct hfsplus_journal *jnl)
564{
565	u32 start_block_number;
566	struct buffer_head *blhdr_bh = NULL;
567	struct hfsplus_blhdr *blhdr;
568	struct super_block *sb = jnl->sbp;
569
570	printk("called from %s()\n", func);
571	start_block_number = (jh->start + jnl->jh_offset) >> sb->s_blocksize_bits;
572	blhdr_bh = sb_bread(sb, start_block_number);
573	if (!blhdr_bh) {
574		printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
575		return;
576	}
577	blhdr = (hfsplus_blhdr_t *)blhdr_bh->b_data;
578	if (jnl->flags == HFSPLUS_JOURNAL_SWAP)
579		swap_block_list_header(blhdr);
580	printk("start block number: %x, max_blocks: %x, num_blocks: %x, bytes_used: %x\n", start_block_number, blhdr->max_blocks, blhdr->num_blocks, blhdr->bytes_used);
581	brelse(blhdr_bh);
582}
583
584/* If the journal consists transaction then write them to disk.
585 * Return success if it brings the file system into consistent state.
586 * Otherwise return fail.
587*/
588static int hfsplus_journal_replay(struct super_block *sb)
589{
590	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
591	struct buffer_head *blhdr_bh = NULL, *tr_bh = NULL, *disk_bh = NULL;
592	struct hfsplus_blhdr *blhdr;
593	u32 start_sector_number, tr_sector_number, disk_sector_number, i, ret = HFSPLUS_JOURNAL_FAIL;
594	u64 tr_offset, disk_offset;
595	struct hfsplus_journal_header *jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
596	unsigned char *tr_buf, *disk_buf;
597	__be32 bufsize;
598
599	if (jh->start == jh->end) {
600		dprint(DBG_JREPLAY, "HFS+-fs: Journal is empty, nothing to replay\n");
601		ret = hfsplus_replay_write_journal_header(sb);
602		return ret;
603	}
604
605	if ((jh->start > jh->size) || (jh->end > jh->size)) {
606		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);
607		return ret;
608	}
609
610	//hfsplus_test_block_list_header(__FUNCTION__, jh, jnl);
611
612	if (jh->start == jh->size)
613		jh->start = jh->jhdr_size;
614
615	down(&jnl->jnl_lock);
616	/* Go through each transaction */
617	while (jh->start != jh->end) {
618		if (blhdr_bh)
619			brelse(blhdr_bh);
620
621		start_sector_number = (jh->start + jnl->jh_offset) >> HFSPLUS_SECTOR_SHIFT;
622		dprint(DBG_JREPLAY, "start: %llx, start_sector_number: %x\n", jh->start, start_sector_number);
623		blhdr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + start_sector_number, blhdr);
624		if (!blhdr_bh) {
625			printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
626			up(&jnl->jnl_lock);
627			return ret;
628		}
629
630		if (jnl->flags == HFSPLUS_JOURNAL_SWAP)
631			swap_block_list_header(blhdr);
632
633		dprint(DBG_JREPLAY, "HFS+-fs: num_blocks: %x, bytes_used: %x\n", blhdr->num_blocks, blhdr->bytes_used);
634		/* Point to the second block in the Volume, first block is already in block list header */
635		tr_offset = jnl->jh_offset + jh->start + jh->blhdr_size;
636
637		for (i=1; i<blhdr->num_blocks; i++) {
638			bufsize = blhdr->binfo[i].bsize;
639			disk_offset = blhdr->binfo[i].bnum << HFSPLUS_SECTOR_SHIFT;
640
641			dprint(DBG_JREPLAY, "[i:%x] bnum: %llx, bsize: %x, bufsize: %x, blocksize: %lx\n", i, blhdr->binfo[i].bnum, blhdr->binfo[i].bsize, bufsize, sb->s_blocksize);
642
643			while (bufsize > 0) {
644				/* Read one block */
645				tr_sector_number = tr_offset >> HFSPLUS_SECTOR_SHIFT;
646				dprint(DBG_JREPLAY, "tr_sector_number: %x, tr_offset: %llx\n", tr_sector_number, tr_offset);
647				tr_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + tr_sector_number, tr_buf);
648				if (!tr_bh) {
649					printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
650					if (blhdr_bh)
651						brelse(blhdr_bh);
652					up(&jnl->jnl_lock);
653					return ret;
654				}
655
656				disk_sector_number = disk_offset >> HFSPLUS_SECTOR_SHIFT;
657				dprint(DBG_JREPLAY, "disk_sector_number: %x, disk_offset: %llx, bufsize: %x\n", disk_sector_number, disk_offset, bufsize);
658				/* Read the same sector from the Volume */
659				disk_bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + disk_sector_number, disk_buf);
660				if (!disk_bh) {
661					printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
662					if (blhdr_bh)
663						brelse(blhdr_bh);
664					if (tr_bh)
665						brelse(tr_bh);
666					up(&jnl->jnl_lock);
667					return ret;
668				}
669
670				/* Write transaction block to the disk block in sector wise */
671				memcpy(disk_buf, tr_buf, HFSPLUS_SECTOR_SIZE);
672				mark_buffer_dirty(disk_bh);
673				sync_dirty_buffer(disk_bh);
674
675				/* Free buffer heads */
676				brelse(disk_bh);
677				brelse(tr_bh);
678
679				tr_offset += HFSPLUS_SECTOR_SIZE;
680				disk_offset += HFSPLUS_SECTOR_SIZE;
681				bufsize -= HFSPLUS_SECTOR_SIZE;
682
683				/* Check tr_offset reaches at the end of journal buffer */
684				if (tr_offset == (jnl->jh_offset + jh->size)) {
685					printk("tr_offset: %llx, jh->size: %llx, jh_offset: %llx\n", tr_offset, jh->size, jnl->jh_offset);
686					tr_offset = jnl->jh_offset + jh->jhdr_size; /* Set to the beginning of journal buffer */
687				}
688			}
689		}
690
691		/* Check position of start index, wrap around if necessary */
692		if ((jh->start + blhdr->bytes_used) < jh->size)
693			jh->start += blhdr->bytes_used;
694		else
695			jh->start = jh->jhdr_size + (jh->start + blhdr->bytes_used) - jh->size;
696	}
697
698	if (blhdr_bh)
699		brelse(blhdr_bh);
700
701	if (jh->start == jh->end) {
702		ret = hfsplus_replay_write_journal_header(sb);
703	} else {
704		printk("HFS+-fs: %s Error in journal replay\n", __func__);
705	}
706
707	/* Populate Volume Header with new values */
708	if (ret == HFSPLUS_JOURNAL_SUCCESS && HFSPLUS_SB(sb).s_vhdr) {
709		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
710		struct buffer_head *bh;
711
712		dprint(DBG_JREPLAY, "Populate Volume Header again\n");
713		HFSPLUS_SB(sb).s_vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
714		mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
715		sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
716
717		if (HFSPLUS_SB(sb).s_vhbh)
718			brelse(HFSPLUS_SB(sb).s_vhbh);
719
720		bh = sb_bread512(sb, HFSPLUS_SB(sb).blockoffset + HFSPLUS_VOLHEAD_SECTOR, vhdr);
721		if (!bh) {
722			printk("HFS+-fs Line=%d: Error in read\n", __LINE__);
723			HFSPLUS_SB(sb).s_vhdr = NULL;
724			up(&jnl->jnl_lock);
725			return HFSPLUS_JOURNAL_FAIL;
726		}
727
728		/* should still be the same... */
729		if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG) {
730			printk("Volume header signature (%x) is wrong\n", be16_to_cpu(vhdr->signature));
731			brelse(bh);
732			HFSPLUS_SB(sb).s_vhdr = NULL;
733			up(&jnl->jnl_lock);
734			return HFSPLUS_JOURNAL_FAIL;
735		}
736
737		HFSPLUS_SB(sb).s_vhbh = bh;
738		HFSPLUS_SB(sb).s_vhdr = vhdr;
739	}
740
741	up(&jnl->jnl_lock);
742	return ret;
743}
744
745/* Check consistency of journal log file in hfsplus volume
746*/
747int hfsplus_journalled_check(struct super_block *sb)
748{
749	struct hfsplus_journal_info_block *jib;
750	struct hfsplus_journal_header *jh;
751	u32 checksum, org_checksum;
752
753	print_volume_header(sb);
754
755	if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) {
756		printk("HFS+-fs: Journal is not present\n");
757		return HFSPLUS_JOURNAL_CONSISTENT;
758	}
759
760	jib = (struct hfsplus_journal_info_block *)(HFSPLUS_SB(sb).jnl.jibhdr);
761	dprint(DBG_JOURNAL, "HFS+-fs: be32_to_cpu(jib->flags): %x\n", be32_to_cpu(jib->flags));
762
763	/* Journal is on another volume, and the "on this volume" flag
764	* isn't set
765	*/
766	if(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_ON_OTHER_DEVICE &&
767		!(be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_IN_FS)) {
768		printk("HFS+-fs: Unable to access the journal.\n");
769		return HFSPLUS_JOURNAL_INCONSISTENT;
770	}
771
772	/* Journal should be created in initialization.
773	* Mark inconsistent if the journal is still not created yet
774	*/
775	if (be32_to_cpu(jib->flags) & HFSPLUS_JOURNAL_NEED_INIT) {
776		printk("HFS+-fs: Error, journal is not created\n");
777		return HFSPLUS_JOURNAL_INCONSISTENT;
778	}
779
780	dprint(DBG_JOURNAL, "HFS+-fs: Found Info Block and verified successfully.\n");
781	jh = (struct hfsplus_journal_header *)(HFSPLUS_SB(sb).jnl.jhdr);
782
783	org_checksum = jh->checksum;
784	jh->checksum = 0;
785
786	if (jh->magic == swab32(HFSPLUS_JOURNAL_HEADER_MAGIC)) {
787		org_checksum = swab32(org_checksum);
788		checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
789		swap_journal_header(jh);
790		HFSPLUS_SB(sb).jnl.flags = HFSPLUS_JOURNAL_SWAP;
791	}
792	else
793		checksum = calc_checksum((unsigned char *)jh, sizeof(struct hfsplus_journal_header));
794
795	print_journal_header(jh);
796
797	/* Verify the journal header */
798	if(jh->magic != HFSPLUS_JOURNAL_HEADER_MAGIC || jh->endian != HFSPLUS_JOURNAL_HEADER_ENDIAN){
799		printk("HFS+-fs: Journal header verification failed.\n");
800		return HFSPLUS_JOURNAL_INCONSISTENT;
801	}
802
803	if (checksum != org_checksum) {
804		jh->checksum = checksum;
805		printk("HFS+-fs: Error in journal header checksum checksum: %x, org_checksum: %x\n", checksum, org_checksum);
806		return HFSPLUS_JOURNAL_INCONSISTENT;
807	}
808	jh->checksum = checksum;
809
810	dprint(DBG_JOURNAL, "HFS+-fs: No problem in magic number, endian and checksum\n");
811
812	/* Compare start to end */
813	if(jh->start == jh->end) {
814		hfsplus_replay_write_journal_header(sb);
815		/* If they're the same, we can mount, it's clean */
816		printk("HFS+-fs: Journal is empty means consistent\n");
817		return HFSPLUS_JOURNAL_CONSISTENT;
818	} else {
819		/* Replay journal and bring the file system in consistent state */
820		if (hfsplus_journal_replay(sb) == HFSPLUS_JOURNAL_FAIL) {
821			/* Unable to replay */
822			printk("HFS+-fs: Journal is non empty means inconsistent, please run fsck.hfsplus\n");
823			return HFSPLUS_JOURNAL_INCONSISTENT;
824		} else
825			dprint(DBG_JOURNAL, "HFS+-fs: Journal replay done\n");
826	}
827
828	return HFSPLUS_JOURNAL_CONSISTENT;
829}
830
831/* Check journal present or not and initialize hfsplus_journal accordingly
832 * Assume that super block and volume header are already initialized
833*/
834void hfsplus_journalled_init(struct super_block *sb, struct hfsplus_vh *vhdr)
835{
836	struct hfsplus_journal *jnl = &(HFSPLUS_SB(sb).jnl);
837	u32 jib_flags;
838
839	jnl->journaled = !HFSPLUS_JOURNAL_PRESENT; /* Initialize as non-journaled */
840	jnl->sbp = NULL;
841	jnl->jh_bh = NULL;
842	jnl->alloc_block = be32_to_cpu(vhdr->alloc_file.extents[0].start_block);
843	jnl->ext_block = be32_to_cpu(vhdr->ext_file.extents[0].start_block);
844	jnl->catalog_block = be32_to_cpu(vhdr->cat_file.extents[0].start_block);
845	dprint(DBG_JOURNAL, "alloc_block: %x, ext_block: %x, catalog_block: %x\n", jnl->alloc_block, jnl->ext_block, jnl->catalog_block);
846
847	if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
848		dprint(DBG_JOURNAL,"HFS+-fs: Journaled filesystem\n");
849		jnl->jib_offset = be32_to_cpu(vhdr->journal_info_block);
850		/* Check the journal info block to find the block # of the journal */
851		jnl->jib_bh = sb_bread(sb, HFSPLUS_SB(sb).blockoffset + jnl->jib_offset);
852		if (!jnl->jib_bh) {
853			printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
854			return;
855		}
856		jnl->jibhdr = (struct hfsplus_journal_info_block *)(jnl->jib_bh->b_data);
857		jib_flags = be32_to_cpu(jnl->jibhdr->flags);
858		dprint(DBG_JOURNAL, "HFS+-fs: jib_flags: %x\n", jib_flags);
859		if ((jib_flags & HFSPLUS_JOURNAL_ON_OTHER_DEVICE) && !(jib_flags & HFSPLUS_JOURNAL_IN_FS))
860			goto init_fail;
861
862		dprint(DBG_JOURNAL, "HFS+-fs: jib size: %x\n", be32_to_cpu(jnl->jibhdr->size));
863		if (jib_flags & HFSPLUS_JOURNAL_NEED_INIT) {
864			dprint(DBG_JOURNAL, "HFS+-fs: Journal is not created\n");
865			if (hfsplus_journalled_create(sb) == 0) {
866				HFSPLUS_SB(sb).jnl.jibhdr->flags &= be32_to_cpu(~HFSPLUS_JOURNAL_NEED_INIT);
867				/* write it to disk */
868				mark_buffer_dirty(HFSPLUS_SB(sb).jnl.jib_bh);
869				sync_dirty_buffer(HFSPLUS_SB(sb).jnl.jib_bh);
870			} else {
871				printk("HFS+-fs: Fail to create journal\n");
872				goto init_fail;
873			}
874		}
875
876		/* Check already initialize in journal create */
877		if (jnl->jh_bh == NULL) {
878			if (map_journal_header(sb) == HFSPLUS_JOURNAL_FAIL) {
879				printk("HFS+-fs Line=%d: Error in buffer read\n", __LINE__);
880				goto init_fail;
881			}
882		}
883
884		init_MUTEX(&jnl->jnl_lock);
885		jnl->sbp = sb;
886		jnl->flags = !HFSPLUS_JOURNAL_SWAP;
887		jnl->journaled = HFSPLUS_JOURNAL_PRESENT;
888	}
889
890	return;
891
892init_fail:
893	printk("HFS+-fs: Journal initialization fails\n");
894	if (jnl->jib_bh)
895		brelse(jnl->jib_bh);
896}
897
898/* Deinitialize journal if it is present */
899void hfsplus_journalled_deinit(struct super_block *sb)
900{
901	if (HFSPLUS_SB(sb).jnl.journaled != HFSPLUS_JOURNAL_PRESENT) {
902		return;
903	}
904
905	if (HFSPLUS_SB(sb).jnl.s_journal != NULL) {
906		hfsplus_jbd_destroy(HFSPLUS_SB(sb).jnl.s_journal);
907	}
908
909	if (HFSPLUS_SB(sb).jnl.jib_bh)
910		brelse(HFSPLUS_SB(sb).jnl.jib_bh);
911
912	if (HFSPLUS_SB(sb).jnl.jh_bh)
913		brelse(HFSPLUS_SB(sb).jnl.jh_bh);
914
915	HFSPLUS_SB(sb).jnl.journaled = !HFSPLUS_JOURNAL_PRESENT;
916}
917