super.c 17.9 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;
S
Sougata Santra 已提交
518 519 520
	err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
	if (unlikely(err < 0))
		goto out_put_root;
L
Linus Torvalds 已提交
521 522 523
	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
		hfs_find_exit(&fd);
		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
524
			goto out_put_root;
525 526 527
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
528
			goto out_put_root;
529
		}
530
		sbi->hidden_dir = inode;
L
Linus Torvalds 已提交
531 532 533
	} else
		hfs_find_exit(&fd);

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

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
636 637
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

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

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

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

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

static int __init init_hfsplus_fs(void)
{
	int err;

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

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
689 690 691 692 693 694

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

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)