Commit 323ee8fc authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 552a9d48
...@@ -374,12 +374,15 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) ...@@ -374,12 +374,15 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
} }
/* we only need to take spinlock for exclusion with ->release() */
spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) { list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
struct hfsplus_readdir_data *rd = struct hfsplus_readdir_data *rd =
list_entry(pos, struct hfsplus_readdir_data, list); list_entry(pos, struct hfsplus_readdir_data, list);
if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
rd->file->f_pos--; rd->file->f_pos--;
} }
spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
err = hfs_brec_remove(&fd); err = hfs_brec_remove(&fd);
if (err) if (err)
......
...@@ -271,8 +271,14 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) ...@@ -271,8 +271,14 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
} }
file->private_data = rd; file->private_data = rd;
rd->file = file; rd->file = file;
spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); 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)); memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
out: out:
kfree(strbuf); kfree(strbuf);
...@@ -284,9 +290,9 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file) ...@@ -284,9 +290,9 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file)
{ {
struct hfsplus_readdir_data *rd = file->private_data; struct hfsplus_readdir_data *rd = file->private_data;
if (rd) { if (rd) {
inode_lock(inode); spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
list_del(&rd->list); list_del(&rd->list);
inode_unlock(inode); spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
kfree(rd); kfree(rd);
} }
return 0; return 0;
...@@ -569,7 +575,7 @@ const struct inode_operations hfsplus_dir_inode_operations = { ...@@ -569,7 +575,7 @@ const struct inode_operations hfsplus_dir_inode_operations = {
const struct file_operations hfsplus_dir_operations = { const struct file_operations hfsplus_dir_operations = {
.fsync = hfsplus_file_fsync, .fsync = hfsplus_file_fsync,
.read = generic_read_dir, .read = generic_read_dir,
.iterate = hfsplus_readdir, .iterate_shared = hfsplus_readdir,
.unlocked_ioctl = hfsplus_ioctl, .unlocked_ioctl = hfsplus_ioctl,
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.release = hfsplus_dir_release, .release = hfsplus_dir_release,
......
...@@ -244,6 +244,7 @@ struct hfsplus_inode_info { ...@@ -244,6 +244,7 @@ struct hfsplus_inode_info {
u8 userflags; /* BSD user file flags */ u8 userflags; /* BSD user file flags */
u32 subfolders; /* Subfolder count (HFSX only) */ u32 subfolders; /* Subfolder count (HFSX only) */
struct list_head open_dir_list; struct list_head open_dir_list;
spinlock_t open_dir_lock;
loff_t phys_size; loff_t phys_size;
struct inode vfs_inode; struct inode vfs_inode;
......
...@@ -374,6 +374,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode) ...@@ -374,6 +374,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
hip = HFSPLUS_I(inode); hip = HFSPLUS_I(inode);
INIT_LIST_HEAD(&hip->open_dir_list); INIT_LIST_HEAD(&hip->open_dir_list);
spin_lock_init(&hip->open_dir_lock);
mutex_init(&hip->extents_lock); mutex_init(&hip->extents_lock);
atomic_set(&hip->opencnt, 0); atomic_set(&hip->opencnt, 0);
hip->extent_state = 0; hip->extent_state = 0;
......
...@@ -67,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) ...@@ -67,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
return inode; return inode;
INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list);
spin_lock_init(&HFSPLUS_I(inode)->open_dir_lock);
mutex_init(&HFSPLUS_I(inode)->extents_lock); mutex_init(&HFSPLUS_I(inode)->extents_lock);
HFSPLUS_I(inode)->flags = 0; HFSPLUS_I(inode)->flags = 0;
HFSPLUS_I(inode)->extent_state = 0; HFSPLUS_I(inode)->extent_state = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment