diff options
author | Christoph Hellwig <hch@tuxera.com> | 2010-09-30 23:45:08 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2010-09-30 23:45:08 -0400 |
commit | 7ac9fb9c2a50963b699b3548e6f00698c1554dc6 (patch) | |
tree | 3660bfb2985c65838b5969694e54043ae6639b3a /fs/hfsplus | |
parent | 58a818f532e83f337689358c102ba2048d1b37f5 (diff) |
hfsplus: add per-superblock lock for volume header updates
Lock updates to the mutal fields in the volume header, and document the
locing in the hfsplus_sb_info structure.
Signed-off-by: Christoph Hellwig <hch@tuxera.com>
Diffstat (limited to 'fs/hfsplus')
-rw-r--r-- | fs/hfsplus/dir.c | 54 | ||||
-rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 13 | ||||
-rw-r--r-- | fs/hfsplus/super.c | 7 |
3 files changed, 50 insertions, 24 deletions
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index f8ae468f4ab6..1c81eedcab01 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c | |||
@@ -251,6 +251,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, | |||
251 | if (HFSPLUS_IS_RSRC(inode)) | 251 | if (HFSPLUS_IS_RSRC(inode)) |
252 | return -EPERM; | 252 | return -EPERM; |
253 | 253 | ||
254 | mutex_lock(&sbi->vh_mutex); | ||
254 | if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { | 255 | if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { |
255 | for (;;) { | 256 | for (;;) { |
256 | get_random_bytes(&id, sizeof(cnid)); | 257 | get_random_bytes(&id, sizeof(cnid)); |
@@ -263,7 +264,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, | |||
263 | if (!res) | 264 | if (!res) |
264 | break; | 265 | break; |
265 | if (res != -EEXIST) | 266 | if (res != -EEXIST) |
266 | return res; | 267 | goto out; |
267 | } | 268 | } |
268 | HFSPLUS_I(inode)->dev = id; | 269 | HFSPLUS_I(inode)->dev = id; |
269 | cnid = sbi->next_cnid++; | 270 | cnid = sbi->next_cnid++; |
@@ -271,13 +272,13 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, | |||
271 | res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); | 272 | res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); |
272 | if (res) | 273 | if (res) |
273 | /* panic? */ | 274 | /* panic? */ |
274 | return res; | 275 | goto out; |
275 | sbi->file_count++; | 276 | sbi->file_count++; |
276 | } | 277 | } |
277 | cnid = sbi->next_cnid++; | 278 | cnid = sbi->next_cnid++; |
278 | res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); | 279 | res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); |
279 | if (res) | 280 | if (res) |
280 | return res; | 281 | goto out; |
281 | 282 | ||
282 | inc_nlink(inode); | 283 | inc_nlink(inode); |
283 | hfsplus_instantiate(dst_dentry, inode, cnid); | 284 | hfsplus_instantiate(dst_dentry, inode, cnid); |
@@ -286,8 +287,9 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, | |||
286 | mark_inode_dirty(inode); | 287 | mark_inode_dirty(inode); |
287 | sbi->file_count++; | 288 | sbi->file_count++; |
288 | dst_dir->i_sb->s_dirt = 1; | 289 | dst_dir->i_sb->s_dirt = 1; |
289 | 290 | out: | |
290 | return 0; | 291 | mutex_unlock(&sbi->vh_mutex); |
292 | return res; | ||
291 | } | 293 | } |
292 | 294 | ||
293 | static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) | 295 | static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) |
@@ -302,6 +304,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) | |||
302 | if (HFSPLUS_IS_RSRC(inode)) | 304 | if (HFSPLUS_IS_RSRC(inode)) |
303 | return -EPERM; | 305 | return -EPERM; |
304 | 306 | ||
307 | mutex_lock(&sbi->vh_mutex); | ||
305 | cnid = (u32)(unsigned long)dentry->d_fsdata; | 308 | cnid = (u32)(unsigned long)dentry->d_fsdata; |
306 | if (inode->i_ino == cnid && | 309 | if (inode->i_ino == cnid && |
307 | atomic_read(&HFSPLUS_I(inode)->opencnt)) { | 310 | atomic_read(&HFSPLUS_I(inode)->opencnt)) { |
@@ -312,11 +315,11 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) | |||
312 | sbi->hidden_dir, &str); | 315 | sbi->hidden_dir, &str); |
313 | if (!res) | 316 | if (!res) |
314 | inode->i_flags |= S_DEAD; | 317 | inode->i_flags |= S_DEAD; |
315 | return res; | 318 | goto out; |
316 | } | 319 | } |
317 | res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); | 320 | res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); |
318 | if (res) | 321 | if (res) |
319 | return res; | 322 | goto out; |
320 | 323 | ||
321 | if (inode->i_nlink > 0) | 324 | if (inode->i_nlink > 0) |
322 | drop_nlink(inode); | 325 | drop_nlink(inode); |
@@ -339,37 +342,44 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) | |||
339 | sbi->file_count--; | 342 | sbi->file_count--; |
340 | inode->i_ctime = CURRENT_TIME_SEC; | 343 | inode->i_ctime = CURRENT_TIME_SEC; |
341 | mark_inode_dirty(inode); | 344 | mark_inode_dirty(inode); |
342 | 345 | out: | |
346 | mutex_unlock(&sbi->vh_mutex); | ||
343 | return res; | 347 | return res; |
344 | } | 348 | } |
345 | 349 | ||
346 | static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) | 350 | static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) |
347 | { | 351 | { |
348 | struct inode *inode; | 352 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); |
353 | struct inode *inode = dentry->d_inode; | ||
349 | int res; | 354 | int res; |
350 | 355 | ||
351 | inode = dentry->d_inode; | ||
352 | if (inode->i_size != 2) | 356 | if (inode->i_size != 2) |
353 | return -ENOTEMPTY; | 357 | return -ENOTEMPTY; |
358 | |||
359 | mutex_lock(&sbi->vh_mutex); | ||
354 | res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); | 360 | res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); |
355 | if (res) | 361 | if (res) |
356 | return res; | 362 | goto out; |
357 | clear_nlink(inode); | 363 | clear_nlink(inode); |
358 | inode->i_ctime = CURRENT_TIME_SEC; | 364 | inode->i_ctime = CURRENT_TIME_SEC; |
359 | hfsplus_delete_inode(inode); | 365 | hfsplus_delete_inode(inode); |
360 | mark_inode_dirty(inode); | 366 | mark_inode_dirty(inode); |
361 | return 0; | 367 | out: |
368 | mutex_unlock(&sbi->vh_mutex); | ||
369 | return res; | ||
362 | } | 370 | } |
363 | 371 | ||
364 | static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, | 372 | static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, |
365 | const char *symname) | 373 | const char *symname) |
366 | { | 374 | { |
375 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); | ||
367 | struct inode *inode; | 376 | struct inode *inode; |
368 | int res; | 377 | int res = -ENOSPC; |
369 | 378 | ||
379 | mutex_lock(&sbi->vh_mutex); | ||
370 | inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); | 380 | inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); |
371 | if (!inode) | 381 | if (!inode) |
372 | return -ENOSPC; | 382 | goto out; |
373 | 383 | ||
374 | res = page_symlink(inode, symname, strlen(symname) + 1); | 384 | res = page_symlink(inode, symname, strlen(symname) + 1); |
375 | if (res) | 385 | if (res) |
@@ -381,31 +391,35 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, | |||
381 | 391 | ||
382 | hfsplus_instantiate(dentry, inode, inode->i_ino); | 392 | hfsplus_instantiate(dentry, inode, inode->i_ino); |
383 | mark_inode_dirty(inode); | 393 | mark_inode_dirty(inode); |
384 | return 0; | 394 | goto out; |
385 | 395 | ||
386 | out_err: | 396 | out_err: |
387 | inode->i_nlink = 0; | 397 | inode->i_nlink = 0; |
388 | hfsplus_delete_inode(inode); | 398 | hfsplus_delete_inode(inode); |
389 | iput(inode); | 399 | iput(inode); |
400 | out: | ||
401 | mutex_unlock(&sbi->vh_mutex); | ||
390 | return res; | 402 | return res; |
391 | } | 403 | } |
392 | 404 | ||
393 | static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, | 405 | static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, |
394 | int mode, dev_t rdev) | 406 | int mode, dev_t rdev) |
395 | { | 407 | { |
408 | struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); | ||
396 | struct inode *inode; | 409 | struct inode *inode; |
397 | int res; | 410 | int res = -ENOSPC; |
398 | 411 | ||
412 | mutex_lock(&sbi->vh_mutex); | ||
399 | inode = hfsplus_new_inode(dir->i_sb, mode); | 413 | inode = hfsplus_new_inode(dir->i_sb, mode); |
400 | if (!inode) | 414 | if (!inode) |
401 | return -ENOSPC; | 415 | goto out; |
402 | 416 | ||
403 | res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); | 417 | res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); |
404 | if (res) { | 418 | if (res) { |
405 | inode->i_nlink = 0; | 419 | inode->i_nlink = 0; |
406 | hfsplus_delete_inode(inode); | 420 | hfsplus_delete_inode(inode); |
407 | iput(inode); | 421 | iput(inode); |
408 | return res; | 422 | goto out; |
409 | } | 423 | } |
410 | 424 | ||
411 | if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) | 425 | if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) |
@@ -413,7 +427,9 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, | |||
413 | 427 | ||
414 | hfsplus_instantiate(dentry, inode, inode->i_ino); | 428 | hfsplus_instantiate(dentry, inode, inode->i_ino); |
415 | mark_inode_dirty(inode); | 429 | mark_inode_dirty(inode); |
416 | return 0; | 430 | out: |
431 | mutex_unlock(&sbi->vh_mutex); | ||
432 | return res; | ||
417 | } | 433 | } |
418 | 434 | ||
419 | static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, | 435 | static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, |
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 0cd9ba00f968..08865ed70f00 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h | |||
@@ -116,23 +116,26 @@ struct hfsplus_sb_info { | |||
116 | struct inode *hidden_dir; | 116 | struct inode *hidden_dir; |
117 | struct nls_table *nls; | 117 | struct nls_table *nls; |
118 | 118 | ||
119 | /* synchronize block allocations */ | ||
120 | struct mutex alloc_mutex; | ||
121 | |||
122 | /* Runtime variables */ | 119 | /* Runtime variables */ |
123 | u32 blockoffset; | 120 | u32 blockoffset; |
124 | u32 sect_count; | 121 | u32 sect_count; |
125 | int fs_shift; | 122 | int fs_shift; |
126 | 123 | ||
127 | /* Stuff in host order from Vol Header */ | 124 | /* immutable data from the volume header */ |
128 | u32 alloc_blksz; | 125 | u32 alloc_blksz; |
129 | int alloc_blksz_shift; | 126 | int alloc_blksz_shift; |
130 | u32 total_blocks; | 127 | u32 total_blocks; |
128 | u32 data_clump_blocks, rsrc_clump_blocks; | ||
129 | |||
130 | /* mutable data from the volume header, protected by alloc_mutex */ | ||
131 | u32 free_blocks; | 131 | u32 free_blocks; |
132 | struct mutex alloc_mutex; | ||
133 | |||
134 | /* mutable data from the volume header, protected by vh_mutex */ | ||
132 | u32 next_cnid; | 135 | u32 next_cnid; |
133 | u32 file_count; | 136 | u32 file_count; |
134 | u32 folder_count; | 137 | u32 folder_count; |
135 | u32 data_clump_blocks, rsrc_clump_blocks; | 138 | struct mutex vh_mutex; |
136 | 139 | ||
137 | /* Config options */ | 140 | /* Config options */ |
138 | u32 creator; | 141 | u32 creator; |
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 923f385b32ca..b766c170e4d8 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c | |||
@@ -160,6 +160,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) | |||
160 | 160 | ||
161 | dprint(DBG_SUPER, "hfsplus_write_super\n"); | 161 | dprint(DBG_SUPER, "hfsplus_write_super\n"); |
162 | 162 | ||
163 | mutex_lock(&sbi->vh_mutex); | ||
163 | mutex_lock(&sbi->alloc_mutex); | 164 | mutex_lock(&sbi->alloc_mutex); |
164 | sb->s_dirt = 0; | 165 | sb->s_dirt = 0; |
165 | 166 | ||
@@ -194,6 +195,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) | |||
194 | sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; | 195 | sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; |
195 | } | 196 | } |
196 | mutex_unlock(&sbi->alloc_mutex); | 197 | mutex_unlock(&sbi->alloc_mutex); |
198 | mutex_unlock(&sbi->vh_mutex); | ||
197 | return 0; | 199 | return 0; |
198 | } | 200 | } |
199 | 201 | ||
@@ -319,6 +321,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) | |||
319 | 321 | ||
320 | sb->s_fs_info = sbi; | 322 | sb->s_fs_info = sbi; |
321 | mutex_init(&sbi->alloc_mutex); | 323 | mutex_init(&sbi->alloc_mutex); |
324 | mutex_init(&sbi->vh_mutex); | ||
322 | hfsplus_fill_defaults(sbi); | 325 | hfsplus_fill_defaults(sbi); |
323 | if (!hfsplus_parse_options(data, sbi)) { | 326 | if (!hfsplus_parse_options(data, sbi)) { |
324 | printk(KERN_ERR "hfs: unable to parse mount options\n"); | 327 | printk(KERN_ERR "hfs: unable to parse mount options\n"); |
@@ -453,9 +456,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) | |||
453 | 456 | ||
454 | if (!sbi->hidden_dir) { | 457 | if (!sbi->hidden_dir) { |
455 | printk(KERN_DEBUG "hfs: create hidden dir...\n"); | 458 | printk(KERN_DEBUG "hfs: create hidden dir...\n"); |
459 | |||
460 | mutex_lock(&sbi->vh_mutex); | ||
456 | sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); | 461 | sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); |
457 | hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, | 462 | hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, |
458 | &str, sbi->hidden_dir); | 463 | &str, sbi->hidden_dir); |
464 | mutex_unlock(&sbi->vh_mutex); | ||
465 | |||
459 | mark_inode_dirty(sbi->hidden_dir); | 466 | mark_inode_dirty(sbi->hidden_dir); |
460 | } | 467 | } |
461 | out: | 468 | out: |