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>
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 70 71
	INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
	mutex_init(&HFSPLUS_I(inode)->extents_lock);
	HFSPLUS_I(inode)->flags = 0;
C
Christoph Hellwig 已提交
72
	HFSPLUS_I(inode)->extent_state = 0;
73 74
	HFSPLUS_I(inode)->rsrc_inode = NULL;
	atomic_set(&HFSPLUS_I(inode)->opencnt, 0);
L
Linus Torvalds 已提交
75

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

282 283
	cancel_delayed_work_sync(&sbi->sync_work);

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

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

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

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

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

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

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

467 468
	err = -EINVAL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static int __init init_hfsplus_fs(void)
{
	int err;

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

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

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

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)