super.c 17.7 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 478 479
	if (vhdr->attr_file.total_blocks != 0) {
		sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
		if (!sbi->attr_tree) {
480
			pr_err("failed to load attributes file\n");
481 482 483 484
			goto out_close_cat_tree;
		}
	}
	sb->s_xattr = hfsplus_xattr_handlers;
L
Linus Torvalds 已提交
485

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

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

A
Al Viro 已提交
502 503 504 505 506 507 508
	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 已提交
509 510
	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
	str.name = HFSP_HIDDENDIR_NAME;
511 512 513
	err = hfs_find_init(sbi->cat_tree, &fd);
	if (err)
		goto out_put_root;
L
Linus Torvalds 已提交
514 515 516 517
	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))
518
			goto out_put_root;
519 520 521
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
522
			goto out_put_root;
523
		}
524
		sbi->hidden_dir = inode;
L
Linus Torvalds 已提交
525 526 527
	} else
		hfs_find_exit(&fd);

528 529 530 531 532 533 534 535 536 537 538
	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 已提交
539

540 541 542
		if (!sbi->hidden_dir) {
			mutex_lock(&sbi->vh_mutex);
			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
543 544 545 546 547 548 549
			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);
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
			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);
567
				goto out_put_hidden_dir;
568
			}
569

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
630 631
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

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

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

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

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

static int __init init_hfsplus_fs(void)
{
	int err;

	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
660
		hfsplus_init_once);
L
Linus Torvalds 已提交
661 662
	if (!hfsplus_inode_cachep)
		return -ENOMEM;
663 664 665
	err = hfsplus_create_attr_tree_cache();
	if (err)
		goto destroy_inode_cache;
L
Linus Torvalds 已提交
666 667
	err = register_filesystem(&hfsplus_fs_type);
	if (err)
668 669 670 671 672 673 674 675 676
		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 已提交
677 678 679 680 681 682
	return err;
}

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
683 684 685 686 687 688

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

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)