aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-05-12 20:02:09 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-05-12 20:08:40 -0400
commit323ee8fc544d407eb053471b9607f95f987f5f12 (patch)
tree7185ea5c3d7b5d08492bd93bb714bc5338b5355c /fs/hfsplus
parent552a9d489f1412beb5914f0c64a54d921a9c6624 (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.c3
-rw-r--r--fs/hfsplus/dir.c12
-rw-r--r--fs/hfsplus/hfsplus_fs.h1
-rw-r--r--fs/hfsplus/inode.c1
-rw-r--r--fs/hfsplus/super.c1
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));
277out: 283out:
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 = {
569const struct file_operations hfsplus_dir_operations = { 575const 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;