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,
223
				   sbi->s_vhdr_buf, NULL, WRITE_SYNC);
224 225
	if (!error)
		error = error2;
226 227 228
	if (!write_backup)
		goto out;

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

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

241
	return error;
C
Christoph Hellwig 已提交
242 243
}

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

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

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 已提交
275 276 277 278
}

static void hfsplus_put_super(struct super_block *sb)
{
279 280
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);

281
	hfs_dbg(SUPER, "hfsplus_put_super\n");
282

283 284
	cancel_delayed_work_sync(&sbi->sync_work);

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

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

		hfsplus_sync_fs(sb, 1);
L
Linus Torvalds 已提交
293 294
	}

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

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

L
Linus Torvalds 已提交
313 314
	buf->f_type = HFSPLUS_SUPER_MAGIC;
	buf->f_bsize = sb->s_blocksize;
315 316
	buf->f_blocks = sbi->total_blocks << sbi->fs_shift;
	buf->f_bfree = sbi->free_blocks << sbi->fs_shift;
L
Linus Torvalds 已提交
317 318
	buf->f_bavail = buf->f_bfree;
	buf->f_files = 0xFFFFFFFF;
319
	buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid;
320 321
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
L
Linus Torvalds 已提交
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)
{
329
	sync_filesystem(sb);
L
Linus Torvalds 已提交
330 331 332
	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
		return 0;
	if (!(*flags & MS_RDONLY)) {
333
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
334
		int force = 0;
335

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

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

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

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

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

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

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

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

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

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

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

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

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

468 469
	err = -EINVAL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
638 639
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

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

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

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

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

static int __init init_hfsplus_fs(void)
{
	int err;

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

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
691 692 693 694 695 696

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

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)