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

Commit 1e3827bf authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull vfs fixes from Al Viro:
 "Assorted fixes + unifying __d_move() and __d_materialise_dentry() +
  minimal regression fix for d_path() of victims of overwriting rename()
  ported on top of that"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  vfs: Don't exchange "short" filenames unconditionally.
  fold swapping ->d_name.hash into switch_names()
  fold unlocking the children into dentry_unlock_parents_for_move()
  kill __d_materialise_dentry()
  __d_materialise_dentry(): flip the order of arguments
  __d_move(): fold manipulations with ->d_child/->d_subdirs
  don't open-code d_rehash() in d_materialise_unique()
  pull rehashing and unlocking the target dentry into __d_materialise_dentry()
  ufs: deal with nfsd/iget races
  fuse: honour max_read and max_write in direct_io mode
  shmem: fix nlink for rename overwrite directory
parents 6111da34 d2fa4a84
Loading
Loading
Loading
Loading
+36 −76
Original line number Original line Diff line number Diff line
@@ -2372,7 +2372,8 @@ void dentry_update_name_case(struct dentry *dentry, struct qstr *name)
}
}
EXPORT_SYMBOL(dentry_update_name_case);
EXPORT_SYMBOL(dentry_update_name_case);


static void switch_names(struct dentry *dentry, struct dentry *target)
static void switch_names(struct dentry *dentry, struct dentry *target,
			 bool exchange)
{
{
	if (dname_external(target)) {
	if (dname_external(target)) {
		if (dname_external(dentry)) {
		if (dname_external(dentry)) {
@@ -2406,13 +2407,19 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
			 */
			 */
			unsigned int i;
			unsigned int i;
			BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
			BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
			if (!exchange) {
				memcpy(dentry->d_iname, target->d_name.name,
						target->d_name.len + 1);
				dentry->d_name.hash_len = target->d_name.hash_len;
				return;
			}
			for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
			for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
				swap(((long *) &dentry->d_iname)[i],
				swap(((long *) &dentry->d_iname)[i],
				     ((long *) &target->d_iname)[i]);
				     ((long *) &target->d_iname)[i]);
			}
			}
		}
		}
	}
	}
	swap(dentry->d_name.len, target->d_name.len);
	swap(dentry->d_name.hash_len, target->d_name.hash_len);
}
}


static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target)
static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target)
@@ -2442,25 +2449,29 @@ static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target)
	}
	}
}
}


static void dentry_unlock_parents_for_move(struct dentry *dentry,
static void dentry_unlock_for_move(struct dentry *dentry, struct dentry *target)
					struct dentry *target)
{
{
	if (target->d_parent != dentry->d_parent)
	if (target->d_parent != dentry->d_parent)
		spin_unlock(&dentry->d_parent->d_lock);
		spin_unlock(&dentry->d_parent->d_lock);
	if (target->d_parent != target)
	if (target->d_parent != target)
		spin_unlock(&target->d_parent->d_lock);
		spin_unlock(&target->d_parent->d_lock);
	spin_unlock(&target->d_lock);
	spin_unlock(&dentry->d_lock);
}
}


/*
/*
 * When switching names, the actual string doesn't strictly have to
 * When switching names, the actual string doesn't strictly have to
 * be preserved in the target - because we're dropping the target
 * be preserved in the target - because we're dropping the target
 * anyway. As such, we can just do a simple memcpy() to copy over
 * anyway. As such, we can just do a simple memcpy() to copy over
 * the new name before we switch.
 * the new name before we switch, unless we are going to rehash
 *
 * it.  Note that if we *do* unhash the target, we are not allowed
 * Note that we have to be a lot more careful about getting the hash
 * to rehash it without giving it a new name/hash key - whether
 * switched - we have to switch the hash value properly even if it
 * we swap or overwrite the names here, resulting name won't match
 * then no longer matches the actual (corrupted) string of the target.
 * the reality in filesystem; it's only there for d_path() purposes.
 * The hash value has to match the hash queue that the dentry is on..
 * Note that all of this is happening under rename_lock, so the
 * any hash lookup seeing it in the middle of manipulations will
 * be discarded anyway.  So we do not care what happens to the hash
 * key in that case.
 */
 */
/*
/*
 * __d_move - move a dentry
 * __d_move - move a dentry
@@ -2506,36 +2517,30 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
			   d_hash(dentry->d_parent, dentry->d_name.hash));
			   d_hash(dentry->d_parent, dentry->d_name.hash));
	}
	}


	list_del(&dentry->d_u.d_child);
	list_del(&target->d_u.d_child);

	/* Switch the names.. */
	/* Switch the names.. */
	switch_names(dentry, target);
	switch_names(dentry, target, exchange);
	swap(dentry->d_name.hash, target->d_name.hash);


	/* ... and switch the parents */
	/* ... and switch them in the tree */
	if (IS_ROOT(dentry)) {
	if (IS_ROOT(dentry)) {
		/* splicing a tree */
		dentry->d_parent = target->d_parent;
		dentry->d_parent = target->d_parent;
		target->d_parent = target;
		target->d_parent = target;
		INIT_LIST_HEAD(&target->d_u.d_child);
		list_del_init(&target->d_u.d_child);
		list_move(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
	} else {
	} else {
		/* swapping two dentries */
		swap(dentry->d_parent, target->d_parent);
		swap(dentry->d_parent, target->d_parent);

		list_move(&target->d_u.d_child, &target->d_parent->d_subdirs);
		/* And add them back to the (new) parent lists */
		list_move(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);
		list_add(&target->d_u.d_child, &target->d_parent->d_subdirs);
		if (exchange)
			fsnotify_d_move(target);
		fsnotify_d_move(dentry);
	}
	}


	list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs);

	write_seqcount_end(&target->d_seq);
	write_seqcount_end(&target->d_seq);
	write_seqcount_end(&dentry->d_seq);
	write_seqcount_end(&dentry->d_seq);


	dentry_unlock_parents_for_move(dentry, target);
	dentry_unlock_for_move(dentry, target);
	if (exchange)
		fsnotify_d_move(target);
	spin_unlock(&target->d_lock);
	fsnotify_d_move(dentry);
	spin_unlock(&dentry->d_lock);
}
}


/*
/*
@@ -2633,45 +2638,6 @@ static struct dentry *__d_unalias(struct inode *inode,
	return ret;
	return ret;
}
}


/*
 * Prepare an anonymous dentry for life in the superblock's dentry tree as a
 * named dentry in place of the dentry to be replaced.
 * returns with anon->d_lock held!
 */
static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)
{
	struct dentry *dparent;

	dentry_lock_for_move(anon, dentry);

	write_seqcount_begin(&dentry->d_seq);
	write_seqcount_begin_nested(&anon->d_seq, DENTRY_D_LOCK_NESTED);

	dparent = dentry->d_parent;

	switch_names(dentry, anon);
	swap(dentry->d_name.hash, anon->d_name.hash);

	dentry->d_parent = dentry;
	list_del_init(&dentry->d_u.d_child);
	anon->d_parent = dparent;
	if (likely(!d_unhashed(anon))) {
		hlist_bl_lock(&anon->d_sb->s_anon);
		__hlist_bl_del(&anon->d_hash);
		anon->d_hash.pprev = NULL;
		hlist_bl_unlock(&anon->d_sb->s_anon);
	}
	list_move(&anon->d_u.d_child, &dparent->d_subdirs);

	write_seqcount_end(&dentry->d_seq);
	write_seqcount_end(&anon->d_seq);

	dentry_unlock_parents_for_move(anon, dentry);
	spin_unlock(&dentry->d_lock);

	/* anon->d_lock still locked, returns locked */
}

/**
/**
 * d_splice_alias - splice a disconnected dentry into the tree if one exists
 * d_splice_alias - splice a disconnected dentry into the tree if one exists
 * @inode:  the inode which may have a disconnected dentry
 * @inode:  the inode which may have a disconnected dentry
@@ -2717,10 +2683,8 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
				return ERR_PTR(-EIO);
				return ERR_PTR(-EIO);
			}
			}
			write_seqlock(&rename_lock);
			write_seqlock(&rename_lock);
			__d_materialise_dentry(dentry, new);
			__d_move(new, dentry, false);
			write_sequnlock(&rename_lock);
			write_sequnlock(&rename_lock);
			_d_rehash(new);
			spin_unlock(&new->d_lock);
			spin_unlock(&inode->i_lock);
			spin_unlock(&inode->i_lock);
			security_d_instantiate(new, inode);
			security_d_instantiate(new, inode);
			iput(inode);
			iput(inode);
@@ -2780,7 +2744,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
			} else if (IS_ROOT(alias)) {
			} else if (IS_ROOT(alias)) {
				/* Is this an anonymous mountpoint that we
				/* Is this an anonymous mountpoint that we
				 * could splice into our tree? */
				 * could splice into our tree? */
				__d_materialise_dentry(dentry, alias);
				__d_move(alias, dentry, false);
				write_sequnlock(&rename_lock);
				write_sequnlock(&rename_lock);
				goto found;
				goto found;
			} else {
			} else {
@@ -2807,13 +2771,9 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
	actual = __d_instantiate_unique(dentry, inode);
	actual = __d_instantiate_unique(dentry, inode);
	if (!actual)
	if (!actual)
		actual = dentry;
		actual = dentry;
	else
		BUG_ON(!d_unhashed(actual));


	spin_lock(&actual->d_lock);
	d_rehash(actual);
found:
found:
	_d_rehash(actual);
	spin_unlock(&actual->d_lock);
	spin_unlock(&inode->i_lock);
	spin_unlock(&inode->i_lock);
out_nolock:
out_nolock:
	if (actual == dentry) {
	if (actual == dentry) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -158,7 +158,7 @@ static inline int dio_refill_pages(struct dio *dio, struct dio_submit *sdio)
{
{
	ssize_t ret;
	ssize_t ret;


	ret = iov_iter_get_pages(sdio->iter, dio->pages, DIO_PAGES,
	ret = iov_iter_get_pages(sdio->iter, dio->pages, LONG_MAX, DIO_PAGES,
				&sdio->from);
				&sdio->from);


	if (ret < 0 && sdio->blocks_available && (dio->rw & WRITE)) {
	if (ret < 0 && sdio->blocks_available && (dio->rw & WRITE)) {
+1 −0
Original line number Original line Diff line number Diff line
@@ -1305,6 +1305,7 @@ static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
		size_t start;
		size_t start;
		ssize_t ret = iov_iter_get_pages(ii,
		ssize_t ret = iov_iter_get_pages(ii,
					&req->pages[req->num_pages],
					&req->pages[req->num_pages],
					*nbytesp - nbytes,
					req->max_pages - req->num_pages,
					req->max_pages - req->num_pages,
					&start);
					&start);
		if (ret < 0)
		if (ret < 0)
+5 −1
Original line number Original line Diff line number Diff line
@@ -298,7 +298,10 @@ struct inode *ufs_new_inode(struct inode *dir, umode_t mode)
	ufsi->i_oeftflag = 0;
	ufsi->i_oeftflag = 0;
	ufsi->i_dir_start_lookup = 0;
	ufsi->i_dir_start_lookup = 0;
	memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1));
	memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1));
	insert_inode_hash(inode);
	if (insert_inode_locked(inode) < 0) {
		err = -EIO;
		goto failed;
	}
	mark_inode_dirty(inode);
	mark_inode_dirty(inode);


	if (uspi->fs_magic == UFS2_MAGIC) {
	if (uspi->fs_magic == UFS2_MAGIC) {
@@ -337,6 +340,7 @@ struct inode *ufs_new_inode(struct inode *dir, umode_t mode)
fail_remove_inode:
fail_remove_inode:
	unlock_ufs(sb);
	unlock_ufs(sb);
	clear_nlink(inode);
	clear_nlink(inode);
	unlock_new_inode(inode);
	iput(inode);
	iput(inode);
	UFSD("EXIT (FAILED): err %d\n", err);
	UFSD("EXIT (FAILED): err %d\n", err);
	return ERR_PTR(err);
	return ERR_PTR(err);
+4 −0
Original line number Original line Diff line number Diff line
@@ -38,10 +38,12 @@ static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode)
{
{
	int err = ufs_add_link(dentry, inode);
	int err = ufs_add_link(dentry, inode);
	if (!err) {
	if (!err) {
		unlock_new_inode(inode);
		d_instantiate(dentry, inode);
		d_instantiate(dentry, inode);
		return 0;
		return 0;
	}
	}
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	unlock_new_inode(inode);
	iput(inode);
	iput(inode);
	return err;
	return err;
}
}
@@ -155,6 +157,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,


out_fail:
out_fail:
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	unlock_new_inode(inode);
	iput(inode);
	iput(inode);
	goto out;
	goto out;
}
}
@@ -210,6 +213,7 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
out_fail:
out_fail:
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	inode_dec_link_count(inode);
	unlock_new_inode(inode);
	iput (inode);
	iput (inode);
	inode_dec_link_count(dir);
	inode_dec_link_count(dir);
	unlock_ufs(dir->i_sb);
	unlock_ufs(dir->i_sb);
Loading