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
	if (tree) {
		int err = hfs_btree_write(tree);
134

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

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

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

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

	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 已提交
160 161
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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 已提交
273 274 275 276
}

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

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

281 282
	cancel_delayed_work_sync(&sbi->sync_work);

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

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

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

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

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

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

	return 0;
}

static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
{
327
	sync_filesystem(sb);
L
Linus Torvalds 已提交
328 329 330
	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
		return 0;
	if (!(*flags & MS_RDONLY)) {
331
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
332
		int force = 0;
333

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

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

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

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

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

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

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

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

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

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

437 438 439 440 441 442 443
	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))) {
444
		pr_err("filesystem size too large\n");
445 446 447
		goto out_free_vhdr;
	}

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

466 467
	err = -EINVAL;

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

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

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

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

532 533 534 535 536 537 538 539 540 541 542
	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 已提交
543

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
634 635
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

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

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

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

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

static int __init init_hfsplus_fs(void)
{
	int err;

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

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
687 688 689 690 691 692

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

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)