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>
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index a4e867e..42e1286 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -271,8 +271,14 @@
}
file->private_data = rd;
rd->file = file;
+ spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
+ spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
}
+ /*
+ * Can be done after the list insertion; exclusion with
+ * hfsplus_delete_cat() is provided by directory lock.
+ */
memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
out:
kfree(strbuf);
@@ -284,9 +290,9 @@
{
struct hfsplus_readdir_data *rd = file->private_data;
if (rd) {
- inode_lock(inode);
+ spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
list_del(&rd->list);
- inode_unlock(inode);
+ spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
kfree(rd);
}
return 0;
@@ -569,7 +575,7 @@
const struct file_operations hfsplus_dir_operations = {
.fsync = hfsplus_file_fsync,
.read = generic_read_dir,
- .iterate = hfsplus_readdir,
+ .iterate_shared = hfsplus_readdir,
.unlocked_ioctl = hfsplus_ioctl,
.llseek = generic_file_llseek,
.release = hfsplus_dir_release,