1/*
2 *  linux/fs/hfsplus/super.c
3 *
4 * Copyright (C) 2001
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
7 *
8 */
9
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/pagemap.h>
13#include <linux/fs.h>
14#include <linux/slab.h>
15#include <linux/vfs.h>
16#include <linux/nls.h>
17
18static struct inode *hfsplus_alloc_inode(struct super_block *sb);
19static void hfsplus_destroy_inode(struct inode *inode);
20
21#include "hfsplus_fs.h"
22
23static void hfsplus_read_inode(struct inode *inode)
24{
25	struct hfs_find_data fd;
26	struct hfsplus_vh *vhdr;
27	hfsplus_handle_t hfsplus_handle;
28	int err;
29
30	INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
31	init_MUTEX(&HFSPLUS_I(inode).extents_lock);
32	HFSPLUS_I(inode).flags = 0;
33	HFSPLUS_I(inode).rsrc_inode = NULL;
34	atomic_set(&HFSPLUS_I(inode).opencnt, 0);
35
36	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
37	read_inode:
38		hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
39		if (hfsplus_journal_start(__FUNCTION__, inode->i_sb, &hfsplus_handle))
40			goto bad_inode;
41		err = hfsplus_find_cat(&hfsplus_handle, inode->i_sb, inode->i_ino, &fd);
42		if (!err)
43			err = hfsplus_cat_read_inode(inode, &fd);
44		hfs_find_exit(&hfsplus_handle, &fd);
45		hfsplus_journal_stop(&hfsplus_handle);
46		if (err)
47			goto bad_inode;
48		return;
49	}
50	vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
51	switch(inode->i_ino) {
52	case HFSPLUS_ROOT_CNID:
53		goto read_inode;
54	case HFSPLUS_EXT_CNID:
55		hfsplus_inode_read_fork(inode, &vhdr->ext_file);
56		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED))
57			inode->i_mapping->a_ops = &hfsplus_journalled_btree_aops;
58		else
59			inode->i_mapping->a_ops = &hfsplus_btree_aops;
60		break;
61	case HFSPLUS_CAT_CNID:
62		hfsplus_inode_read_fork(inode, &vhdr->cat_file);
63		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED))
64			inode->i_mapping->a_ops = &hfsplus_journalled_btree_aops;
65		else
66			inode->i_mapping->a_ops = &hfsplus_btree_aops;
67		break;
68	case HFSPLUS_ALLOC_CNID:
69		hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
70		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED))
71			inode->i_mapping->a_ops = &hfsplus_journalled_aops;
72		else
73			inode->i_mapping->a_ops = &hfsplus_aops;
74		break;
75	case HFSPLUS_START_CNID:
76		hfsplus_inode_read_fork(inode, &vhdr->start_file);
77		break;
78	case HFSPLUS_ATTR_CNID:
79		hfsplus_inode_read_fork(inode, &vhdr->attr_file);
80		if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED))
81			inode->i_mapping->a_ops = &hfsplus_journalled_btree_aops;
82		else
83			inode->i_mapping->a_ops = &hfsplus_btree_aops;
84		break;
85	default:
86		goto bad_inode;
87	}
88
89	return;
90
91 bad_inode:
92	make_bad_inode(inode);
93}
94
95static int hfsplus_write_inode(struct inode *inode, int unused)
96{
97	struct hfsplus_vh *vhdr;
98	hfsplus_handle_t hfsplus_handle;
99	int ret = 0;
100
101	if ((HFSPLUS_SB(inode->i_sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) &&
102		((inode->i_mapping->a_ops == &hfsplus_journalled_btree_aops) ||
103		(inode->i_mapping->a_ops == &hfsplus_journalled_aops))) {
104		if (current->flags & PF_MEMALLOC)
105			return 0;
106
107		if (hfsplus_jbd_current_handle()) {
108			hfsplus_jbd_debug(0, "called recursively, non-PF_MEMALLOC!\n");
109			dump_stack();
110			return -EIO;
111 		}
112
113		if (!unused)
114			return 0;
115
116   	return hfsplus_force_commit(inode->i_sb);
117	}
118
119	dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
120	hfsplus_handle.journaled = !HFSPLUS_JOURNAL_PRESENT;
121
122	hfsplus_ext_write_extent(&hfsplus_handle, inode);
123	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
124		ret = hfsplus_cat_write_inode(&hfsplus_handle, inode);
125		return ret;
126	}
127	vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
128	switch (inode->i_ino) {
129	case HFSPLUS_ROOT_CNID:
130		ret = hfsplus_cat_write_inode(&hfsplus_handle, inode);
131		break;
132	case HFSPLUS_EXT_CNID:
133		if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) {
134			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
135			inode->i_sb->s_dirt = 1;
136		}
137		hfsplus_inode_write_fork(inode, &vhdr->ext_file);
138		hfs_btree_write(&hfsplus_handle, HFSPLUS_SB(inode->i_sb).ext_tree);
139		break;
140	case HFSPLUS_CAT_CNID:
141		if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) {
142			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
143			inode->i_sb->s_dirt = 1;
144		}
145		hfsplus_inode_write_fork(inode, &vhdr->cat_file);
146		hfs_btree_write(&hfsplus_handle, HFSPLUS_SB(inode->i_sb).cat_tree);
147		break;
148	case HFSPLUS_ALLOC_CNID:
149		if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) {
150			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
151			inode->i_sb->s_dirt = 1;
152		}
153		hfsplus_inode_write_fork(inode, &vhdr->alloc_file);
154		break;
155	case HFSPLUS_START_CNID:
156		if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) {
157			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
158			inode->i_sb->s_dirt = 1;
159		}
160		hfsplus_inode_write_fork(inode, &vhdr->start_file);
161		break;
162	case HFSPLUS_ATTR_CNID:
163		if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) {
164			HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
165			inode->i_sb->s_dirt = 1;
166		}
167		hfsplus_inode_write_fork(inode, &vhdr->attr_file);
168		hfs_btree_write(&hfsplus_handle, HFSPLUS_SB(inode->i_sb).attr_tree);
169		break;
170	}
171	return ret;
172}
173
174static void hfsplus_clear_inode(struct inode *inode)
175{
176	dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino);
177	if (HFSPLUS_IS_RSRC(inode)) {
178		HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL;
179		iput(HFSPLUS_I(inode).rsrc_inode);
180	}
181}
182
183static void hfsplus_write_super(struct super_block *sb)
184{
185	struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
186	hfsplus_handle_t hfsplus_handle;
187
188	dprint(DBG_SUPER, "hfsplus_write_super\n");
189	sb->s_dirt = 0;
190	if (sb->s_flags & MS_RDONLY)
191		/* warn? */
192		return;
193
194	if (hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle))
195		return;
196
197	vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks);
198	vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc);
199	vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid);
200	vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count);
201	vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count);
202
203	if (hfsplus_handle.journaled == HFSPLUS_JOURNAL_PRESENT) {
204		int err;
205
206		BUG_ON(!buffer_mapped(HFSPLUS_SB(sb).s_vhbh));
207
208		err = hfsplus_journal_get_write_access(__FUNCTION__, &hfsplus_handle, HFSPLUS_SB(sb).s_vhbh);
209		if (err) {
210			printk(KERN_ERR "%s: Error in journal_get_write_access\n", __FUNCTION__);
211			goto write_super_out;
212		}
213		err = hfsplus_jbd_dirty_metadata(hfsplus_handle.handle, HFSPLUS_SB(sb).s_vhbh);
214		if (err) {
215			printk(KERN_ERR "%s: Error in hfsplus_jbd_dirty_metadata\n", __FUNCTION__);
216			goto write_super_out;
217		}
218	}
219	else
220		mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
221	if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) {
222		if (HFSPLUS_SB(sb).sect_count) {
223			struct buffer_head *bh;
224			u32 block, offset;
225
226			block = HFSPLUS_SB(sb).blockoffset;
227			block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9);
228			offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1);
229			printk("backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset,
230				HFSPLUS_SB(sb).sect_count, block, offset);
231			bh = sb_bread(sb, block);
232			if (bh) {
233				vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
234				if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
235					memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr));
236					if (hfsplus_handle.journaled == HFSPLUS_JOURNAL_PRESENT)
237						hfsplus_jbd_dirty_metadata(hfsplus_handle.handle, bh);
238					else
239						mark_buffer_dirty(bh);
240					brelse(bh);
241				} else
242					printk("backup not found!\n");
243			}
244		}
245		HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP;
246	}
247write_super_out:
248	hfsplus_journal_stop(&hfsplus_handle);
249}
250
251static void hfsplus_put_super(struct super_block *sb)
252{
253	dprint(DBG_SUPER, "hfsplus_put_super\n");
254	if (!sb->s_fs_info)
255		return;
256	hfsplus_journalled_deinit(sb);
257	if (!(sb->s_flags & MS_RDONLY) && HFSPLUS_SB(sb).s_vhdr) {
258		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
259
260		vhdr->modify_date = hfsp_now2mt();
261		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
262		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
263		mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
264		sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
265	}
266
267	hfs_btree_close(HFSPLUS_SB(sb).cat_tree);
268	hfs_btree_close(HFSPLUS_SB(sb).ext_tree);
269	iput(HFSPLUS_SB(sb).alloc_file);
270	iput(HFSPLUS_SB(sb).hidden_dir);
271	if (HFSPLUS_SB(sb).s_vhbh)
272		brelse(HFSPLUS_SB(sb).s_vhbh);
273	if (HFSPLUS_SB(sb).nls)
274		unload_nls(HFSPLUS_SB(sb).nls);
275	kfree(sb->s_fs_info);
276	sb->s_fs_info = NULL;
277}
278
279static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
280{
281	struct super_block *sb = dentry->d_sb;
282
283	buf->f_type = HFSPLUS_SUPER_MAGIC;
284	buf->f_bsize = sb->s_blocksize;
285	buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift;
286	buf->f_bfree = HFSPLUS_SB(sb).free_blocks << HFSPLUS_SB(sb).fs_shift;
287	buf->f_bavail = buf->f_bfree;
288	buf->f_files = 0xFFFFFFFF;
289	buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid;
290	buf->f_namelen = HFSPLUS_MAX_STRLEN;
291
292	return 0;
293}
294
295static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
296{
297	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
298		return 0;
299	if (!(*flags & MS_RDONLY)) {
300		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
301		struct hfsplus_sb_info sbi;
302
303		memset(&sbi, 0, sizeof(struct hfsplus_sb_info));
304		sbi.nls = HFSPLUS_SB(sb).nls;
305		if (!hfsplus_parse_options(data, &sbi))
306			return -EINVAL;
307
308		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
309			printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
310					"running fsck.hfsplus is recommended.\n");
311		} else
312		if (sbi.flags & HFSPLUS_SB_FORCE) {
313			/* nothing */
314		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
315			printk("HFS+-fs: Filesystem is marked locked, leaving read-only.\n");
316			sb->s_flags |= MS_RDONLY;
317			*flags |= MS_RDONLY;
318		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
319			if (hfsplus_journalled_check(sb)) {
320				printk("HFS+-fs: Filesystem is marked journaled, leaving read-only.\n");
321				sb->s_flags |= MS_RDONLY;
322				*flags |= MS_RDONLY;
323			} else
324				printk("HFS+-fs: Able to mount journaled hfsplus volume in read-write mode\n");
325		}
326	}
327	return 0;
328}
329
330static const struct super_operations hfsplus_sops = {
331	.alloc_inode	= hfsplus_alloc_inode,
332	.destroy_inode	= hfsplus_destroy_inode,
333	.read_inode	= hfsplus_read_inode,
334	.write_inode	= hfsplus_write_inode,
335	.clear_inode	= hfsplus_clear_inode,
336	.put_super	= hfsplus_put_super,
337	.write_super	= hfsplus_write_super,
338	.statfs		= hfsplus_statfs,
339	.remount_fs	= hfsplus_remount,
340	.show_options	= hfsplus_show_options,
341};
342
343static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
344{
345	struct hfsplus_vh *vhdr;
346	struct hfsplus_sb_info *sbi;
347	hfsplus_cat_entry entry;
348	struct hfs_find_data fd;
349	struct inode *root;
350	struct qstr str;
351	struct nls_table *nls = NULL;
352	hfsplus_handle_t hfsplus_handle;
353	int err = -EINVAL;
354
355	sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
356	if (!sbi)
357		return -ENOMEM;
358
359	memset(sbi, 0, sizeof(HFSPLUS_SB(sb)));
360	sb->s_fs_info = sbi;
361	INIT_HLIST_HEAD(&sbi->rsrc_inodes);
362	hfsplus_fill_defaults(sbi);
363	if (!hfsplus_parse_options(data, sbi)) {
364		printk(KERN_ERR "hfs: unable to parse mount options\n");
365		err = -EINVAL;
366		goto cleanup;
367	}
368
369	/* temporarily use utf8 to correctly find the hidden dir below */
370	nls = sbi->nls;
371	sbi->nls = load_nls("utf8");
372	if (!sbi->nls) {
373		printk(KERN_ERR "hfs: unable to load nls for utf8\n");
374		err = -EINVAL;
375		goto cleanup;
376	}
377
378	/* Grab the volume header */
379	if (hfsplus_read_wrapper(sb)) {
380		if (!silent)
381			printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
382		err = -EINVAL;
383		goto cleanup;
384	}
385	vhdr = HFSPLUS_SB(sb).s_vhdr;
386
387	/* Copy parts of the volume header into the superblock */
388	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
389	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
390	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
391		printk(KERN_ERR "hfs: wrong filesystem version\n");
392		goto cleanup;
393	}
394
395	hfsplus_journalled_init(sb, vhdr);
396	HFSPLUS_SB(sb).jnl.s_journal = NULL;
397	if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) {
398		if (hfsplus_journalled_check(sb)) {
399			if (!silent)
400				printk("HFS+-fs: Error in journal, use the force option at your own risk, mounting read-only.\n");
401			if (HFSPLUS_SB(sb).s_vhdr == NULL) {
402				printk("HFS+-fs: Error in Volume Header\n");
403				goto cleanup;
404			}
405			sb->s_flags |= MS_RDONLY;
406		} else {
407			hfsplus_jbd_t *journal;
408
409			journal = hfsplus_get_dev_journal(sb);
410			if (journal == NULL) {
411				printk("HFS+-fs: Error in getting journal device from JBD layer\n");
412				sb->s_flags |= MS_RDONLY;
413			} else {
414				int err;
415				err = hfsplus_jbd_load(journal);
416				if (err == 0) {
417					dprint(DBG_JOURNAL, "HFS+-fs: Successfully load journal\n");
418					HFSPLUS_SB(sb).jnl.s_journal = journal;
419				} else {
420					printk("HFS+-fs: Error in loading journal\n");
421					hfsplus_jbd_destroy(journal);
422					sb->s_flags |= MS_RDONLY;
423				}
424			}
425			if (sb->s_flags & MS_RDONLY)
426				printk("HFS+-fs: Error in journal, mounting read-only.\n");
427			else
428				dprint(DBG_JOURNAL, "HFS+-fs: No problem in journal. Should be able to mount hfsplus volume in read-write mode\n");
429		}
430	}
431
432	HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks);
433	HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks);
434	HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc);
435	HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid);
436	HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count);
437	HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count);
438	HFSPLUS_SB(sb).data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
439	if (!HFSPLUS_SB(sb).data_clump_blocks)
440		HFSPLUS_SB(sb).data_clump_blocks = 1;
441	HFSPLUS_SB(sb).rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
442	if (!HFSPLUS_SB(sb).rsrc_clump_blocks)
443		HFSPLUS_SB(sb).rsrc_clump_blocks = 1;
444
445	/* Set up operations so we can load metadata */
446	sb->s_op = &hfsplus_sops;
447	sb->s_maxbytes = MAX_LFS_FILESIZE;
448
449	if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
450		printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, "
451		       "running fsck.hfsplus is recommended.  mounting read-only.\n");
452        /* Foxconn removed start pling 05/31/2010 */
453        /* Ignore this flag to force writeable */
454#if 0
455		sb->s_flags |= MS_RDONLY;
456#endif
457        /* Foxconn removed end pling 05/31/2010 */
458	} else if (sbi->flags & HFSPLUS_SB_FORCE) {
459		/* nothing */
460	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
461			printk("HFS+-fs: Filesystem is marked locked, mounting read-only.\n");
462		sb->s_flags |= MS_RDONLY;
463	}
464	sbi->flags &= ~HFSPLUS_SB_FORCE;
465
466	/* Load metadata objects (B*Trees) */
467	HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
468	if (!HFSPLUS_SB(sb).ext_tree) {
469		printk(KERN_ERR "hfs: failed to load extents file\n");
470		goto cleanup;
471	}
472	HFSPLUS_SB(sb).cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
473	if (!HFSPLUS_SB(sb).cat_tree) {
474		printk(KERN_ERR "hfs: failed to load catalog file\n");
475		goto cleanup;
476	}
477
478	HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID);
479	if (!HFSPLUS_SB(sb).alloc_file) {
480		printk(KERN_ERR "hfs: failed to load allocation file\n");
481		goto cleanup;
482	}
483
484	/* Load the root directory */
485	root = iget(sb, HFSPLUS_ROOT_CNID);
486	sb->s_root = d_alloc_root(root);
487	if (!sb->s_root) {
488		printk(KERN_ERR "hfs: failed to load root directory\n");
489		iput(root);
490		goto cleanup;
491	}
492
493	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
494	str.name = HFSP_HIDDENDIR_NAME;
495	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
496	hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
497	if (hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle))
498		goto cleanup;
499	if (!hfs_brec_read(&hfsplus_handle, &fd, &entry, sizeof(entry))) {
500		hfs_find_exit(&hfsplus_handle, &fd);
501		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) {
502			hfsplus_journal_stop(&hfsplus_handle);
503			goto cleanup;
504		}
505		HFSPLUS_SB(sb).hidden_dir = iget(sb, be32_to_cpu(entry.folder.id));
506		if (!HFSPLUS_SB(sb).hidden_dir) {
507			hfsplus_journal_stop(&hfsplus_handle);
508			goto cleanup;
509		}
510		hfsplus_journal_stop(&hfsplus_handle);
511	} else {
512		hfsplus_journal_stop(&hfsplus_handle);
513		hfs_find_exit(NULL, &fd);
514	}
515
516	if (sb->s_flags & MS_RDONLY)
517		goto out;
518
519	/* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
520	 * all three are registered with Apple for our use
521	 */
522	if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT)
523		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_JOURNALED_VERSION);
524	else
525		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
526	vhdr->modify_date = hfsp_now2mt();
527	vhdr->write_count = cpu_to_be32(be32_to_cpu(vhdr->write_count) + 1);
528	vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
529	vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
530	mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
531	sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
532
533	if (!HFSPLUS_SB(sb).hidden_dir) {
534		printk("HFS+: create hidden dir...\n");
535
536		if (hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle))
537			goto out;
538		HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(&hfsplus_handle, sb, S_IFDIR);
539		hfsplus_create_cat(&hfsplus_handle, HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode,
540				   &str, HFSPLUS_SB(sb).hidden_dir);
541		hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, HFSPLUS_SB(sb).hidden_dir);
542		hfsplus_journal_stop(&hfsplus_handle);
543	}
544out:
545	unload_nls(sbi->nls);
546	sbi->nls = nls;
547	return 0;
548
549cleanup:
550	if (HFSPLUS_SB(sb).jnl.s_journal != NULL)
551		HFSPLUS_SB(sb).jnl.s_journal->j_flags |= JFS_ABORT;
552	hfsplus_put_super(sb);
553	if (nls)
554		unload_nls(nls);
555	return err;
556}
557
558MODULE_AUTHOR("Brad Boyer");
559MODULE_DESCRIPTION("Extended Macintosh Filesystem");
560MODULE_LICENSE("GPL");
561
562static struct kmem_cache *hfsplus_inode_cachep;
563
564static struct inode *hfsplus_alloc_inode(struct super_block *sb)
565{
566	struct hfsplus_inode_info *i;
567
568	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
569	return i ? &i->vfs_inode : NULL;
570}
571
572static void hfsplus_destroy_inode(struct inode *inode)
573{
574	kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode));
575}
576
577#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)
578
579static int hfsplus_get_sb(struct file_system_type *fs_type,
580			  int flags, const char *dev_name, void *data,
581			  struct vfsmount *mnt)
582{
583	return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super,
584			   mnt);
585}
586
587static struct file_system_type hfsplus_fs_type = {
588	.owner		= THIS_MODULE,
589	.name		= "hfsplus",
590	.get_sb		= hfsplus_get_sb,
591	.kill_sb	= kill_block_super,
592	.fs_flags	= FS_REQUIRES_DEV,
593};
594
595static void hfsplus_init_once(void *p, struct kmem_cache *cachep, unsigned long flags)
596{
597	struct hfsplus_inode_info *i = p;
598
599	inode_init_once(&i->vfs_inode);
600}
601
602static int __init init_hfsplus_fs(void)
603{
604	int err;
605
606	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
607		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
608		hfsplus_init_once, NULL);
609	if (!hfsplus_inode_cachep)
610		return -ENOMEM;
611	err = hfsplus_jbd_init();
612	if (err) {
613		printk(KERN_ERR "Error in initializing hfsplus-jbd caches\n");
614		kmem_cache_destroy(hfsplus_inode_cachep);
615		return -ENOMEM;
616	}
617	err = register_filesystem(&hfsplus_fs_type);
618	if (err) {
619		hfsplus_jbd_exit();
620		kmem_cache_destroy(hfsplus_inode_cachep);
621	}
622	return err;
623}
624
625static void __exit exit_hfsplus_fs(void)
626{
627	unregister_filesystem(&hfsplus_fs_type);
628	hfsplus_jbd_exit();
629	kmem_cache_destroy(hfsplus_inode_cachep);
630}
631
632module_init(init_hfsplus_fs)
633module_exit(exit_hfsplus_fs)
634