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>
This commit is contained in:
Родитель
552a9d489f
Коммит
323ee8fc54
|
@ -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);
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
struct hfsplus_readdir_data *rd =
|
||||
list_entry(pos, struct hfsplus_readdir_data, list);
|
||||
if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
|
||||
rd->file->f_pos--;
|
||||
}
|
||||
spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
|
||||
|
||||
err = hfs_brec_remove(&fd);
|
||||
if (err)
|
||||
|
|
|
@ -271,8 +271,14 @@ next:
|
|||
}
|
||||
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 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file)
|
|||
{
|
||||
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 inode_operations hfsplus_dir_inode_operations = {
|
|||
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,
|
||||
|
|
|
@ -244,6 +244,7 @@ struct hfsplus_inode_info {
|
|||
u8 userflags; /* BSD user file flags */
|
||||
u32 subfolders; /* Subfolder count (HFSX only) */
|
||||
struct list_head open_dir_list;
|
||||
spinlock_t open_dir_lock;
|
||||
loff_t phys_size;
|
||||
|
||||
struct inode vfs_inode;
|
||||
|
|
|
@ -374,6 +374,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
|
|||
|
||||
hip = HFSPLUS_I(inode);
|
||||
INIT_LIST_HEAD(&hip->open_dir_list);
|
||||
spin_lock_init(&hip->open_dir_lock);
|
||||
mutex_init(&hip->extents_lock);
|
||||
atomic_set(&hip->opencnt, 0);
|
||||
hip->extent_state = 0;
|
||||
|
|
|
@ -67,6 +67,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
|
|||
return inode;
|
||||
|
||||
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);
|
||||
HFSPLUS_I(inode)->flags = 0;
|
||||
HFSPLUS_I(inode)->extent_state = 0;
|
||||
|
|
Загрузка…
Ссылка в новой задаче