/*
 * returns: -ERRNO if error (returned to user)
 *          0: tell VFS to invalidate dentry
 *          1: dentry is valid
 */
static int esdfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	struct path lower_path;
	struct path lower_parent_path;
	struct dentry *parent_dentry = NULL;
	struct dentry *lower_dentry = NULL;
	struct dentry *lower_parent_dentry = NULL;
	int err = 1;

	if (nd && (nd->flags & LOOKUP_RCU))
		return -ECHILD;

	/* short-circuit if it's root */
	spin_lock(&dentry->d_lock);
	if (IS_ROOT(dentry)) {
		spin_unlock(&dentry->d_lock);
		return 1;
	}
	spin_unlock(&dentry->d_lock);

	esdfs_get_lower_path(dentry, &lower_path);
	lower_dentry = lower_path.dentry;
	esdfs_get_lower_parent(dentry, lower_dentry, &lower_parent_dentry);

	parent_dentry = dget_parent(dentry);
	esdfs_get_lower_path(parent_dentry, &lower_parent_path);

	if (lower_parent_path.dentry != lower_parent_dentry)
		goto drop;

	/* can't do strcmp if lower is hashed */
	spin_lock(&lower_dentry->d_lock);
	if (d_unhashed(lower_dentry)) {
		spin_unlock(&lower_dentry->d_lock);
		goto drop;
	}

	spin_lock(&dentry->d_lock);

	if (lower_dentry->d_name.len != dentry->d_name.len ||
	    strncasecmp(lower_dentry->d_name.name,
			dentry->d_name.name,
			dentry->d_name.len) != 0) {
		err = 0;
		__d_drop(dentry);	/* already holding spin lock */
	}

	spin_unlock(&dentry->d_lock);
	spin_unlock(&lower_dentry->d_lock);

	esdfs_revalidate_perms(dentry);

	goto out;

drop:
	d_drop(dentry);
	err = 0;
out:
	esdfs_put_lower_path(parent_dentry, &lower_parent_path);
	dput(parent_dentry);
	esdfs_put_lower_parent(dentry, &lower_parent_dentry);
	esdfs_put_lower_path(dentry, &lower_path);
	return err;
}
Exemple #2
0
/*
 * The locking rules in esdfs_rename are complex.  We could use a simpler
 * superblock-level name-space lock for renames and copy-ups.
 */
static int esdfs_rename(struct inode *old_dir, struct dentry *old_dentry,
			 struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *lower_old_dentry = NULL;
	struct dentry *lower_new_dentry = NULL;
	struct dentry *lower_old_dir_dentry = NULL;
	struct dentry *lower_new_dir_dentry = NULL;
	struct dentry *trap = NULL;
	struct path lower_old_path, lower_new_path;
	int mask;
	const struct cred *creds =
			esdfs_override_creds(ESDFS_SB(old_dir->i_sb), &mask);
	if (!creds)
		return -ENOMEM;

	/* Never rename to or from a pseudo hard link target. */
	if (ESDFS_DENTRY_HAS_STUB(old_dentry))
		esdfs_get_lower_stub_path(old_dentry, &lower_old_path);
	else
		esdfs_get_lower_path(old_dentry, &lower_old_path);
	if (ESDFS_DENTRY_HAS_STUB(new_dentry))
		esdfs_get_lower_stub_path(new_dentry, &lower_new_path);
	else
		esdfs_get_lower_path(new_dentry, &lower_new_path);
	lower_old_dentry = lower_old_path.dentry;
	lower_new_dentry = lower_new_path.dentry;
	esdfs_get_lower_parent(old_dentry, lower_old_dentry,
			       &lower_old_dir_dentry);
	esdfs_get_lower_parent(new_dentry, lower_new_dentry,
			       &lower_new_dir_dentry);

	trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	/* source should not be ancestor of target */
	if (trap == lower_old_dentry) {
		err = -EINVAL;
		goto out;
	}
	/* target should not be ancestor of source */
	if (trap == lower_new_dentry) {
		err = -ENOTEMPTY;
		goto out;
	}

	err = mnt_want_write(lower_old_path.mnt);
	if (err)
		goto out;
	err = mnt_want_write(lower_new_path.mnt);
	if (err)
		goto out_drop_old_write;

	err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
			 lower_new_dir_dentry->d_inode, lower_new_dentry);
	if (err)
		goto out_err;

	esdfs_copy_attr(new_dir, lower_new_dir_dentry->d_inode);
	fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode);
	if (new_dir != old_dir) {
		esdfs_copy_attr(old_dir,
				      lower_old_dir_dentry->d_inode);
		fsstack_copy_inode_size(old_dir,
					lower_old_dir_dentry->d_inode);
	}

	/* Drop any old links */
	if (ESDFS_DENTRY_HAS_STUB(old_dentry))
		d_drop(old_dentry);
	if (ESDFS_DENTRY_HAS_STUB(new_dentry))
		d_drop(new_dentry);
out_err:
	mnt_drop_write(lower_new_path.mnt);
out_drop_old_write:
	mnt_drop_write(lower_old_path.mnt);
out:
	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	esdfs_put_lower_parent(old_dentry, &lower_old_dir_dentry);
	esdfs_put_lower_parent(new_dentry, &lower_new_dir_dentry);
	esdfs_put_lower_path(old_dentry, &lower_old_path);
	esdfs_put_lower_path(new_dentry, &lower_new_path);
	esdfs_revert_creds(creds, &mask);
	return err;
}