Blame view

fs/hfsplus/super.c 16 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /*
   *  linux/fs/hfsplus/super.c
   *
   * Copyright (C) 2001
   * Brad Boyer (flar@allandria.com)
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/pagemap.h>
34a2d313c   Christoph Hellwig   hfsplus: flush di...
12
  #include <linux/blkdev.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
  #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"
fc4fff821   Christoph Hellwig   hfsplus: clean up...
22
23
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
  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;
  }
635253915   David Howells   iget: stop HFSPLU...
52
  struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
  {
  	struct hfs_find_data fd;
635253915   David Howells   iget: stop HFSPLU...
55
  	struct inode *inode;
fc4fff821   Christoph Hellwig   hfsplus: clean up...
56
  	int err;
635253915   David Howells   iget: stop HFSPLU...
57
58
59
60
61
62
  
  	inode = iget_locked(sb, ino);
  	if (!inode)
  		return ERR_PTR(-ENOMEM);
  	if (!(inode->i_state & I_NEW))
  		return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
64
65
66
  	INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
  	mutex_init(&HFSPLUS_I(inode)->extents_lock);
  	HFSPLUS_I(inode)->flags = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
67
  	HFSPLUS_I(inode)->extent_state = 0;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
68
69
  	HFSPLUS_I(inode)->rsrc_inode = NULL;
  	atomic_set(&HFSPLUS_I(inode)->opencnt, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

fc4fff821   Christoph Hellwig   hfsplus: clean up...
71
72
  	if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID ||
  	    inode->i_ino == HFSPLUS_ROOT_CNID) {
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
73
74
75
76
77
78
79
  		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);
  		}
fc4fff821   Christoph Hellwig   hfsplus: clean up...
80
81
  	} else {
  		err = hfsplus_system_read_inode(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  	}
fc4fff821   Christoph Hellwig   hfsplus: clean up...
83
84
85
86
  
  	if (err) {
  		iget_failed(inode);
  		return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  	}
635253915   David Howells   iget: stop HFSPLU...
88
89
  	unlock_new_inode(inode);
  	return inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  }
b5080f77e   Christoph Hellwig   hfsplus: clean up...
91
  static int hfsplus_system_write_inode(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
93
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
b5080f77e   Christoph Hellwig   hfsplus: clean up...
94
95
96
  	struct hfsplus_vh *vhdr = sbi->s_vhdr;
  	struct hfsplus_fork_raw *fork;
  	struct hfs_btree *tree = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	switch (inode->i_ino) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  	case HFSPLUS_EXT_CNID:
b5080f77e   Christoph Hellwig   hfsplus: clean up...
100
101
  		fork = &vhdr->ext_file;
  		tree = sbi->ext_tree;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
  		break;
  	case HFSPLUS_CAT_CNID:
b5080f77e   Christoph Hellwig   hfsplus: clean up...
104
105
  		fork = &vhdr->cat_file;
  		tree = sbi->cat_tree;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
  		break;
  	case HFSPLUS_ALLOC_CNID:
b5080f77e   Christoph Hellwig   hfsplus: clean up...
108
  		fork = &vhdr->alloc_file;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
  		break;
  	case HFSPLUS_START_CNID:
b5080f77e   Christoph Hellwig   hfsplus: clean up...
111
  		fork = &vhdr->start_file;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
  		break;
  	case HFSPLUS_ATTR_CNID:
b5080f77e   Christoph Hellwig   hfsplus: clean up...
114
115
116
117
118
119
120
  		fork = &vhdr->attr_file;
  		tree = sbi->attr_tree;
  	default:
  		return -EIO;
  	}
  
  	if (fork->total_size != cpu_to_be64(inode->i_size)) {
84adede31   Christoph Hellwig   hfsplus: use atom...
121
  		set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags);
b5080f77e   Christoph Hellwig   hfsplus: clean up...
122
  		inode->i_sb->s_dirt = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	}
b5080f77e   Christoph Hellwig   hfsplus: clean up...
124
125
126
127
128
129
130
131
132
  	hfsplus_inode_write_fork(inode, fork);
  	if (tree)
  		hfs_btree_write(tree);
  	return 0;
  }
  
  static int hfsplus_write_inode(struct inode *inode,
  		struct writeback_control *wbc)
  {
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
133
  	int err;
b5080f77e   Christoph Hellwig   hfsplus: clean up...
134
135
  	dprint(DBG_INODE, "hfsplus_write_inode: %lu
  ", inode->i_ino);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
136
137
138
  	err = hfsplus_ext_write_extent(inode);
  	if (err)
  		return err;
b5080f77e   Christoph Hellwig   hfsplus: clean up...
139
140
141
142
143
144
  
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  }
b57922d97   Al Viro   convert remaining...
146
  static void hfsplus_evict_inode(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  {
b57922d97   Al Viro   convert remaining...
148
149
150
151
  	dprint(DBG_INODE, "hfsplus_evict_inode: %lu
  ", inode->i_ino);
  	truncate_inode_pages(&inode->i_data, 0);
  	end_writeback(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  	if (HFSPLUS_IS_RSRC(inode)) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
153
154
  		HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
  		iput(HFSPLUS_I(inode)->rsrc_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  }
b5fc510c4   Al Viro   get rid of file_f...
157
  int hfsplus_sync_fs(struct super_block *sb, int wait)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
159
160
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
  	struct hfsplus_vh *vhdr = sbi->s_vhdr;
52399b171   Christoph Hellwig   hfsplus: use raw ...
161
162
  	int write_backup = 0;
  	int error, error2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

f02e26f8d   Christoph Hellwig   hfsplus: avoid us...
164
165
  	if (!wait)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
  
  	dprint(DBG_SUPER, "hfsplus_write_super
  ");
ebc1ac164   Christoph Hellwig   ->write_super loc...
169

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  	sb->s_dirt = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

7dc4f0011   Christoph Hellwig   hfsplus: make sur...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  	/*
  	 * 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;
52399b171   Christoph Hellwig   hfsplus: use raw ...
187
188
  	mutex_lock(&sbi->vh_mutex);
  	mutex_lock(&sbi->alloc_mutex);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
189
  	vhdr->free_blocks = cpu_to_be32(sbi->free_blocks);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
190
191
192
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193

84adede31   Christoph Hellwig   hfsplus: use atom...
194
  	if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) {
52399b171   Christoph Hellwig   hfsplus: use raw ...
195
196
  		memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr));
  		write_backup = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
52399b171   Christoph Hellwig   hfsplus: use raw ...
198

6596528e3   Seth Forshee   hfsplus: ensure b...
199
  	error2 = hfsplus_submit_bio(sb,
52399b171   Christoph Hellwig   hfsplus: use raw ...
200
  				   sbi->part_start + HFSPLUS_VOLHEAD_SECTOR,
6596528e3   Seth Forshee   hfsplus: ensure b...
201
  				   sbi->s_vhdr_buf, NULL, WRITE_SYNC);
7dc4f0011   Christoph Hellwig   hfsplus: make sur...
202
203
  	if (!error)
  		error = error2;
52399b171   Christoph Hellwig   hfsplus: use raw ...
204
205
  	if (!write_backup)
  		goto out;
6596528e3   Seth Forshee   hfsplus: ensure b...
206
  	error2 = hfsplus_submit_bio(sb,
52399b171   Christoph Hellwig   hfsplus: use raw ...
207
  				  sbi->part_start + sbi->sect_count - 2,
6596528e3   Seth Forshee   hfsplus: ensure b...
208
  				  sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC);
52399b171   Christoph Hellwig   hfsplus: use raw ...
209
210
211
  	if (!error)
  		error2 = error;
  out:
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
212
  	mutex_unlock(&sbi->alloc_mutex);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
213
  	mutex_unlock(&sbi->vh_mutex);
34a2d313c   Christoph Hellwig   hfsplus: flush di...
214
215
216
  
  	if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))
  		blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL);
52399b171   Christoph Hellwig   hfsplus: use raw ...
217
  	return error;
7fbc6df0e   Christoph Hellwig   hfsplus: add ->sy...
218
219
220
221
222
223
224
225
  }
  
  static void hfsplus_write_super(struct super_block *sb)
  {
  	if (!(sb->s_flags & MS_RDONLY))
  		hfsplus_sync_fs(sb, 1);
  	else
  		sb->s_dirt = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
  }
  
  static void hfsplus_put_super(struct super_block *sb)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
230
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
  	dprint(DBG_SUPER, "hfsplus_put_super
  ");
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
233

945b09201   Colin Leroy   [PATCH] hfs, hfsp...
234
235
  	if (!sb->s_fs_info)
  		return;
6cfd01484   Christoph Hellwig   push BKL down int...
236

dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
237
238
  	if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) {
  		struct hfsplus_vh *vhdr = sbi->s_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
  
  		vhdr->modify_date = hfsp_now2mt();
  		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
  		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
3b5ce8ae3   Christoph Hellwig   hfsplus: always u...
243
244
  
  		hfsplus_sync_fs(sb, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
246
247
248
249
  	hfs_btree_close(sbi->cat_tree);
  	hfs_btree_close(sbi->ext_tree);
  	iput(sbi->alloc_file);
  	iput(sbi->hidden_dir);
6596528e3   Seth Forshee   hfsplus: ensure b...
250
251
  	kfree(sbi->s_vhdr_buf);
  	kfree(sbi->s_backup_vhdr_buf);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
252
  	unload_nls(sbi->nls);
945b09201   Colin Leroy   [PATCH] hfs, hfsp...
253
254
  	kfree(sb->s_fs_info);
  	sb->s_fs_info = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  }
726c33422   David Howells   [PATCH] VFS: Perm...
256
  static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
  {
726c33422   David Howells   [PATCH] VFS: Perm...
258
  	struct super_block *sb = dentry->d_sb;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
259
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
25564dd86   Coly Li   fs/hfsplus: retur...
260
  	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
726c33422   David Howells   [PATCH] VFS: Perm...
261

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
  	buf->f_type = HFSPLUS_SUPER_MAGIC;
  	buf->f_bsize = sb->s_blocksize;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
264
265
  	buf->f_blocks = sbi->total_blocks << sbi->fs_shift;
  	buf->f_bfree = sbi->free_blocks << sbi->fs_shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
  	buf->f_bavail = buf->f_bfree;
  	buf->f_files = 0xFFFFFFFF;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
268
  	buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid;
25564dd86   Coly Li   fs/hfsplus: retur...
269
270
  	buf->f_fsid.val[0] = (u32)id;
  	buf->f_fsid.val[1] = (u32)(id >> 32);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
280
  	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)) {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
281
  		struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
6f80dfe55   Christoph Hellwig   hfsplus: fix opti...
282
  		int force = 0;
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
283

6f80dfe55   Christoph Hellwig   hfsplus: fix opti...
284
  		if (!hfsplus_parse_options_remount(data, &force))
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
285
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  
  		if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
288
289
290
291
292
  			printk(KERN_WARNING "hfs: filesystem was "
  					"not cleanly unmounted, "
  					"running fsck.hfsplus is recommended.  "
  					"leaving read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
  			sb->s_flags |= MS_RDONLY;
  			*flags |= MS_RDONLY;
6f80dfe55   Christoph Hellwig   hfsplus: fix opti...
295
  		} else if (force) {
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
296
  			/* nothing */
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
297
298
299
300
301
  		} else if (vhdr->attributes &
  				cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
  			printk(KERN_WARNING "hfs: filesystem is marked locked, "
  					"leaving read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
  			sb->s_flags |= MS_RDONLY;
  			*flags |= MS_RDONLY;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
304
305
306
307
308
309
  		} else if (vhdr->attributes &
  				cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
  			printk(KERN_WARNING "hfs: filesystem is "
  					"marked journaled, "
  					"leaving read-only.
  ");
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
310
311
  			sb->s_flags |= MS_RDONLY;
  			*flags |= MS_RDONLY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
  		}
  	}
  	return 0;
  }
ee9b6d61a   Josef 'Jeff' Sipek   [PATCH] Mark stru...
316
  static const struct super_operations hfsplus_sops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
  	.alloc_inode	= hfsplus_alloc_inode,
  	.destroy_inode	= hfsplus_destroy_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  	.write_inode	= hfsplus_write_inode,
b57922d97   Al Viro   convert remaining...
320
  	.evict_inode	= hfsplus_evict_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
  	.put_super	= hfsplus_put_super,
  	.write_super	= hfsplus_write_super,
7fbc6df0e   Christoph Hellwig   hfsplus: add ->sy...
323
  	.sync_fs	= hfsplus_sync_fs,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
  	.statfs		= hfsplus_statfs,
  	.remount_fs	= hfsplus_remount,
717dd80e9   Roman Zippel   [PATCH] hfs: show...
326
  	.show_options	= hfsplus_show_options,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
334
  };
  
  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;
635253915   David Howells   iget: stop HFSPLU...
335
  	struct inode *root, *inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  	struct qstr str;
  	struct nls_table *nls = NULL;
f1fcd9f0e   Christoph Hellwig   hfsplus: fix file...
338
  	u64 last_fs_block, last_fs_page;
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
339
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
341
  	err = -EINVAL;
a5001a278   Wyatt Banks   HFSPlus: change k...
342
  	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  	if (!sbi)
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
344
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  	sb->s_fs_info = sbi;
40bf48afe   Christoph Hellwig   hfsplus: introduc...
347
  	mutex_init(&sbi->alloc_mutex);
7ac9fb9c2   Christoph Hellwig   hfsplus: add per-...
348
  	mutex_init(&sbi->vh_mutex);
717dd80e9   Roman Zippel   [PATCH] hfs: show...
349
  	hfsplus_fill_defaults(sbi);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
350
351
  
  	err = -EINVAL;
717dd80e9   Roman Zippel   [PATCH] hfs: show...
352
  	if (!hfsplus_parse_options(data, sbi)) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
353
354
  		printk(KERN_ERR "hfs: unable to parse mount options
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
355
  		goto out_unload_nls;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
360
  	}
  
  	/* temporarily use utf8 to correctly find the hidden dir below */
  	nls = sbi->nls;
  	sbi->nls = load_nls("utf8");
bd6a59b22   Joshua Kwan   [PATCH] hfsplus o...
361
  	if (!sbi->nls) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
362
363
  		printk(KERN_ERR "hfs: unable to load nls for utf8
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
364
  		goto out_unload_nls;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
  	}
  
  	/* Grab the volume header */
  	if (hfsplus_read_wrapper(sb)) {
  		if (!silent)
634725a92   Roman Zippel   [PATCH] hfs: clea...
370
371
  			printk(KERN_WARNING "hfs: unable to find HFS+ superblock
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
372
  		goto out_unload_nls;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
374
  	vhdr = sbi->s_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
  
  	/* Copy parts of the volume header into the superblock */
2179d372d   David Elliott   [PATCH] hfs: add ...
377
378
379
  	sb->s_magic = HFSPLUS_VOLHEAD_SIG;
  	if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
  	    be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
380
381
  		printk(KERN_ERR "hfs: wrong filesystem version
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
382
  		goto out_free_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
384
385
  	sbi->total_blocks = be32_to_cpu(vhdr->total_blocks);
  	sbi->free_blocks = be32_to_cpu(vhdr->free_blocks);
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
386
387
388
389
390
391
392
393
394
395
396
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397

f1fcd9f0e   Christoph Hellwig   hfsplus: fix file...
398
399
400
401
402
403
404
  	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))) {
c6d5f5fa6   Christoph Hellwig   hfsplus: lift the...
405
406
407
408
  		printk(KERN_ERR "hfs: filesystem size too large.
  ");
  		goto out_free_vhdr;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
411
412
413
  	/* 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))) {
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
414
415
416
417
418
  		printk(KERN_WARNING "hfs: Filesystem was "
  				"not cleanly unmounted, "
  				"running fsck.hfsplus is recommended.  "
  				"mounting read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
  		sb->s_flags |= MS_RDONLY;
84adede31   Christoph Hellwig   hfsplus: use atom...
420
  	} else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
421
  		/* nothing */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  	} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
423
424
  		printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  		sb->s_flags |= MS_RDONLY;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
426
427
428
429
430
431
432
  	} 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.
  ");
b0b623c3b   Roman Zippel   [PATCH] hfsplus: ...
433
  		sb->s_flags |= MS_RDONLY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  	}
c6d5f5fa6   Christoph Hellwig   hfsplus: lift the...
435
  	err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  	/* Load metadata objects (B*Trees) */
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
437
438
  	sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
  	if (!sbi->ext_tree) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
439
440
  		printk(KERN_ERR "hfs: failed to load extents file
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
441
  		goto out_free_vhdr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
443
444
  	sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
  	if (!sbi->cat_tree) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
445
446
  		printk(KERN_ERR "hfs: failed to load catalog file
  ");
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
447
  		goto out_close_ext_tree;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
  	}
635253915   David Howells   iget: stop HFSPLU...
449
450
  	inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
  	if (IS_ERR(inode)) {
634725a92   Roman Zippel   [PATCH] hfs: clea...
451
452
  		printk(KERN_ERR "hfs: failed to load allocation file
  ");
635253915   David Howells   iget: stop HFSPLU...
453
  		err = PTR_ERR(inode);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
454
  		goto out_close_cat_tree;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  	}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
456
  	sbi->alloc_file = inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  
  	/* Load the root directory */
635253915   David Howells   iget: stop HFSPLU...
459
460
461
462
463
  	root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
  	if (IS_ERR(root)) {
  		printk(KERN_ERR "hfs: failed to load root directory
  ");
  		err = PTR_ERR(root);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
464
  		goto out_put_alloc_file;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
  	}
  
  	str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
  	str.name = HFSP_HIDDENDIR_NAME;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
469
470
471
  	err = hfs_find_init(sbi->cat_tree, &fd);
  	if (err)
  		goto out_put_root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
  	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))
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
476
  			goto out_put_root;
635253915   David Howells   iget: stop HFSPLU...
477
478
479
  		inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
  		if (IS_ERR(inode)) {
  			err = PTR_ERR(inode);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
480
  			goto out_put_root;
635253915   David Howells   iget: stop HFSPLU...
481
  		}
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
482
  		sbi->hidden_dir = inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  	} else
  		hfs_find_exit(&fd);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
485
486
487
488
489
490
491
492
493
494
495
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496

c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
497
498
499
  		if (!sbi->hidden_dir) {
  			mutex_lock(&sbi->vh_mutex);
  			sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
b3f2a9244   Al Viro   hfsplus: creation...
500
501
502
503
504
505
506
  			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);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
507
  			mutex_unlock(&sbi->vh_mutex);
b3f2a9244   Al Viro   hfsplus: creation...
508
509
  			if (err)
  				goto out_put_hidden_dir;
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
510
511
512
513
  
  			hfsplus_mark_inode_dirty(sbi->hidden_dir,
  						 HFSPLUS_I_CAT_DIRTY);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
  	}
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
515
516
517
518
519
520
521
  
  	sb->s_d_op = &hfsplus_dentry_operations;
  	sb->s_root = d_alloc_root(root);
  	if (!sb->s_root) {
  		err = -ENOMEM;
  		goto out_put_hidden_dir;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
  	unload_nls(sbi->nls);
  	sbi->nls = nls;
  	return 0;
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
525
526
527
  out_put_hidden_dir:
  	iput(sbi->hidden_dir);
  out_put_root:
032016a56   Alexey Khoroshilov   hfsplus: Fix doub...
528
  	iput(root);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
529
530
531
532
533
534
535
  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:
f588c960f   Seth Forshee   hfsplus: Fix kfre...
536
537
  	kfree(sbi->s_vhdr_buf);
  	kfree(sbi->s_backup_vhdr_buf);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
538
539
  out_unload_nls:
  	unload_nls(sbi->nls);
6d729e44a   Thomas Gleixner   fs: Make unload_n...
540
  	unload_nls(nls);
c5b8d0bce   Christoph Hellwig   hfsplus: fix fail...
541
542
  	kfree(sbi);
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
545
546
547
548
  	return err;
  }
  
  MODULE_AUTHOR("Brad Boyer");
  MODULE_DESCRIPTION("Extended Macintosh Filesystem");
  MODULE_LICENSE("GPL");
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
549
  static struct kmem_cache *hfsplus_inode_cachep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
  
  static struct inode *hfsplus_alloc_inode(struct super_block *sb)
  {
  	struct hfsplus_inode_info *i;
e94b17660   Christoph Lameter   [PATCH] slab: rem...
554
  	i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
  	return i ? &i->vfs_inode : NULL;
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
557
  static void hfsplus_i_callback(struct rcu_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  {
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
559
  	struct inode *inode = container_of(head, struct inode, i_rcu);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
560
  	kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  }
fa0d7e3de   Nick Piggin   fs: icache RCU fr...
562
563
564
565
  static void hfsplus_destroy_inode(struct inode *inode)
  {
  	call_rcu(&inode->i_rcu, hfsplus_i_callback);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  #define HFSPLUS_INODE_SIZE	sizeof(struct hfsplus_inode_info)
152a08366   Al Viro   new helper: mount...
567
568
  static struct dentry *hfsplus_mount(struct file_system_type *fs_type,
  			  int flags, const char *dev_name, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
  {
152a08366   Al Viro   new helper: mount...
570
  	return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
  }
  
  static struct file_system_type hfsplus_fs_type = {
  	.owner		= THIS_MODULE,
  	.name		= "hfsplus",
152a08366   Al Viro   new helper: mount...
576
  	.mount		= hfsplus_mount,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
  	.kill_sb	= kill_block_super,
  	.fs_flags	= FS_REQUIRES_DEV,
  };
51cc50685   Alexey Dobriyan   SL*B: drop kmem c...
580
  static void hfsplus_init_once(void *p)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
  {
  	struct hfsplus_inode_info *i = p;
a35afb830   Christoph Lameter   Remove SLAB_CTOR_...
583
  	inode_init_once(&i->vfs_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
  }
  
  static int __init init_hfsplus_fs(void)
  {
  	int err;
  
  	hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
  		HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
20c2df83d   Paul Mundt   mm: Remove slab d...
592
  		hfsplus_init_once);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
597
598
599
600
601
602
603
  	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);
1a1d92c10   Alexey Dobriyan   [PATCH] Really ig...
604
  	kmem_cache_destroy(hfsplus_inode_cachep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
  }
  
  module_init(init_hfsplus_fs)
  module_exit(exit_hfsplus_fs)