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