xref: /linux/fs/hfsplus/super.c (revision 092e0e7e520a1fca03e13c9f2d157432a8657ff2)
1 /*
2  *  linux/fs/hfsplus/super.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer (flar@allandria.com)
6  * (C) 2003 Ardis Technologies <roman@ardistech.com>
7  *
8  */
9 
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/pagemap.h>
13 #include <linux/fs.h>
14 #include <linux/slab.h>
15 #include <linux/vfs.h>
16 #include <linux/nls.h>
17 
18 static struct inode *hfsplus_alloc_inode(struct super_block *sb);
19 static void hfsplus_destroy_inode(struct inode *inode);
20 
21 #include "hfsplus_fs.h"
22 
23 static int hfsplus_system_read_inode(struct inode *inode)
24 {
25 	struct hfsplus_vh *vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr;
26 
27 	switch (inode->i_ino) {
28 	case HFSPLUS_EXT_CNID:
29 		hfsplus_inode_read_fork(inode, &vhdr->ext_file);
30 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
31 		break;
32 	case HFSPLUS_CAT_CNID:
33 		hfsplus_inode_read_fork(inode, &vhdr->cat_file);
34 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
35 		break;
36 	case HFSPLUS_ALLOC_CNID:
37 		hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
38 		inode->i_mapping->a_ops = &hfsplus_aops;
39 		break;
40 	case HFSPLUS_START_CNID:
41 		hfsplus_inode_read_fork(inode, &vhdr->start_file);
42 		break;
43 	case HFSPLUS_ATTR_CNID:
44 		hfsplus_inode_read_fork(inode, &vhdr->attr_file);
45 		inode->i_mapping->a_ops = &hfsplus_btree_aops;
46 		break;
47 	default:
48 		return -EIO;
49 	}
50 
51 	return 0;
52 }
53 
54 struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
55 {
56 	struct hfs_find_data fd;
57 	struct inode *inode;
58 	int err;
59 
60 	inode = iget_locked(sb, ino);
61 	if (!inode)
62 		return ERR_PTR(-ENOMEM);
63 	if (!(inode->i_state & I_NEW))
64 		return inode;
65 
66 	INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
67 	mutex_init(&HFSPLUS_I(inode)->extents_lock);
68 	HFSPLUS_I(inode)->flags = 0;
69 	HFSPLUS_I(inode)->rsrc_inode = NULL;
70 	atomic_set(&HFSPLUS_I(inode)->opencnt, 0);
71 
72 	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
73 	    inode->i_ino == HFSPLUS_ROOT_CNID) {
74 		hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
75 		err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
76 		if (!err)
77 			err = hfsplus_cat_read_inode(inode, &fd);
78 		hfs_find_exit(&fd);
79 	} else {
80 		err = hfsplus_system_read_inode(inode);
81 	}
82 
83 	if (err) {
84 		iget_failed(inode);
85 		return ERR_PTR(err);
86 	}
87 
88 	unlock_new_inode(inode);
89 	return inode;
90 }
91 
92 static int hfsplus_system_write_inode(struct inode *inode)
93 {
94 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
95 	struct hfsplus_vh *vhdr = sbi->s_vhdr;
96 	struct hfsplus_fork_raw *fork;
97 	struct hfs_btree *tree = NULL;
98 
99 	switch (inode->i_ino) {
100 	case HFSPLUS_EXT_CNID:
101 		fork = &vhdr->ext_file;
102 		tree = sbi->ext_tree;
103 		break;
104 	case HFSPLUS_CAT_CNID:
105 		fork = &vhdr->cat_file;
106 		tree = sbi->cat_tree;
107 		break;
108 	case HFSPLUS_ALLOC_CNID:
109 		fork = &vhdr->alloc_file;
110 		break;
111 	case HFSPLUS_START_CNID:
112 		fork = &vhdr->start_file;
113 		break;
114 	case HFSPLUS_ATTR_CNID:
115 		fork = &vhdr->attr_file;
116 		tree = sbi->attr_tree;
117 	default:
118 		return -EIO;
119 	}
120 
121 	if (fork->total_size != cpu_to_be64(inode->i_size)) {
122 		set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags);
123 		inode->i_sb->s_dirt = 1;
124 	}
125 	hfsplus_inode_write_fork(inode, fork);
126 	if (tree)
127 		hfs_btree_write(tree);
128 	return 0;
129 }
130 
131 static int hfsplus_write_inode(struct inode *inode,
132 		struct writeback_control *wbc)
133 {
134 	dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
135 
136 	hfsplus_ext_write_extent(inode);
137 
138 	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
139 	    inode->i_ino == HFSPLUS_ROOT_CNID)
140 		return hfsplus_cat_write_inode(inode);
141 	else
142 		return hfsplus_system_write_inode(inode);
143 }
144 
145 static void hfsplus_evict_inode(struct inode *inode)
146 {
147 	dprint(DBG_INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino);
148 	truncate_inode_pages(&inode->i_data, 0);
149 	end_writeback(inode);
150 	if (HFSPLUS_IS_RSRC(inode)) {
151 		HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
152 		iput(HFSPLUS_I(inode)->rsrc_inode);
153 	}
154 }
155 
156 int hfsplus_sync_fs(struct super_block *sb, int wait)
157 {
158 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
159 	struct hfsplus_vh *vhdr = sbi->s_vhdr;
160 
161 	dprint(DBG_SUPER, "hfsplus_write_super\n");
162 
163 	mutex_lock(&sbi->vh_mutex);
164 	mutex_lock(&sbi->alloc_mutex);
165 	sb->s_dirt = 0;
166 
167 	vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
168 	vhdr->next_cnid = cpu_to_be32(sbi->next_cnid);
169 	vhdr->folder_count = cpu_to_be32(sbi->folder_count);
170 	vhdr->file_count = cpu_to_be32(sbi->file_count);
171 
172 	mark_buffer_dirty(sbi->s_vhbh);
173 	if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
174 		if (sbi->sect_count) {
175 			struct buffer_head *bh;
176 			u32 block, offset;
177 
178 			block = sbi->blockoffset;
179 			block += (sbi->sect_count - 2) >> (sb->s_blocksize_bits - 9);
180 			offset = ((sbi->sect_count - 2) << 9) & (sb->s_blocksize - 1);
181 			printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n",
182 					  sbi->blockoffset, sbi->sect_count,
183 					  block, offset);
184 			bh = sb_bread(sb, block);
185 			if (bh) {
186 				vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
187 				if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
188 					memcpy(vhdr, sbi->s_vhdr, sizeof(*vhdr));
189 					mark_buffer_dirty(bh);
190 					brelse(bh);
191 				} else
192 					printk(KERN_WARNING "hfs: backup not found!\n");
193 			}
194 		}
195 	}
196 	mutex_unlock(&sbi->alloc_mutex);
197 	mutex_unlock(&sbi->vh_mutex);
198 	return 0;
199 }
200 
201 static void hfsplus_write_super(struct super_block *sb)
202 {
203 	if (!(sb->s_flags & MS_RDONLY))
204 		hfsplus_sync_fs(sb, 1);
205 	else
206 		sb->s_dirt = 0;
207 }
208 
209 static void hfsplus_put_super(struct super_block *sb)
210 {
211 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
212 
213 	dprint(DBG_SUPER, "hfsplus_put_super\n");
214 
215 	if (!sb->s_fs_info)
216 		return;
217 
218 	if (sb->s_dirt)
219 		hfsplus_write_super(sb);
220 	if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) {
221 		struct hfsplus_vh *vhdr = sbi->s_vhdr;
222 
223 		vhdr->modify_date = hfsp_now2mt();
224 		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
225 		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
226 		mark_buffer_dirty(sbi->s_vhbh);
227 		sync_dirty_buffer(sbi->s_vhbh);
228 	}
229 
230 	hfs_btree_close(sbi->cat_tree);
231 	hfs_btree_close(sbi->ext_tree);
232 	iput(sbi->alloc_file);
233 	iput(sbi->hidden_dir);
234 	brelse(sbi->s_vhbh);
235 	unload_nls(sbi->nls);
236 	kfree(sb->s_fs_info);
237 	sb->s_fs_info = NULL;
238 }
239 
240 static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
241 {
242 	struct super_block *sb = dentry->d_sb;
243 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
244 	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
245 
246 	buf->f_type = HFSPLUS_SUPER_MAGIC;
247 	buf->f_bsize = sb->s_blocksize;
248 	buf->f_blocks = sbi->total_blocks << sbi->fs_shift;
249 	buf->f_bfree = sbi->free_blocks << sbi->fs_shift;
250 	buf->f_bavail = buf->f_bfree;
251 	buf->f_files = 0xFFFFFFFF;
252 	buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid;
253 	buf->f_fsid.val[0] = (u32)id;
254 	buf->f_fsid.val[1] = (u32)(id >> 32);
255 	buf->f_namelen = HFSPLUS_MAX_STRLEN;
256 
257 	return 0;
258 }
259 
260 static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
261 {
262 	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
263 		return 0;
264 	if (!(*flags & MS_RDONLY)) {
265 		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
266 		struct hfsplus_sb_info sbi;
267 
268 		memset(&sbi, 0, sizeof(struct hfsplus_sb_info));
269 		sbi.nls = HFSPLUS_SB(sb)->nls;
270 		if (!hfsplus_parse_options(data, &sbi))
271 			return -EINVAL;
272 
273 		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
274 			printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, "
275 			       "running fsck.hfsplus is recommended.  leaving read-only.\n");
276 			sb->s_flags |= MS_RDONLY;
277 			*flags |= MS_RDONLY;
278 		} else if (test_bit(HFSPLUS_SB_FORCE, &sbi.flags)) {
279 			/* nothing */
280 		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
281 			printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n");
282 			sb->s_flags |= MS_RDONLY;
283 			*flags |= MS_RDONLY;
284 		} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
285 			printk(KERN_WARNING "hfs: filesystem is marked journaled, leaving read-only.\n");
286 			sb->s_flags |= MS_RDONLY;
287 			*flags |= MS_RDONLY;
288 		}
289 	}
290 	return 0;
291 }
292 
293 static const struct super_operations hfsplus_sops = {
294 	.alloc_inode	= hfsplus_alloc_inode,
295 	.destroy_inode	= hfsplus_destroy_inode,
296 	.write_inode	= hfsplus_write_inode,
297 	.evict_inode	= hfsplus_evict_inode,
298 	.put_super	= hfsplus_put_super,
299 	.write_super	= hfsplus_write_super,
300 	.sync_fs	= hfsplus_sync_fs,
301 	.statfs		= hfsplus_statfs,
302 	.remount_fs	= hfsplus_remount,
303 	.show_options	= hfsplus_show_options,
304 };
305 
306 static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
307 {
308 	struct hfsplus_vh *vhdr;
309 	struct hfsplus_sb_info *sbi;
310 	hfsplus_cat_entry entry;
311 	struct hfs_find_data fd;
312 	struct inode *root, *inode;
313 	struct qstr str;
314 	struct nls_table *nls = NULL;
315 	int err = -EINVAL;
316 
317 	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
318 	if (!sbi)
319 		return -ENOMEM;
320 
321 	sb->s_fs_info = sbi;
322 	mutex_init(&sbi->alloc_mutex);
323 	mutex_init(&sbi->vh_mutex);
324 	hfsplus_fill_defaults(sbi);
325 	if (!hfsplus_parse_options(data, sbi)) {
326 		printk(KERN_ERR "hfs: unable to parse mount options\n");
327 		err = -EINVAL;
328 		goto cleanup;
329 	}
330 
331 	/* temporarily use utf8 to correctly find the hidden dir below */
332 	nls = sbi->nls;
333 	sbi->nls = load_nls("utf8");
334 	if (!sbi->nls) {
335 		printk(KERN_ERR "hfs: unable to load nls for utf8\n");
336 		err = -EINVAL;
337 		goto cleanup;
338 	}
339 
340 	/* Grab the volume header */
341 	if (hfsplus_read_wrapper(sb)) {
342 		if (!silent)
343 			printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
344 		err = -EINVAL;
345 		goto cleanup;
346 	}
347 	vhdr = sbi->s_vhdr;
348 
349 	/* Copy parts of the volume header into the superblock */
350 	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
351 	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
352 	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
353 		printk(KERN_ERR "hfs: wrong filesystem version\n");
354 		goto cleanup;
355 	}
356 	sbi->total_blocks = be32_to_cpu(vhdr->total_blocks);
357 	sbi->free_blocks = be32_to_cpu(vhdr->free_blocks);
358 	sbi->next_cnid = be32_to_cpu(vhdr->next_cnid);
359 	sbi->file_count = be32_to_cpu(vhdr->file_count);
360 	sbi->folder_count = be32_to_cpu(vhdr->folder_count);
361 	sbi->data_clump_blocks =
362 		be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift;
363 	if (!sbi->data_clump_blocks)
364 		sbi->data_clump_blocks = 1;
365 	sbi->rsrc_clump_blocks =
366 		be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift;
367 	if (!sbi->rsrc_clump_blocks)
368 		sbi->rsrc_clump_blocks = 1;
369 
370 	/* Set up operations so we can load metadata */
371 	sb->s_op = &hfsplus_sops;
372 	sb->s_maxbytes = MAX_LFS_FILESIZE;
373 
374 	if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
375 		printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, "
376 		       "running fsck.hfsplus is recommended.  mounting read-only.\n");
377 		sb->s_flags |= MS_RDONLY;
378 	} else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
379 		/* nothing */
380 	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
381 		printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n");
382 		sb->s_flags |= MS_RDONLY;
383 	} else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && !(sb->s_flags & MS_RDONLY)) {
384 		printk(KERN_WARNING "hfs: write access to a journaled filesystem is not supported, "
385 		       "use the force option at your own risk, mounting read-only.\n");
386 		sb->s_flags |= MS_RDONLY;
387 	}
388 
389 	/* Load metadata objects (B*Trees) */
390 	sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
391 	if (!sbi->ext_tree) {
392 		printk(KERN_ERR "hfs: failed to load extents file\n");
393 		goto cleanup;
394 	}
395 	sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
396 	if (!sbi->cat_tree) {
397 		printk(KERN_ERR "hfs: failed to load catalog file\n");
398 		goto cleanup;
399 	}
400 
401 	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
402 	if (IS_ERR(inode)) {
403 		printk(KERN_ERR "hfs: failed to load allocation file\n");
404 		err = PTR_ERR(inode);
405 		goto cleanup;
406 	}
407 	sbi->alloc_file = inode;
408 
409 	/* Load the root directory */
410 	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
411 	if (IS_ERR(root)) {
412 		printk(KERN_ERR "hfs: failed to load root directory\n");
413 		err = PTR_ERR(root);
414 		goto cleanup;
415 	}
416 	sb->s_root = d_alloc_root(root);
417 	if (!sb->s_root) {
418 		iput(root);
419 		err = -ENOMEM;
420 		goto cleanup;
421 	}
422 	sb->s_root->d_op = &hfsplus_dentry_operations;
423 
424 	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
425 	str.name = HFSP_HIDDENDIR_NAME;
426 	hfs_find_init(sbi->cat_tree, &fd);
427 	hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
428 	if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
429 		hfs_find_exit(&fd);
430 		if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
431 			goto cleanup;
432 		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
433 		if (IS_ERR(inode)) {
434 			err = PTR_ERR(inode);
435 			goto cleanup;
436 		}
437 		sbi->hidden_dir = inode;
438 	} else
439 		hfs_find_exit(&fd);
440 
441 	if (sb->s_flags & MS_RDONLY)
442 		goto out;
443 
444 	/* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
445 	 * all three are registered with Apple for our use
446 	 */
447 	vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
448 	vhdr->modify_date = hfsp_now2mt();
449 	be32_add_cpu(&vhdr->write_count, 1);
450 	vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
451 	vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
452 	mark_buffer_dirty(sbi->s_vhbh);
453 	sync_dirty_buffer(sbi->s_vhbh);
454 
455 	if (!sbi->hidden_dir) {
456 		printk(KERN_DEBUG "hfs: create hidden dir...\n");
457 
458 		mutex_lock(&sbi->vh_mutex);
459 		sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
460 		hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode,
461 				   &str, sbi->hidden_dir);
462 		mutex_unlock(&sbi->vh_mutex);
463 
464 		mark_inode_dirty(sbi->hidden_dir);
465 	}
466 out:
467 	unload_nls(sbi->nls);
468 	sbi->nls = nls;
469 	return 0;
470 
471 cleanup:
472 	hfsplus_put_super(sb);
473 	unload_nls(nls);
474 	return err;
475 }
476 
477 MODULE_AUTHOR("Brad Boyer");
478 MODULE_DESCRIPTION("Extended Macintosh Filesystem");
479 MODULE_LICENSE("GPL");
480 
481 static struct kmem_cache *hfsplus_inode_cachep;
482 
483 static struct inode *hfsplus_alloc_inode(struct super_block *sb)
484 {
485 	struct hfsplus_inode_info *i;
486 
487 	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
488 	return i ? &i->vfs_inode : NULL;
489 }
490 
491 static void hfsplus_destroy_inode(struct inode *inode)
492 {
493 	kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
494 }
495 
496 #define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)
497 
498 static int hfsplus_get_sb(struct file_system_type *fs_type,
499 			  int flags, const char *dev_name, void *data,
500 			  struct vfsmount *mnt)
501 {
502 	return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super,
503 			   mnt);
504 }
505 
506 static struct file_system_type hfsplus_fs_type = {
507 	.owner		= THIS_MODULE,
508 	.name		= "hfsplus",
509 	.get_sb		= hfsplus_get_sb,
510 	.kill_sb	= kill_block_super,
511 	.fs_flags	= FS_REQUIRES_DEV,
512 };
513 
514 static void hfsplus_init_once(void *p)
515 {
516 	struct hfsplus_inode_info *i = p;
517 
518 	inode_init_once(&i->vfs_inode);
519 }
520 
521 static int __init init_hfsplus_fs(void)
522 {
523 	int err;
524 
525 	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
526 		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
527 		hfsplus_init_once);
528 	if (!hfsplus_inode_cachep)
529 		return -ENOMEM;
530 	err = register_filesystem(&hfsplus_fs_type);
531 	if (err)
532 		kmem_cache_destroy(hfsplus_inode_cachep);
533 	return err;
534 }
535 
536 static void __exit exit_hfsplus_fs(void)
537 {
538 	unregister_filesystem(&hfsplus_fs_type);
539 	kmem_cache_destroy(hfsplus_inode_cachep);
540 }
541 
542 module_init(init_hfsplus_fs)
543 module_exit(exit_hfsplus_fs)
544