Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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)
+9 −3
Original line number Diff line number Diff line
@@ -271,8 +271,14 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
		}
		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,
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;