super.c 17.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 *  linux/fs/hfsplus/super.c
 *
 * Copyright (C) 2001
 * Brad Boyer (flar@allandria.com)
 * (C) 2003 Ardis Technologies <roman@ardistech.com>
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/pagemap.h>
13
#include <linux/blkdev.h>
L
Linus Torvalds 已提交
14 15 16 17 18 19 20 21 22
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/vfs.h>
#include <linux/nls.h>

static struct inode *hfsplus_alloc_inode(struct super_block *sb);
static void hfsplus_destroy_inode(struct inode *inode);

#include "hfsplus_fs.h"
23
#include "xattr.h"
L
Linus Torvalds 已提交
24

C
Christoph Hellwig 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
static int hfsplus_system_read_inode(struct inode *inode)
{
	struct hfsplus_vh *vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr;

	switch (inode->i_ino) {
	case HFSPLUS_EXT_CNID:
		hfsplus_inode_read_fork(inode, &vhdr->ext_file);
		inode->i_mapping->a_ops = &hfsplus_btree_aops;
		break;
	case HFSPLUS_CAT_CNID:
		hfsplus_inode_read_fork(inode, &vhdr->cat_file);
		inode->i_mapping->a_ops = &hfsplus_btree_aops;
		break;
	case HFSPLUS_ALLOC_CNID:
		hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
		inode->i_mapping->a_ops = &hfsplus_aops;
		break;
	case HFSPLUS_START_CNID:
		hfsplus_inode_read_fork(inode, &vhdr->start_file);
		break;
	case HFSPLUS_ATTR_CNID:
		hfsplus_inode_read_fork(inode, &vhdr->attr_file);
		inode->i_mapping->a_ops = &hfsplus_btree_aops;
		break;
	default:
		return -EIO;
	}

	return 0;
}

56
struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
L
Linus Torvalds 已提交
57 58
{
	struct hfs_find_data fd;
59
	struct inode *inode;
C
Christoph Hellwig 已提交
60
	int err;
61 62 63 64 65 66

	inode = iget_locked(sb, ino);
	if (!inode)
		return ERR_PTR(-ENOMEM);
	if (!(inode->i_state & I_NEW))
		return inode;
L
Linus Torvalds 已提交
67

68 69 70
	INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
	mutex_init(&HFSPLUS_I(inode)->extents_lock);
	HFSPLUS_I(inode)->flags = 0;
C
Christoph Hellwig 已提交
71
	HFSPLUS_I(inode)->extent_state = 0;
72 73
	HFSPLUS_I(inode)->rsrc_inode = NULL;
	atomic_set(&HFSPLUS_I(inode)->opencnt, 0);
L
Linus Torvalds 已提交
74

C
Christoph Hellwig 已提交
75 76
	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
	    inode->i_ino == HFSPLUS_ROOT_CNID) {
77 78 79 80 81 82 83
		err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
		if (!err) {
			err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
			if (!err)
				err = hfsplus_cat_read_inode(inode, &fd);
			hfs_find_exit(&fd);
		}
C
Christoph Hellwig 已提交
84 85
	} else {
		err = hfsplus_system_read_inode(inode);
L
Linus Torvalds 已提交
86
	}
C
Christoph Hellwig 已提交
87 88 89 90

	if (err) {
		iget_failed(inode);
		return ERR_PTR(err);
L
Linus Torvalds 已提交
91 92
	}

93 94
	unlock_new_inode(inode);
	return inode;
L
Linus Torvalds 已提交
95 96
}

97
static int hfsplus_system_write_inode(struct inode *inode)
L
Linus Torvalds 已提交
98
{
99
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
100 101 102
	struct hfsplus_vh *vhdr = sbi->s_vhdr;
	struct hfsplus_fork_raw *fork;
	struct hfs_btree *tree = NULL;
L
Linus Torvalds 已提交
103 104 105

	switch (inode->i_ino) {
	case HFSPLUS_EXT_CNID:
106 107
		fork = &vhdr->ext_file;
		tree = sbi->ext_tree;
L
Linus Torvalds 已提交
108 109
		break;
	case HFSPLUS_CAT_CNID:
110 111
		fork = &vhdr->cat_file;
		tree = sbi->cat_tree;
L
Linus Torvalds 已提交
112 113
		break;
	case HFSPLUS_ALLOC_CNID:
114
		fork = &vhdr->alloc_file;
L
Linus Torvalds 已提交
115 116
		break;
	case HFSPLUS_START_CNID:
117
		fork = &vhdr->start_file;
L
Linus Torvalds 已提交
118 119
		break;
	case HFSPLUS_ATTR_CNID:
120 121
		fork = &vhdr->attr_file;
		tree = sbi->attr_tree;
122
		break;
123 124 125 126 127
	default:
		return -EIO;
	}

	if (fork->total_size != cpu_to_be64(inode->i_size)) {
128
		set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags);
129
		hfsplus_mark_mdb_dirty(inode->i_sb);
L
Linus Torvalds 已提交
130
	}
131
	hfsplus_inode_write_fork(inode, fork);
132 133 134
	if (tree) {
		int err = hfs_btree_write(tree);
		if (err) {
135
			pr_err("b-tree write err: %d, ino %lu\n",
136 137 138 139
					err, inode->i_ino);
			return err;
		}
	}
140 141 142 143 144 145
	return 0;
}

static int hfsplus_write_inode(struct inode *inode,
		struct writeback_control *wbc)
{
146 147
	int err;

148
	hfs_dbg(INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
149

150 151 152
	err = hfsplus_ext_write_extent(inode);
	if (err)
		return err;
153 154 155 156 157 158

	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
	    inode->i_ino == HFSPLUS_ROOT_CNID)
		return hfsplus_cat_write_inode(inode);
	else
		return hfsplus_system_write_inode(inode);
L
Linus Torvalds 已提交
159 160
}

161
static void hfsplus_evict_inode(struct inode *inode)
L
Linus Torvalds 已提交
162
{
163
	hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino);
164
	truncate_inode_pages(&inode->i_data, 0);
165
	clear_inode(inode);
L
Linus Torvalds 已提交
166
	if (HFSPLUS_IS_RSRC(inode)) {
167 168
		HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
		iput(HFSPLUS_I(inode)->rsrc_inode);
L
Linus Torvalds 已提交
169 170 171
	}
}

172
static int hfsplus_sync_fs(struct super_block *sb, int wait)
L
Linus Torvalds 已提交
173
{
174 175
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
	struct hfsplus_vh *vhdr = sbi->s_vhdr;
176 177
	int write_backup = 0;
	int error, error2;
L
Linus Torvalds 已提交
178

179 180
	if (!wait)
		return 0;
L
Linus Torvalds 已提交
181

182
	hfs_dbg(SUPER, "hfsplus_sync_fs\n");
183

184 185 186 187 188 189 190 191 192 193 194 195
	/*
	 * Explicitly write out the special metadata inodes.
	 *
	 * While these special inodes are marked as hashed and written
	 * out peridocically by the flusher threads we redirty them
	 * during writeout of normal inodes, and thus the life lock
	 * prevents us from getting the latest state to disk.
	 */
	error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping);
	error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
	if (!error)
		error = error2;
196 197 198 199 200 201
	if (sbi->attr_tree) {
		error2 =
		    filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
		if (!error)
			error = error2;
	}
202 203 204 205
	error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
	if (!error)
		error = error2;

206 207
	mutex_lock(&sbi->vh_mutex);
	mutex_lock(&sbi->alloc_mutex);
208 209 210 211
	vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
	vhdr->next_cnid = cpu_to_be32(sbi->next_cnid);
	vhdr->folder_count = cpu_to_be32(sbi->folder_count);
	vhdr->file_count = cpu_to_be32(sbi->file_count);
L
Linus Torvalds 已提交
212

213
	if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
214 215
		memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
		write_backup = 1;
L
Linus Torvalds 已提交
216
	}
217

218
	error2 = hfsplus_submit_bio(sb,
219
				   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
220
				   sbi->s_vhdr_buf, NULL, WRITE_SYNC);
221 222
	if (!error)
		error = error2;
223 224 225
	if (!write_backup)
		goto out;

226
	error2 = hfsplus_submit_bio(sb,
227
				  sbi->part_start + sbi->sect_count - 2,
228
				  sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
229 230 231
	if (!error)
		error2 = error;
out:
232
	mutex_unlock(&sbi->alloc_mutex);
233
	mutex_unlock(&sbi->vh_mutex);
234 235 236 237

	if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
		blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL);

238
	return error;
C
Christoph Hellwig 已提交
239 240
}

241
static void delayed_sync_fs(struct work_struct *work)
C
Christoph Hellwig 已提交
242
{
243
	int err;
244 245 246 247 248 249 250 251
	struct hfsplus_sb_info *sbi;

	sbi = container_of(work, struct hfsplus_sb_info, sync_work.work);

	spin_lock(&sbi->work_lock);
	sbi->work_queued = 0;
	spin_unlock(&sbi->work_lock);

252 253
	err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1);
	if (err)
254
		pr_err("delayed sync fs err %d\n", err);
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
}

void hfsplus_mark_mdb_dirty(struct super_block *sb)
{
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
	unsigned long delay;

	if (sb->s_flags & MS_RDONLY)
		return;

	spin_lock(&sbi->work_lock);
	if (!sbi->work_queued) {
		delay = msecs_to_jiffies(dirty_writeback_interval * 10);
		queue_delayed_work(system_long_wq, &sbi->sync_work, delay);
		sbi->work_queued = 1;
	}
	spin_unlock(&sbi->work_lock);
L
Linus Torvalds 已提交
272 273 274 275
}

static void hfsplus_put_super(struct super_block *sb)
{
276 277
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);

278
	hfs_dbg(SUPER, "hfsplus_put_super\n");
279

280 281
	cancel_delayed_work_sync(&sbi->sync_work);

282 283
	if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) {
		struct hfsplus_vh *vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
284 285 286 287

		vhdr->modify_date = hfsp_now2mt();
		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
288 289

		hfsplus_sync_fs(sb, 1);
L
Linus Torvalds 已提交
290 291
	}

292
	hfs_btree_close(sbi->attr_tree);
293 294 295 296
	hfs_btree_close(sbi->cat_tree);
	hfs_btree_close(sbi->ext_tree);
	iput(sbi->alloc_file);
	iput(sbi->hidden_dir);
297 298
	kfree(sbi->s_vhdr_buf);
	kfree(sbi->s_backup_vhdr_buf);
299
	unload_nls(sbi->nls);
300 301
	kfree(sb->s_fs_info);
	sb->s_fs_info = NULL;
L
Linus Torvalds 已提交
302 303
}

304
static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
L
Linus Torvalds 已提交
305
{
306
	struct super_block *sb = dentry->d_sb;
307
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
308
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
309

L
Linus Torvalds 已提交
310 311
	buf->f_type = HFSPLUS_SUPER_MAGIC;
	buf->f_bsize = sb->s_blocksize;
312 313
	buf->f_blocks = sbi->total_blocks << sbi->fs_shift;
	buf->f_bfree = sbi->free_blocks << sbi->fs_shift;
L
Linus Torvalds 已提交
314 315
	buf->f_bavail = buf->f_bfree;
	buf->f_files = 0xFFFFFFFF;
316
	buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid;
317 318
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
L
Linus Torvalds 已提交
319 320 321 322 323 324 325 326 327 328
	buf->f_namelen = HFSPLUS_MAX_STRLEN;

	return 0;
}

static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
{
	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
		return 0;
	if (!(*flags & MS_RDONLY)) {
329
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
330
		int force = 0;
331

332
		if (!hfsplus_parse_options_remount(data, &force))
333
			return -EINVAL;
L
Linus Torvalds 已提交
334 335

		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
336
			pr_warn("filesystem was not cleanly unmounted, running fsck.hfsplus is recommended.  leaving read-only.\n");
L
Linus Torvalds 已提交
337 338
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
339
		} else if (force) {
340
			/* nothing */
341 342
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
343
			pr_warn("filesystem is marked locked, leaving read-only.\n");
L
Linus Torvalds 已提交
344 345
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
346 347
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
348
			pr_warn("filesystem is marked journaled, leaving read-only.\n");
349 350
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
L
Linus Torvalds 已提交
351 352 353 354 355
		}
	}
	return 0;
}

356
static const struct super_operations hfsplus_sops = {
L
Linus Torvalds 已提交
357 358 359
	.alloc_inode	= hfsplus_alloc_inode,
	.destroy_inode	= hfsplus_destroy_inode,
	.write_inode	= hfsplus_write_inode,
360
	.evict_inode	= hfsplus_evict_inode,
L
Linus Torvalds 已提交
361
	.put_super	= hfsplus_put_super,
C
Christoph Hellwig 已提交
362
	.sync_fs	= hfsplus_sync_fs,
L
Linus Torvalds 已提交
363 364
	.statfs		= hfsplus_statfs,
	.remount_fs	= hfsplus_remount,
R
Roman Zippel 已提交
365
	.show_options	= hfsplus_show_options,
L
Linus Torvalds 已提交
366 367 368 369 370 371 372 373
};

static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
{
	struct hfsplus_vh *vhdr;
	struct hfsplus_sb_info *sbi;
	hfsplus_cat_entry entry;
	struct hfs_find_data fd;
374
	struct inode *root, *inode;
L
Linus Torvalds 已提交
375 376
	struct qstr str;
	struct nls_table *nls = NULL;
377
	u64 last_fs_block, last_fs_page;
378
	int err;
L
Linus Torvalds 已提交
379

380
	err = -ENOMEM;
381
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
L
Linus Torvalds 已提交
382
	if (!sbi)
383
		goto out;
L
Linus Torvalds 已提交
384 385

	sb->s_fs_info = sbi;
C
Christoph Hellwig 已提交
386
	mutex_init(&sbi->alloc_mutex);
387
	mutex_init(&sbi->vh_mutex);
388 389
	spin_lock_init(&sbi->work_lock);
	INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
R
Roman Zippel 已提交
390
	hfsplus_fill_defaults(sbi);
391 392

	err = -EINVAL;
R
Roman Zippel 已提交
393
	if (!hfsplus_parse_options(data, sbi)) {
394
		pr_err("unable to parse mount options\n");
395
		goto out_unload_nls;
L
Linus Torvalds 已提交
396 397 398 399 400
	}

	/* temporarily use utf8 to correctly find the hidden dir below */
	nls = sbi->nls;
	sbi->nls = load_nls("utf8");
J
Joshua Kwan 已提交
401
	if (!sbi->nls) {
402
		pr_err("unable to load nls for utf8\n");
403
		goto out_unload_nls;
L
Linus Torvalds 已提交
404 405 406 407 408
	}

	/* Grab the volume header */
	if (hfsplus_read_wrapper(sb)) {
		if (!silent)
409
			pr_warn("unable to find HFS+ superblock\n");
410
		goto out_unload_nls;
L
Linus Torvalds 已提交
411
	}
412
	vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
413 414

	/* Copy parts of the volume header into the superblock */
D
David Elliott 已提交
415 416 417
	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
418
		pr_err("wrong filesystem version\n");
419
		goto out_free_vhdr;
L
Linus Torvalds 已提交
420
	}
421 422 423 424 425 426 427 428 429 430 431 432 433
	sbi->total_blocks = be32_to_cpu(vhdr->total_blocks);
	sbi->free_blocks = be32_to_cpu(vhdr->free_blocks);
	sbi->next_cnid = be32_to_cpu(vhdr->next_cnid);
	sbi->file_count = be32_to_cpu(vhdr->file_count);
	sbi->folder_count = be32_to_cpu(vhdr->folder_count);
	sbi->data_clump_blocks =
		be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift;
	if (!sbi->data_clump_blocks)
		sbi->data_clump_blocks = 1;
	sbi->rsrc_clump_blocks =
		be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift;
	if (!sbi->rsrc_clump_blocks)
		sbi->rsrc_clump_blocks = 1;
L
Linus Torvalds 已提交
434

435 436 437 438 439 440 441
	err = -EFBIG;
	last_fs_block = sbi->total_blocks - 1;
	last_fs_page = (last_fs_block << sbi->alloc_blksz_shift) >>
			PAGE_CACHE_SHIFT;

	if ((last_fs_block > (sector_t)(~0ULL) >> (sbi->alloc_blksz_shift - 9)) ||
	    (last_fs_page > (pgoff_t)(~0ULL))) {
442
		pr_err("filesystem size too large\n");
443 444 445
		goto out_free_vhdr;
	}

L
Linus Torvalds 已提交
446 447 448 449 450
	/* Set up operations so we can load metadata */
	sb->s_op = &hfsplus_sops;
	sb->s_maxbytes = MAX_LFS_FILESIZE;

	if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
451
		pr_warn("Filesystem was not cleanly unmounted, running fsck.hfsplus is recommended.  mounting read-only.\n");
L
Linus Torvalds 已提交
452
		sb->s_flags |= MS_RDONLY;
453
	} else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
454
		/* nothing */
L
Linus Torvalds 已提交
455
	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
456
		pr_warn("Filesystem is marked locked, mounting read-only.\n");
L
Linus Torvalds 已提交
457
		sb->s_flags |= MS_RDONLY;
458 459
	} else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) &&
			!(sb->s_flags & MS_RDONLY)) {
460
		pr_warn("write access to a journaled filesystem is not supported, use the force option at your own risk, mounting read-only.\n");
461
		sb->s_flags |= MS_RDONLY;
L
Linus Torvalds 已提交
462 463
	}

464 465
	err = -EINVAL;

L
Linus Torvalds 已提交
466
	/* Load metadata objects (B*Trees) */
467 468
	sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
	if (!sbi->ext_tree) {
469
		pr_err("failed to load extents file\n");
470
		goto out_free_vhdr;
L
Linus Torvalds 已提交
471
	}
472 473
	sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
	if (!sbi->cat_tree) {
474
		pr_err("failed to load catalog file\n");
475
		goto out_close_ext_tree;
L
Linus Torvalds 已提交
476
	}
477
	atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
478 479 480
	if (vhdr->attr_file.total_blocks != 0) {
		sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
		if (!sbi->attr_tree) {
481
			pr_err("failed to load attributes file\n");
482 483
			goto out_close_cat_tree;
		}
484
		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
485 486
	}
	sb->s_xattr = hfsplus_xattr_handlers;
L
Linus Torvalds 已提交
487

488 489
	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
	if (IS_ERR(inode)) {
490
		pr_err("failed to load allocation file\n");
491
		err = PTR_ERR(inode);
492
		goto out_close_attr_tree;
L
Linus Torvalds 已提交
493
	}
494
	sbi->alloc_file = inode;
L
Linus Torvalds 已提交
495 496

	/* Load the root directory */
497 498
	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
	if (IS_ERR(root)) {
499
		pr_err("failed to load root directory\n");
500
		err = PTR_ERR(root);
501
		goto out_put_alloc_file;
L
Linus Torvalds 已提交
502 503
	}

A
Al Viro 已提交
504 505 506 507 508 509 510
	sb->s_d_op = &hfsplus_dentry_operations;
	sb->s_root = d_make_root(root);
	if (!sb->s_root) {
		err = -ENOMEM;
		goto out_put_alloc_file;
	}

L
Linus Torvalds 已提交
511 512
	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
	str.name = HFSP_HIDDENDIR_NAME;
513 514 515
	err = hfs_find_init(sbi->cat_tree, &fd);
	if (err)
		goto out_put_root;
L
Linus Torvalds 已提交
516 517 518 519
	hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
		hfs_find_exit(&fd);
		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
520
			goto out_put_root;
521 522 523
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
524
			goto out_put_root;
525
		}
526
		sbi->hidden_dir = inode;
L
Linus Torvalds 已提交
527 528 529
	} else
		hfs_find_exit(&fd);

530 531 532 533 534 535 536 537 538 539 540
	if (!(sb->s_flags & MS_RDONLY)) {
		/*
		 * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
		 * all three are registered with Apple for our use
		 */
		vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
		vhdr->modify_date = hfsp_now2mt();
		be32_add_cpu(&vhdr->write_count, 1);
		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
		hfsplus_sync_fs(sb, 1);
L
Linus Torvalds 已提交
541

542 543 544
		if (!sbi->hidden_dir) {
			mutex_lock(&sbi->vh_mutex);
			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
545 546 547 548 549 550 551
			if (!sbi->hidden_dir) {
				mutex_unlock(&sbi->vh_mutex);
				err = -ENOMEM;
				goto out_put_root;
			}
			err = hfsplus_create_cat(sbi->hidden_dir->i_ino, root,
						 &str, sbi->hidden_dir);
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
			if (err) {
				mutex_unlock(&sbi->vh_mutex);
				goto out_put_hidden_dir;
			}

			err = hfsplus_init_inode_security(sbi->hidden_dir,
								root, &str);
			if (err == -EOPNOTSUPP)
				err = 0; /* Operation is not supported. */
			else if (err) {
				/*
				 * Try to delete anyway without
				 * error analysis.
				 */
				hfsplus_delete_cat(sbi->hidden_dir->i_ino,
							root, &str);
				mutex_unlock(&sbi->vh_mutex);
569
				goto out_put_hidden_dir;
570
			}
571

572
			mutex_unlock(&sbi->vh_mutex);
573 574 575
			hfsplus_mark_inode_dirty(sbi->hidden_dir,
						 HFSPLUS_I_CAT_DIRTY);
		}
L
Linus Torvalds 已提交
576
	}
577

L
Linus Torvalds 已提交
578 579 580 581
	unload_nls(sbi->nls);
	sbi->nls = nls;
	return 0;

582 583 584
out_put_hidden_dir:
	iput(sbi->hidden_dir);
out_put_root:
A
Al Viro 已提交
585 586
	dput(sb->s_root);
	sb->s_root = NULL;
587 588
out_put_alloc_file:
	iput(sbi->alloc_file);
589 590
out_close_attr_tree:
	hfs_btree_close(sbi->attr_tree);
591 592 593 594 595
out_close_cat_tree:
	hfs_btree_close(sbi->cat_tree);
out_close_ext_tree:
	hfs_btree_close(sbi->ext_tree);
out_free_vhdr:
596 597
	kfree(sbi->s_vhdr_buf);
	kfree(sbi->s_backup_vhdr_buf);
598 599
out_unload_nls:
	unload_nls(sbi->nls);
600
	unload_nls(nls);
601 602
	kfree(sbi);
out:
L
Linus Torvalds 已提交
603 604 605 606 607 608 609
	return err;
}

MODULE_AUTHOR("Brad Boyer");
MODULE_DESCRIPTION("Extended Macintosh Filesystem");
MODULE_LICENSE("GPL");

610
static struct kmem_cache *hfsplus_inode_cachep;
L
Linus Torvalds 已提交
611 612 613 614 615

static struct inode *hfsplus_alloc_inode(struct super_block *sb)
{
	struct hfsplus_inode_info *i;

616
	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
617 618 619
	return i ? &i->vfs_inode : NULL;
}

N
Nick Piggin 已提交
620
static void hfsplus_i_callback(struct rcu_head *head)
L
Linus Torvalds 已提交
621
{
N
Nick Piggin 已提交
622 623
	struct inode *inode = container_of(head, struct inode, i_rcu);

624
	kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
L
Linus Torvalds 已提交
625 626
}

N
Nick Piggin 已提交
627 628 629 630 631
static void hfsplus_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, hfsplus_i_callback);
}

L
Linus Torvalds 已提交
632 633
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

A
Al Viro 已提交
634 635
static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
			  int flags, const char *dev_name, void *data)
L
Linus Torvalds 已提交
636
{
A
Al Viro 已提交
637
	return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
L
Linus Torvalds 已提交
638 639 640 641 642
}

static struct file_system_type hfsplus_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "hfsplus",
A
Al Viro 已提交
643
	.mount		= hfsplus_mount,
L
Linus Torvalds 已提交
644 645 646
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
};
647
MODULE_ALIAS_FS("hfsplus");
L
Linus Torvalds 已提交
648

649
static void hfsplus_init_once(void *p)
L
Linus Torvalds 已提交
650 651 652
{
	struct hfsplus_inode_info *i = p;

C
Christoph Lameter 已提交
653
	inode_init_once(&i->vfs_inode);
L
Linus Torvalds 已提交
654 655 656 657 658 659 660 661
}

static int __init init_hfsplus_fs(void)
{
	int err;

	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
662
		hfsplus_init_once);
L
Linus Torvalds 已提交
663 664
	if (!hfsplus_inode_cachep)
		return -ENOMEM;
665 666 667
	err = hfsplus_create_attr_tree_cache();
	if (err)
		goto destroy_inode_cache;
L
Linus Torvalds 已提交
668 669
	err = register_filesystem(&hfsplus_fs_type);
	if (err)
670 671 672 673 674 675 676 677 678
		goto destroy_attr_tree_cache;
	return 0;

destroy_attr_tree_cache:
	hfsplus_destroy_attr_tree_cache();

destroy_inode_cache:
	kmem_cache_destroy(hfsplus_inode_cachep);

L
Linus Torvalds 已提交
679 680 681 682 683 684
	return err;
}

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
685 686 687 688 689 690

	/*
	 * Make sure all delayed rcu free inodes are flushed before we
	 * destroy cache.
	 */
	rcu_barrier();
691
	hfsplus_destroy_attr_tree_cache();
692
	kmem_cache_destroy(hfsplus_inode_cachep);
L
Linus Torvalds 已提交
693 694 695 696
}

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)