super.c 18.0 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>
14
#include <linux/backing-dev.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23
#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"
24
#include "xattr.h"
L
Linus Torvalds 已提交
25

C
Christoph Hellwig 已提交
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 56
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;
}

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

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

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

C
Christoph Hellwig 已提交
77 78
	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
	    inode->i_ino == HFSPLUS_ROOT_CNID) {
79 80 81 82 83 84 85
		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 已提交
86 87
	} else {
		err = hfsplus_system_read_inode(inode);
L
Linus Torvalds 已提交
88
	}
C
Christoph Hellwig 已提交
89 90 91 92

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

95 96
	unlock_new_inode(inode);
	return inode;
L
Linus Torvalds 已提交
97 98
}

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

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

	if (fork->total_size != cpu_to_be64(inode->i_size)) {
130
		set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags);
131
		hfsplus_mark_mdb_dirty(inode->i_sb);
L
Linus Torvalds 已提交
132
	}
133
	hfsplus_inode_write_fork(inode, fork);
134 135
	if (tree) {
		int err = hfs_btree_write(tree);
136

137
		if (err) {
138
			pr_err("b-tree write err: %d, ino %lu\n",
139
			       err, inode->i_ino);
140 141 142
			return err;
		}
	}
143 144 145 146 147 148
	return 0;
}

static int hfsplus_write_inode(struct inode *inode,
		struct writeback_control *wbc)
{
149 150
	int err;

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

153 154 155
	err = hfsplus_ext_write_extent(inode);
	if (err)
		return err;
156 157 158 159 160 161

	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 已提交
162 163
}

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

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

182 183
	if (!wait)
		return 0;
L
Linus Torvalds 已提交
184

185
	hfs_dbg(SUPER, "hfsplus_sync_fs\n");
186

187 188 189 190 191 192 193 194 195 196 197 198
	/*
	 * 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;
199 200 201 202 203 204
	if (sbi->attr_tree) {
		error2 =
		    filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
		if (!error)
			error = error2;
	}
205 206 207 208
	error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
	if (!error)
		error = error2;

209 210
	mutex_lock(&sbi->vh_mutex);
	mutex_lock(&sbi->alloc_mutex);
211 212 213 214
	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 已提交
215

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

221
	error2 = hfsplus_submit_bio(sb,
222
				   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
M
Mike Christie 已提交
223
				   sbi->s_vhdr_buf, NULL, REQ_OP_WRITE,
224
				   REQ_SYNC);
225 226
	if (!error)
		error = error2;
227 228 229
	if (!write_backup)
		goto out;

230
	error2 = hfsplus_submit_bio(sb,
231
				  sbi->part_start + sbi->sect_count - 2,
M
Mike Christie 已提交
232
				  sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE,
233
				  REQ_SYNC);
234 235 236
	if (!error)
		error2 = error;
out:
237
	mutex_unlock(&sbi->alloc_mutex);
238
	mutex_unlock(&sbi->vh_mutex);
239 240 241 242

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

243
	return error;
C
Christoph Hellwig 已提交
244 245
}

246
static void delayed_sync_fs(struct work_struct *work)
C
Christoph Hellwig 已提交
247
{
248
	int err;
249 250 251 252 253 254 255 256
	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);

257 258
	err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1);
	if (err)
259
		pr_err("delayed sync fs err %d\n", err);
260 261 262 263 264 265 266
}

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

267
	if (sb_rdonly(sb))
268 269 270 271 272 273 274 275 276
		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 已提交
277 278 279 280
}

static void hfsplus_put_super(struct super_block *sb)
{
281 282
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);

283
	hfs_dbg(SUPER, "hfsplus_put_super\n");
284

285 286
	cancel_delayed_work_sync(&sbi->sync_work);

287
	if (!sb_rdonly(sb) && sbi->s_vhdr) {
288
		struct hfsplus_vh *vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
289 290 291 292

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

		hfsplus_sync_fs(sb, 1);
L
Linus Torvalds 已提交
295 296
	}

297
	hfs_btree_close(sbi->attr_tree);
298 299 300 301
	hfs_btree_close(sbi->cat_tree);
	hfs_btree_close(sbi->ext_tree);
	iput(sbi->alloc_file);
	iput(sbi->hidden_dir);
302 303
	kfree(sbi->s_vhdr_buf);
	kfree(sbi->s_backup_vhdr_buf);
304
	unload_nls(sbi->nls);
305 306
	kfree(sb->s_fs_info);
	sb->s_fs_info = NULL;
L
Linus Torvalds 已提交
307 308
}

309
static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
L
Linus Torvalds 已提交
310
{
311
	struct super_block *sb = dentry->d_sb;
312
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
313
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
314

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

	return 0;
}

static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
{
331
	sync_filesystem(sb);
332
	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
L
Linus Torvalds 已提交
333
		return 0;
334
	if (!(*flags & SB_RDONLY)) {
335
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
336
		int force = 0;
337

338
		if (!hfsplus_parse_options_remount(data, &force))
339
			return -EINVAL;
L
Linus Torvalds 已提交
340 341

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

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

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;
380
	struct inode *root, *inode;
L
Linus Torvalds 已提交
381 382
	struct qstr str;
	struct nls_table *nls = NULL;
383
	u64 last_fs_block, last_fs_page;
384
	int err;
L
Linus Torvalds 已提交
385

386
	err = -ENOMEM;
387
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
L
Linus Torvalds 已提交
388
	if (!sbi)
389
		goto out;
L
Linus Torvalds 已提交
390 391

	sb->s_fs_info = sbi;
C
Christoph Hellwig 已提交
392
	mutex_init(&sbi->alloc_mutex);
393
	mutex_init(&sbi->vh_mutex);
394 395
	spin_lock_init(&sbi->work_lock);
	INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
R
Roman Zippel 已提交
396
	hfsplus_fill_defaults(sbi);
397 398

	err = -EINVAL;
R
Roman Zippel 已提交
399
	if (!hfsplus_parse_options(data, sbi)) {
400
		pr_err("unable to parse mount options\n");
401
		goto out_unload_nls;
L
Linus Torvalds 已提交
402 403 404 405 406
	}

	/* temporarily use utf8 to correctly find the hidden dir below */
	nls = sbi->nls;
	sbi->nls = load_nls("utf8");
J
Joshua Kwan 已提交
407
	if (!sbi->nls) {
408
		pr_err("unable to load nls for utf8\n");
409
		goto out_unload_nls;
L
Linus Torvalds 已提交
410 411 412 413 414
	}

	/* Grab the volume header */
	if (hfsplus_read_wrapper(sb)) {
		if (!silent)
415
			pr_warn("unable to find HFS+ superblock\n");
416
		goto out_unload_nls;
L
Linus Torvalds 已提交
417
	}
418
	vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
419 420

	/* Copy parts of the volume header into the superblock */
D
David Elliott 已提交
421 422 423
	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
424
		pr_err("wrong filesystem version\n");
425
		goto out_free_vhdr;
L
Linus Torvalds 已提交
426
	}
427 428 429 430 431 432 433 434 435 436 437 438 439
	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 已提交
440

441 442 443
	err = -EFBIG;
	last_fs_block = sbi->total_blocks - 1;
	last_fs_page = (last_fs_block << sbi->alloc_blksz_shift) >>
444
			PAGE_SHIFT;
445 446 447

	if ((last_fs_block > (sector_t)(~0ULL) >> (sbi->alloc_blksz_shift - 9)) ||
	    (last_fs_page > (pgoff_t)(~0ULL))) {
448
		pr_err("filesystem size too large\n");
449 450 451
		goto out_free_vhdr;
	}

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

470 471
	err = -EINVAL;

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

494 495
	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
	if (IS_ERR(inode)) {
496
		pr_err("failed to load allocation file\n");
497
		err = PTR_ERR(inode);
498
		goto out_close_attr_tree;
L
Linus Torvalds 已提交
499
	}
500
	sbi->alloc_file = inode;
L
Linus Torvalds 已提交
501 502

	/* Load the root directory */
503 504
	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
	if (IS_ERR(root)) {
505
		pr_err("failed to load root directory\n");
506
		err = PTR_ERR(root);
507
		goto out_put_alloc_file;
L
Linus Torvalds 已提交
508 509
	}

A
Al Viro 已提交
510 511 512 513 514 515 516
	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 已提交
517 518
	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
	str.name = HFSP_HIDDENDIR_NAME;
519 520 521
	err = hfs_find_init(sbi->cat_tree, &fd);
	if (err)
		goto out_put_root;
S
Sougata Santra 已提交
522 523 524
	err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
	if (unlikely(err < 0))
		goto out_put_root;
L
Linus Torvalds 已提交
525 526 527
	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
		hfs_find_exit(&fd);
		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
528
			goto out_put_root;
529 530 531
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
532
			goto out_put_root;
533
		}
534
		sbi->hidden_dir = inode;
L
Linus Torvalds 已提交
535 536 537
	} else
		hfs_find_exit(&fd);

538
	if (!sb_rdonly(sb)) {
539 540 541 542 543 544 545 546 547 548
		/*
		 * 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 已提交
549

550 551 552
		if (!sbi->hidden_dir) {
			mutex_lock(&sbi->vh_mutex);
			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
553 554 555 556 557 558 559
			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);
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
			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);
577
				goto out_put_hidden_dir;
578
			}
579

580
			mutex_unlock(&sbi->vh_mutex);
581 582 583
			hfsplus_mark_inode_dirty(sbi->hidden_dir,
						 HFSPLUS_I_CAT_DIRTY);
		}
L
Linus Torvalds 已提交
584
	}
585

L
Linus Torvalds 已提交
586 587 588 589
	unload_nls(sbi->nls);
	sbi->nls = nls;
	return 0;

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

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

618
static struct kmem_cache *hfsplus_inode_cachep;
L
Linus Torvalds 已提交
619 620 621 622 623

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

624
	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
625 626 627
	return i ? &i->vfs_inode : NULL;
}

N
Nick Piggin 已提交
628
static void hfsplus_i_callback(struct rcu_head *head)
L
Linus Torvalds 已提交
629
{
N
Nick Piggin 已提交
630 631
	struct inode *inode = container_of(head, struct inode, i_rcu);

632
	kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
L
Linus Torvalds 已提交
633 634
}

N
Nick Piggin 已提交
635 636 637 638 639
static void hfsplus_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, hfsplus_i_callback);
}

L
Linus Torvalds 已提交
640 641
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

A
Al Viro 已提交
642 643
static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
			  int flags, const char *dev_name, void *data)
L
Linus Torvalds 已提交
644
{
A
Al Viro 已提交
645
	return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
L
Linus Torvalds 已提交
646 647 648 649 650
}

static struct file_system_type hfsplus_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "hfsplus",
A
Al Viro 已提交
651
	.mount		= hfsplus_mount,
L
Linus Torvalds 已提交
652 653 654
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
};
655
MODULE_ALIAS_FS("hfsplus");
L
Linus Torvalds 已提交
656

657
static void hfsplus_init_once(void *p)
L
Linus Torvalds 已提交
658 659 660
{
	struct hfsplus_inode_info *i = p;

C
Christoph Lameter 已提交
661
	inode_init_once(&i->vfs_inode);
L
Linus Torvalds 已提交
662 663 664 665 666 667 668
}

static int __init init_hfsplus_fs(void)
{
	int err;

	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
669
		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT,
670
		hfsplus_init_once);
L
Linus Torvalds 已提交
671 672
	if (!hfsplus_inode_cachep)
		return -ENOMEM;
673 674 675
	err = hfsplus_create_attr_tree_cache();
	if (err)
		goto destroy_inode_cache;
L
Linus Torvalds 已提交
676 677
	err = register_filesystem(&hfsplus_fs_type);
	if (err)
678 679 680 681 682 683 684 685 686
		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 已提交
687 688 689 690 691 692
	return err;
}

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
693 694 695 696 697 698

	/*
	 * Make sure all delayed rcu free inodes are flushed before we
	 * destroy cache.
	 */
	rcu_barrier();
699
	hfsplus_destroy_attr_tree_cache();
700
	kmem_cache_destroy(hfsplus_inode_cachep);
L
Linus Torvalds 已提交
701 702 703 704
}

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)