diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-05-12 20:02:09 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-05-12 20:08:40 -0400 |
commit | 323ee8fc544d407eb053471b9607f95f987f5f12 (patch) | |
tree | 7185ea5c3d7b5d08492bd93bb714bc5338b5355c /fs/hfsplus | |
parent | 552a9d489f1412beb5914f0c64a54d921a9c6624 (diff) |
hfsplus: switch to ->iterate_shared()
We need to protect the list of hfsplus_readdir_data against parallel
insertions (in readdir) and removals (in release). Add a spinlock
for that. Note that it has nothing to do with protection of
hfsplus_readdir_data->key - we have an exclusion between hfsplus_readdir()
and hfsplus_delete_cat() on directory lock and between several
hfsplus_readdir() for the same struct file on ->f_pos_lock. The spinlock
is strictly for list changes.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/hfsplus')
-rw-r--r-- | fs/hfsplus/catalog.c | 3 | ||||
-rw-r--r-- | fs/hfsplus/dir.c | 12 | ||||
-rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 1 | ||||
-rw-r--r-- | fs/hfsplus/inode.c | 1 | ||||
-rw-r--r-- | fs/hfsplus/super.c | 1 |
5 files changed, 15 insertions, 3 deletions
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 022974ab6e3c..fb707e8f423a 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c | |||
@@ -374,12 +374,15 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) | |||
374 | hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); | 374 | hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); |
375 | } | 375 | } |
376 | 376 | ||
377 | /* we only need to take spinlock for exclusion with ->release() */ | ||
378 | spin_lock(&HFSPLUS_I(dir)->open_dir_lock); | ||
377 | list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) { | 379 | list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) { |
378 | struct hfsplus_readdir_data *rd = | 380 | struct hfsplus_readdir_data *rd = |
379 | list_entry(pos, struct hfsplus_readdir_data, list); | 381 | list_entry(pos, struct hfsplus_readdir_data, list); |
380 | if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) | 382 | if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) |
381 | rd->file->f_pos--; | 383 | rd->file->f_pos--; |
382 | } | 384 | } |
385 | spin_unlock(&HFSPLUS_I(dir)->open_dir_lock); | ||
383 | 386 | ||
384 | err = hfs_brec_remove(&fd); | 387 | err = hfs_brec_remove(&fd); |
385 | if (err) | 388 | if (err) |
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index a4e867e08947..42e128661dc1 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c | |||
@@ -271,8 +271,14 @@ next: | |||
271 | } | 271 | } |
272 | file->private_data = rd; | 272 | file->private_data = rd; |
273 | rd->file = file; | 273 | rd->file = file; |
274 | spin_lock(&HFSPLUS_I(inode)->open_dir_lock); | ||
274 | list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); | 275 | list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); |
276 | spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); | ||
275 | } | 277 | } |
278 | /* | ||
279 | * Can be done after the list insertion; exclusion with | ||
280 | * hfsplus_delete_cat() is provided by directory lock. | ||
281 | */ | ||
276 | memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); | 282 | memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); |
277 | out: | 283 | out: |
278 | kfree(strbuf); | 284 | kfree(strbuf); |
@@ -284,9 +290,9 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file) | |||
284 | { | 290 | { |
285 | struct hfsplus_readdir_data *rd = file->private_data; | 291 | struct hfsplus_readdir_data *rd = file->private_data; |
286 | if (rd) { | 292 | if (rd) { |
287 | inode_lock(inode); | 293 | spin_lock(&HFSPLUS_I(inode)->open_dir_lock); |
288 | list_del(&rd->list); | 294 | list_del(&rd->list); |
289 | inode_unlock(inode); | 295 | spin_unlock(&HFSPLUS_I(inode)->open_dir_lock); |
290 | kfree(rd); | 296 | kfree(rd); |
291 | } | 297 | } |
292 | return 0; | 298 | return 0; |
@@ -569,7 +575,7 @@ const struct inode_operations hfsplus_dir_inode_operations = { | |||
569 | const struct file_operations hfsplus_dir_operations = { | 575 | const struct file_operations hfsplus_dir_operations = { |
570 | .fsync = hfsplus_file_fsync, | 576 | .fsync = hfsplus_file_fsync, |
571 | .read = generic_read_dir, | 577 | .read = generic_read_dir, |
572 | .iterate = hfsplus_readdir, | 578 | .iterate_shared = hfsplus_readdir, |
573 | .unlocked_ioctl = hfsplus_ioctl, | 579 | .unlocked_ioctl = hfsplus_ioctl, |
574 | .llseek = generic_file_llseek, | 580 | .llseek = generic_file_llseek, |
575 | .release = hfsplus_dir_release, | 581 | .release = hfsplus_dir_release, |
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index f91a1faf819e..fdc3446d934a 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h | |||
@@ -244,6 +244,7 @@ struct hfsplus_inode_info { | |||
244 | u8 userflags; /* BSD user file flags */ | 244 | u8 userflags; /* BSD user file flags */ |
245 | u32 subfolders; /* Subfolder count (HFSX only) */ | 245 | u32 subfolders; /* Subfolder count (HFSX only) */ |
246 | struct list_head open_dir_list; | 246 | struct list_head open_dir_list; |
247 | spinlock_t open_dir_lock; | ||
247 | loff_t phys_size; | 248 | loff_t phys_size; |
248 | 249 | ||
249 | struct inode vfs_inode; | 250 | struct inode vfs_inode; |
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index b28f39865c3a..037f738c5871 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c | |||
@@ -374,6 +374,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) | |||
374 | 374 | ||
375 | hip = HFSPLUS_I(inode); | 375 | hip = HFSPLUS_I(inode); |
376 | INIT_LIST_HEAD(&hip->open_dir_list); | 376 | INIT_LIST_HEAD(&hip->open_dir_list); |
377 | spin_lock_init(&hip->open_dir_lock); | ||
377 | mutex_init(&hip->extents_lock); | 378 | mutex_init(&hip->extents_lock); |
378 | atomic_set(&hip->opencnt, 0); | 379 | atomic_set(&hip->opencnt, 0); |
379 | hip->extent_state = 0; | 380 | hip->extent_state = 0; |
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index c35911362ff9..755bf30ba1ce 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c | |||
@@ -67,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) | |||
67 | return inode; | 67 | return inode; |
68 | 68 | ||
69 | INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); | 69 | INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); |
70 | spin_lock_init(&HFSPLUS_I(inode)->open_dir_lock); | ||
70 | mutex_init(&HFSPLUS_I(inode)->extents_lock); | 71 | mutex_init(&HFSPLUS_I(inode)->extents_lock); |
71 | HFSPLUS_I(inode)->flags = 0; | 72 | HFSPLUS_I(inode)->flags = 0; |
72 | HFSPLUS_I(inode)->extent_state = 0; | 73 | HFSPLUS_I(inode)->extent_state = 0; |