super.c 16.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 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"

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

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

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

67 68 69
	INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
	mutex_init(&HFSPLUS_I(inode)->extents_lock);
	HFSPLUS_I(inode)->flags = 0;
C
Christoph Hellwig 已提交
70
	HFSPLUS_I(inode)->extent_state = 0;
71 72
	HFSPLUS_I(inode)->rsrc_inode = NULL;
	atomic_set(&HFSPLUS_I(inode)->opencnt, 0);
L
Linus Torvalds 已提交
73

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

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

92 93
	unlock_new_inode(inode);
	return inode;
L
Linus Torvalds 已提交
94 95
}

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

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

	if (fork->total_size != cpu_to_be64(inode->i_size)) {
126
		set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags);
127
		hfsplus_mark_mdb_dirty(inode->i_sb);
L
Linus Torvalds 已提交
128
	}
129
	hfsplus_inode_write_fork(inode, fork);
130 131 132 133 134 135 136 137
	if (tree) {
		int err = hfs_btree_write(tree);
		if (err) {
			printk(KERN_ERR "hfs: b-tree write err: %d, ino %lu\n",
					err, inode->i_ino);
			return err;
		}
	}
138 139 140 141 142 143
	return 0;
}

static int hfsplus_write_inode(struct inode *inode,
		struct writeback_control *wbc)
{
144 145
	int err;

146 147
	dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);

148 149 150
	err = hfsplus_ext_write_extent(inode);
	if (err)
		return err;
151 152 153 154 155 156

	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 已提交
157 158
}

159
static void hfsplus_evict_inode(struct inode *inode)
L
Linus Torvalds 已提交
160
{
161 162
	dprint(DBG_INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino);
	truncate_inode_pages(&inode->i_data, 0);
163
	clear_inode(inode);
L
Linus Torvalds 已提交
164
	if (HFSPLUS_IS_RSRC(inode)) {
165 166
		HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
		iput(HFSPLUS_I(inode)->rsrc_inode);
L
Linus Torvalds 已提交
167 168 169
	}
}

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

177 178
	if (!wait)
		return 0;
L
Linus Torvalds 已提交
179

180
	dprint(DBG_SUPER, "hfsplus_sync_fs\n");
181

182 183 184 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;
	error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
	if (!error)
		error = error2;

198 199
	mutex_lock(&sbi->vh_mutex);
	mutex_lock(&sbi->alloc_mutex);
200 201 202 203
	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 已提交
204

205
	if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
206 207
		memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
		write_backup = 1;
L
Linus Torvalds 已提交
208
	}
209

210
	error2 = hfsplus_submit_bio(sb,
211
				   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
212
				   sbi->s_vhdr_buf, NULL, WRITE_SYNC);
213 214
	if (!error)
		error = error2;
215 216 217
	if (!write_backup)
		goto out;

218
	error2 = hfsplus_submit_bio(sb,
219
				  sbi->part_start + sbi->sect_count - 2,
220
				  sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
221 222 223
	if (!error)
		error2 = error;
out:
224
	mutex_unlock(&sbi->alloc_mutex);
225
	mutex_unlock(&sbi->vh_mutex);
226 227 228 229

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

230
	return error;
C
Christoph Hellwig 已提交
231 232
}

233
static void delayed_sync_fs(struct work_struct *work)
C
Christoph Hellwig 已提交
234
{
235
	int err;
236 237 238 239 240 241 242 243
	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);

244 245 246
	err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1);
	if (err)
		printk(KERN_ERR "hfs: delayed sync fs err %d\n", err);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
}

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 已提交
264 265 266 267
}

static void hfsplus_put_super(struct super_block *sb)
{
268 269
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);

L
Linus Torvalds 已提交
270
	dprint(DBG_SUPER, "hfsplus_put_super\n");
271

272 273
	cancel_delayed_work_sync(&sbi->sync_work);

274 275
	if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) {
		struct hfsplus_vh *vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
276 277 278 279

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

		hfsplus_sync_fs(sb, 1);
L
Linus Torvalds 已提交
282 283
	}

284 285 286 287
	hfs_btree_close(sbi->cat_tree);
	hfs_btree_close(sbi->ext_tree);
	iput(sbi->alloc_file);
	iput(sbi->hidden_dir);
288 289
	kfree(sbi->s_vhdr_buf);
	kfree(sbi->s_backup_vhdr_buf);
290
	unload_nls(sbi->nls);
291 292
	kfree(sb->s_fs_info);
	sb->s_fs_info = NULL;
L
Linus Torvalds 已提交
293 294
}

295
static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
L
Linus Torvalds 已提交
296
{
297
	struct super_block *sb = dentry->d_sb;
298
	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
299
	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
300

L
Linus Torvalds 已提交
301 302
	buf->f_type = HFSPLUS_SUPER_MAGIC;
	buf->f_bsize = sb->s_blocksize;
303 304
	buf->f_blocks = sbi->total_blocks << sbi->fs_shift;
	buf->f_bfree = sbi->free_blocks << sbi->fs_shift;
L
Linus Torvalds 已提交
305 306
	buf->f_bavail = buf->f_bfree;
	buf->f_files = 0xFFFFFFFF;
307
	buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid;
308 309
	buf->f_fsid.val[0] = (u32)id;
	buf->f_fsid.val[1] = (u32)(id >> 32);
L
Linus Torvalds 已提交
310 311 312 313 314 315 316 317 318 319
	buf->f_namelen = HFSPLUS_MAX_STRLEN;

	return 0;
}

static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
{
	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
		return 0;
	if (!(*flags & MS_RDONLY)) {
320
		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
321
		int force = 0;
322

323
		if (!hfsplus_parse_options_remount(data, &force))
324
			return -EINVAL;
L
Linus Torvalds 已提交
325 326

		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
327 328 329 330
			printk(KERN_WARNING "hfs: filesystem was "
					"not cleanly unmounted, "
					"running fsck.hfsplus is recommended.  "
					"leaving read-only.\n");
L
Linus Torvalds 已提交
331 332
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
333
		} else if (force) {
334
			/* nothing */
335 336 337 338
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
			printk(KERN_WARNING "hfs: filesystem is marked locked, "
					"leaving read-only.\n");
L
Linus Torvalds 已提交
339 340
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
341 342 343 344 345
		} else if (vhdr->attributes &
				cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
			printk(KERN_WARNING "hfs: filesystem is "
					"marked journaled, "
					"leaving read-only.\n");
346 347
			sb->s_flags |= MS_RDONLY;
			*flags |= MS_RDONLY;
L
Linus Torvalds 已提交
348 349 350 351 352
		}
	}
	return 0;
}

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

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;
371
	struct inode *root, *inode;
L
Linus Torvalds 已提交
372 373
	struct qstr str;
	struct nls_table *nls = NULL;
374
	u64 last_fs_block, last_fs_page;
375
	int err;
L
Linus Torvalds 已提交
376

377
	err = -ENOMEM;
378
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
L
Linus Torvalds 已提交
379
	if (!sbi)
380
		goto out;
L
Linus Torvalds 已提交
381 382

	sb->s_fs_info = sbi;
C
Christoph Hellwig 已提交
383
	mutex_init(&sbi->alloc_mutex);
384
	mutex_init(&sbi->vh_mutex);
385 386
	spin_lock_init(&sbi->work_lock);
	INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
R
Roman Zippel 已提交
387
	hfsplus_fill_defaults(sbi);
388 389

	err = -EINVAL;
R
Roman Zippel 已提交
390
	if (!hfsplus_parse_options(data, sbi)) {
R
Roman Zippel 已提交
391
		printk(KERN_ERR "hfs: unable to parse mount options\n");
392
		goto out_unload_nls;
L
Linus Torvalds 已提交
393 394 395 396 397
	}

	/* temporarily use utf8 to correctly find the hidden dir below */
	nls = sbi->nls;
	sbi->nls = load_nls("utf8");
J
Joshua Kwan 已提交
398
	if (!sbi->nls) {
R
Roman Zippel 已提交
399
		printk(KERN_ERR "hfs: unable to load nls for utf8\n");
400
		goto out_unload_nls;
L
Linus Torvalds 已提交
401 402 403 404 405
	}

	/* Grab the volume header */
	if (hfsplus_read_wrapper(sb)) {
		if (!silent)
R
Roman Zippel 已提交
406
			printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
407
		goto out_unload_nls;
L
Linus Torvalds 已提交
408
	}
409
	vhdr = sbi->s_vhdr;
L
Linus Torvalds 已提交
410 411

	/* Copy parts of the volume header into the superblock */
D
David Elliott 已提交
412 413 414
	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
R
Roman Zippel 已提交
415
		printk(KERN_ERR "hfs: wrong filesystem version\n");
416
		goto out_free_vhdr;
L
Linus Torvalds 已提交
417
	}
418 419 420 421 422 423 424 425 426 427 428 429 430
	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 已提交
431

432 433 434 435 436 437 438
	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))) {
439 440 441 442
		printk(KERN_ERR "hfs: filesystem size too large.\n");
		goto out_free_vhdr;
	}

L
Linus Torvalds 已提交
443 444 445 446 447
	/* 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))) {
448 449 450 451
		printk(KERN_WARNING "hfs: Filesystem was "
				"not cleanly unmounted, "
				"running fsck.hfsplus is recommended.  "
				"mounting read-only.\n");
L
Linus Torvalds 已提交
452
		sb->s_flags |= MS_RDONLY;
453
	} else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
454
		/* nothing */
L
Linus Torvalds 已提交
455
	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
R
Roman Zippel 已提交
456
		printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n");
L
Linus Torvalds 已提交
457
		sb->s_flags |= MS_RDONLY;
458 459 460 461 462 463
	} else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) &&
			!(sb->s_flags & MS_RDONLY)) {
		printk(KERN_WARNING "hfs: 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) {
R
Roman Zippel 已提交
472
		printk(KERN_ERR "hfs: 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) {
R
Roman Zippel 已提交
477
		printk(KERN_ERR "hfs: failed to load catalog file\n");
478
		goto out_close_ext_tree;
L
Linus Torvalds 已提交
479 480
	}

481 482
	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
	if (IS_ERR(inode)) {
R
Roman Zippel 已提交
483
		printk(KERN_ERR "hfs: failed to load allocation file\n");
484
		err = PTR_ERR(inode);
485
		goto out_close_cat_tree;
L
Linus Torvalds 已提交
486
	}
487
	sbi->alloc_file = inode;
L
Linus Torvalds 已提交
488 489

	/* Load the root directory */
490 491 492 493
	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
	if (IS_ERR(root)) {
		printk(KERN_ERR "hfs: failed to load root directory\n");
		err = PTR_ERR(root);
494
		goto out_put_alloc_file;
L
Linus Torvalds 已提交
495 496
	}

A
Al Viro 已提交
497 498 499 500 501 502 503
	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 已提交
504 505
	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
	str.name = HFSP_HIDDENDIR_NAME;
506 507 508
	err = hfs_find_init(sbi->cat_tree, &fd);
	if (err)
		goto out_put_root;
L
Linus Torvalds 已提交
509 510 511 512
	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))
513
			goto out_put_root;
514 515 516
		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
517
			goto out_put_root;
518
		}
519
		sbi->hidden_dir = inode;
L
Linus Torvalds 已提交
520 521 522
	} else
		hfs_find_exit(&fd);

523 524 525 526 527 528 529 530 531 532 533
	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 已提交
534

535 536 537
		if (!sbi->hidden_dir) {
			mutex_lock(&sbi->vh_mutex);
			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
538 539 540 541 542 543 544
			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);
545
			mutex_unlock(&sbi->vh_mutex);
546 547
			if (err)
				goto out_put_hidden_dir;
548 549 550 551

			hfsplus_mark_inode_dirty(sbi->hidden_dir,
						 HFSPLUS_I_CAT_DIRTY);
		}
L
Linus Torvalds 已提交
552
	}
553

L
Linus Torvalds 已提交
554 555 556 557
	unload_nls(sbi->nls);
	sbi->nls = nls;
	return 0;

558 559 560
out_put_hidden_dir:
	iput(sbi->hidden_dir);
out_put_root:
A
Al Viro 已提交
561 562
	dput(sb->s_root);
	sb->s_root = NULL;
563 564 565 566 567 568 569
out_put_alloc_file:
	iput(sbi->alloc_file);
out_close_cat_tree:
	hfs_btree_close(sbi->cat_tree);
out_close_ext_tree:
	hfs_btree_close(sbi->ext_tree);
out_free_vhdr:
570 571
	kfree(sbi->s_vhdr_buf);
	kfree(sbi->s_backup_vhdr_buf);
572 573
out_unload_nls:
	unload_nls(sbi->nls);
574
	unload_nls(nls);
575 576
	kfree(sbi);
out:
L
Linus Torvalds 已提交
577 578 579 580 581 582 583
	return err;
}

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

584
static struct kmem_cache *hfsplus_inode_cachep;
L
Linus Torvalds 已提交
585 586 587 588 589

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

590
	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
L
Linus Torvalds 已提交
591 592 593
	return i ? &i->vfs_inode : NULL;
}

N
Nick Piggin 已提交
594
static void hfsplus_i_callback(struct rcu_head *head)
L
Linus Torvalds 已提交
595
{
N
Nick Piggin 已提交
596 597
	struct inode *inode = container_of(head, struct inode, i_rcu);

598
	kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
L
Linus Torvalds 已提交
599 600
}

N
Nick Piggin 已提交
601 602 603 604 605
static void hfsplus_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, hfsplus_i_callback);
}

L
Linus Torvalds 已提交
606 607
#define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)

A
Al Viro 已提交
608 609
static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
			  int flags, const char *dev_name, void *data)
L
Linus Torvalds 已提交
610
{
A
Al Viro 已提交
611
	return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
L
Linus Torvalds 已提交
612 613 614 615 616
}

static struct file_system_type hfsplus_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "hfsplus",
A
Al Viro 已提交
617
	.mount		= hfsplus_mount,
L
Linus Torvalds 已提交
618 619 620 621
	.kill_sb	= kill_block_super,
	.fs_flags	= FS_REQUIRES_DEV,
};

622
static void hfsplus_init_once(void *p)
L
Linus Torvalds 已提交
623 624 625
{
	struct hfsplus_inode_info *i = p;

C
Christoph Lameter 已提交
626
	inode_init_once(&i->vfs_inode);
L
Linus Torvalds 已提交
627 628 629 630 631 632 633 634
}

static int __init init_hfsplus_fs(void)
{
	int err;

	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
635
		hfsplus_init_once);
L
Linus Torvalds 已提交
636 637 638 639 640 641 642 643 644 645 646
	if (!hfsplus_inode_cachep)
		return -ENOMEM;
	err = register_filesystem(&hfsplus_fs_type);
	if (err)
		kmem_cache_destroy(hfsplus_inode_cachep);
	return err;
}

static void __exit exit_hfsplus_fs(void)
{
	unregister_filesystem(&hfsplus_fs_type);
647 648 649 650 651 652

	/*
	 * Make sure all delayed rcu free inodes are flushed before we
	 * destroy cache.
	 */
	rcu_barrier();
653
	kmem_cache_destroy(hfsplus_inode_cachep);
L
Linus Torvalds 已提交
654 655 656 657
}

module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)