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

Commit b70a80e7 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Al Viro
Browse files

vfs: introduce d_instantiate_no_diralias()



...which just returns -EBUSY if a directory alias would be created.

This is to be used by fuse mkdir to make sure that a buggy or malicious
userspace filesystem doesn't do anything nasty.  Previously fuse used a
private mutex for this purpose, which can now go away.

Signed-off-by: default avatarMiklos Szeredi <mszeredi@suse.cz>
parent 94e92a6e
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -1801,6 +1801,33 @@ struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)

EXPORT_SYMBOL(d_instantiate_unique);

/**
 * d_instantiate_no_diralias - instantiate a non-aliased dentry
 * @entry: dentry to complete
 * @inode: inode to attach to this dentry
 *
 * Fill in inode information in the entry.  If a directory alias is found, then
 * return an error (and drop inode).  Together with d_materialise_unique() this
 * guarantees that a directory inode may never have more than one alias.
 */
int d_instantiate_no_diralias(struct dentry *entry, struct inode *inode)
{
	BUG_ON(!hlist_unhashed(&entry->d_alias));

	spin_lock(&inode->i_lock);
	if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) {
		spin_unlock(&inode->i_lock);
		iput(inode);
		return -EBUSY;
	}
	__d_instantiate(entry, inode);
	spin_unlock(&inode->i_lock);
	security_d_instantiate(entry, inode);

	return 0;
}
EXPORT_SYMBOL(d_instantiate_no_diralias);

struct dentry *d_make_root(struct inode *root_inode)
{
	struct dentry *res = NULL;
+5 −35
Original line number Diff line number Diff line
@@ -342,24 +342,6 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
	return err;
}

static struct dentry *fuse_materialise_dentry(struct dentry *dentry,
					      struct inode *inode)
{
	struct dentry *newent;

	if (inode && S_ISDIR(inode->i_mode)) {
		struct fuse_conn *fc = get_fuse_conn(inode);

		mutex_lock(&fc->inst_mutex);
		newent = d_materialise_unique(dentry, inode);
		mutex_unlock(&fc->inst_mutex);
	} else {
		newent = d_materialise_unique(dentry, inode);
	}

	return newent;
}

static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
				  unsigned int flags)
{
@@ -382,7 +364,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
	if (inode && get_node_id(inode) == FUSE_ROOT_ID)
		goto out_iput;

	newent = fuse_materialise_dentry(entry, inode);
	newent = d_materialise_unique(entry, inode);
	err = PTR_ERR(newent);
	if (IS_ERR(newent))
		goto out_err;
@@ -601,21 +583,9 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
	}
	kfree(forget);

	if (S_ISDIR(inode->i_mode)) {
		struct dentry *alias;
		mutex_lock(&fc->inst_mutex);
		alias = d_find_alias(inode);
		if (alias) {
			/* New directory must have moved since mkdir */
			mutex_unlock(&fc->inst_mutex);
			dput(alias);
			iput(inode);
			return -EBUSY;
		}
		d_instantiate(entry, inode);
		mutex_unlock(&fc->inst_mutex);
	} else
		d_instantiate(entry, inode);
	err = d_instantiate_no_diralias(entry, inode);
	if (err)
		return err;

	fuse_change_entry_timeout(entry, &outarg);
	fuse_invalidate_attr(dir);
@@ -1284,7 +1254,7 @@ static int fuse_direntplus_link(struct file *file,
	if (!inode)
		goto out;

	alias = fuse_materialise_dentry(dentry, inode);
	alias = d_materialise_unique(dentry, inode);
	err = PTR_ERR(alias);
	if (IS_ERR(alias))
		goto out;
+0 −3
Original line number Diff line number Diff line
@@ -374,9 +374,6 @@ struct fuse_conn {
	/** Lock protecting accessess to  members of this structure */
	spinlock_t lock;

	/** Mutex protecting against directory alias creation */
	struct mutex inst_mutex;

	/** Refcount */
	atomic_t count;

+0 −2
Original line number Diff line number Diff line
@@ -565,7 +565,6 @@ void fuse_conn_init(struct fuse_conn *fc)
{
	memset(fc, 0, sizeof(*fc));
	spin_lock_init(&fc->lock);
	mutex_init(&fc->inst_mutex);
	init_rwsem(&fc->killsb);
	atomic_set(&fc->count, 1);
	init_waitqueue_head(&fc->waitq);
@@ -596,7 +595,6 @@ void fuse_conn_put(struct fuse_conn *fc)
	if (atomic_dec_and_test(&fc->count)) {
		if (fc->destroy_req)
			fuse_request_free(fc->destroy_req);
		mutex_destroy(&fc->inst_mutex);
		fc->release(fc);
	}
}
+1 −0
Original line number Diff line number Diff line
@@ -224,6 +224,7 @@ static inline int dname_external(const struct dentry *dentry)
extern void d_instantiate(struct dentry *, struct inode *);
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
extern struct dentry * d_materialise_unique(struct dentry *, struct inode *);
extern int d_instantiate_no_diralias(struct dentry *, struct inode *);
extern void __d_drop(struct dentry *dentry);
extern void d_drop(struct dentry *dentry);
extern void d_delete(struct dentry *);