示例#1
0
/*
 * Copy up a dentry to a file of specified name.
 *
 * @dir: used to pull the ->i_sb to access other branches
 * @dentry: the non-negative dentry whose lower_inode we should copy
 * @bstart: the branch of the lower_inode to copy from
 * @new_bindex: the branch to create the new file in
 * @name: the name of the file to create
 * @namelen: length of @name
 * @copyup_file: the "struct file" to return (optional)
 * @len: how many bytes to copy-up?
 */
int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart,
		  int new_bindex, const char *name, int namelen,
		  struct file **copyup_file, loff_t len)
{
	struct dentry *new_lower_dentry;
	struct dentry *old_lower_dentry = NULL;
	struct super_block *sb;
	int err = 0;
	int old_bindex;
	int old_bstart;
	int old_bend;
	struct dentry *new_lower_parent_dentry = NULL;
	mm_segment_t oldfs;
	char *symbuf = NULL;

	verify_locked(dentry);

	old_bindex = bstart;
	old_bstart = dbstart(dentry);
	old_bend = dbend(dentry);

	BUG_ON(new_bindex < 0);
	BUG_ON(new_bindex >= old_bindex);

	sb = dir->i_sb;

	err = is_robranch_super(sb, new_bindex);
	if (err)
		goto out;

	/* Create the directory structure above this dentry. */
	new_lower_dentry = create_parents(dir, dentry, name, new_bindex);
	if (IS_ERR(new_lower_dentry)) {
		err = PTR_ERR(new_lower_dentry);
		goto out;
	}

	old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex);
	/* we conditionally dput this old_lower_dentry at end of function */
	dget(old_lower_dentry);

	/* For symlinks, we must read the link before we lock the directory. */
	if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) {

		symbuf = kmalloc(PATH_MAX, GFP_KERNEL);
		if (unlikely(!symbuf)) {
			__clear(dentry, old_lower_dentry,
				old_bstart, old_bend,
				new_lower_dentry, new_bindex);
			err = -ENOMEM;
			goto out_free;
		}

		oldfs = get_fs();
		set_fs(KERNEL_DS);
		err = old_lower_dentry->d_inode->i_op->readlink(
			old_lower_dentry,
			(char __user *)symbuf,
			PATH_MAX);
		set_fs(oldfs);
		if (err < 0) {
			__clear(dentry, old_lower_dentry,
				old_bstart, old_bend,
				new_lower_dentry, new_bindex);
			goto out_free;
		}
		symbuf[err] = '\0';
	}

	/* Now we lock the parent, and create the object in the new branch. */
	new_lower_parent_dentry = lock_parent(new_lower_dentry);

	/* create the new inode */
	err = __copyup_ndentry(old_lower_dentry, new_lower_dentry,
			       new_lower_parent_dentry, symbuf);

	if (err) {
		__clear(dentry, old_lower_dentry,
			old_bstart, old_bend,
			new_lower_dentry, new_bindex);
		goto out_unlock;
	}

	/* We actually copyup the file here. */
	if (S_ISREG(old_lower_dentry->d_inode->i_mode))
		err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex,
					old_lower_dentry, old_bindex,
					copyup_file, len);
	if (err)
		goto out_unlink;

	/* Set permissions. */
	err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry);
	if (err)
		goto out_unlink;

#ifdef CONFIG_UNION_FS_XATTR
	/* Selinux uses extended attributes for permissions. */
	err = copyup_xattrs(old_lower_dentry, new_lower_dentry);
	if (err)
		goto out_unlink;
#endif /* CONFIG_UNION_FS_XATTR */

	/* do not allow files getting deleted to be re-interposed */
	if (!d_deleted(dentry))
		unionfs_reinterpose(dentry);

	goto out_unlock;

out_unlink:
	/*
	 * copyup failed, because we possibly ran out of space or
	 * quota, or something else happened so let's unlink; we don't
	 * really care about the return value of vfs_unlink
	 */
	vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry);

	if (copyup_file) {
		/* need to close the file */

		fput(*copyup_file);
		branchput(sb, new_bindex);
	}

	/*
	 * TODO: should we reset the error to something like -EIO?
	 *
	 * If we don't reset, the user may get some nonsensical errors, but
	 * on the other hand, if we reset to EIO, we guarantee that the user
	 * will get a "confusing" error message.
	 */

out_unlock:
	unlock_dir(new_lower_parent_dentry);

out_free:
	/*
	 * If old_lower_dentry was not a file, then we need to dput it.  If
	 * it was a file, then it was already dput indirectly by other
	 * functions we call above which operate on regular files.
	 */
	if (old_lower_dentry && old_lower_dentry->d_inode &&
	    !S_ISREG(old_lower_dentry->d_inode->i_mode))
		dput(old_lower_dentry);
	kfree(symbuf);

	if (err) {
		/*
		 * if directory creation succeeded, but inode copyup failed,
		 * then purge new dentries.
		 */
		if (dbstart(dentry) < old_bstart &&
		    ibstart(dentry->d_inode) > dbstart(dentry))
			__clear(dentry, NULL, old_bstart, old_bend,
				unionfs_lower_dentry(dentry), dbstart(dentry));
		goto out;
	}
	if (!S_ISDIR(dentry->d_inode->i_mode)) {
		unionfs_postcopyup_release(dentry);
		if (!unionfs_lower_inode(dentry->d_inode)) {
			/*
			 * If we got here, then we copied up to an
			 * unlinked-open file, whose name is .unionfsXXXXX.
			 */
			struct inode *inode = new_lower_dentry->d_inode;
			atomic_inc(&inode->i_count);
			unionfs_set_lower_inode_idx(dentry->d_inode,
						    ibstart(dentry->d_inode),
						    inode);
		}
	}
	unionfs_postcopyup_setmnt(dentry);
	/* sync inode times from copied-up inode to our inode */
	unionfs_copy_attr_times(dentry->d_inode);
	unionfs_check_inode(dir);
	unionfs_check_dentry(dentry);
out:
	return err;
}
示例#2
0
/* copy up a dentry to a file of specified name */
static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
			       int bstart, int new_bindex, const char *name,
			       int namelen, struct file **copyup_file,
			       loff_t len)
{
	struct dentry *new_hidden_dentry;
	struct dentry *old_hidden_dentry = NULL;
	struct super_block *sb;
	int err = 0;
	int old_bindex;
	int old_bstart;
	int old_bend;
	struct dentry *new_hidden_parent_dentry = NULL;
	mm_segment_t oldfs;
	char *symbuf = NULL;

	verify_locked(dentry);

	old_bindex = bstart;
	old_bstart = dbstart(dentry);
	old_bend = dbend(dentry);

	BUG_ON(new_bindex < 0);
	BUG_ON(new_bindex >= old_bindex);

	sb = dir->i_sb;

	unionfs_read_lock(sb);

	if ((err = is_robranch_super(sb, new_bindex))) {
		dput(old_hidden_dentry);
		goto out;
	}

	/* Create the directory structure above this dentry. */
	new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex);
	if (IS_ERR(new_hidden_dentry)) {
		dput(old_hidden_dentry);
		err = PTR_ERR(new_hidden_dentry);
		goto out;
	}

	old_hidden_dentry = unionfs_lower_dentry_idx(dentry, old_bindex);
	dget(old_hidden_dentry);

	/* For symlinks, we must read the link before we lock the directory. */
	if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {

		symbuf = kmalloc(PATH_MAX, GFP_KERNEL);
		if (!symbuf) {
			__clear(dentry, old_hidden_dentry,
				old_bstart, old_bend,
				new_hidden_dentry, new_bindex);
			err = -ENOMEM;
			goto out_free;
		}

		oldfs = get_fs();
		set_fs(KERNEL_DS);
		err = old_hidden_dentry->d_inode->i_op->readlink(
					old_hidden_dentry,
					(char __user *)symbuf,
					PATH_MAX);
		set_fs(oldfs);
		if (err) {
			__clear(dentry, old_hidden_dentry,
				old_bstart, old_bend,
				new_hidden_dentry, new_bindex);
			goto out_free;
		}
		symbuf[err] = '\0';
	}

	/* Now we lock the parent, and create the object in the new branch. */
	new_hidden_parent_dentry = lock_parent(new_hidden_dentry);

	/* create the new inode */
	err = __copyup_ndentry(old_hidden_dentry, new_hidden_dentry,
			       new_hidden_parent_dentry, symbuf);

	if (err) {
		__clear(dentry, old_hidden_dentry,
			old_bstart, old_bend,
			new_hidden_dentry, new_bindex);
		goto out_unlock;
	}

	/* We actually copyup the file here. */
	if (S_ISREG(old_hidden_dentry->d_inode->i_mode))
		err = __copyup_reg_data(dentry, new_hidden_dentry, new_bindex,
				old_hidden_dentry, old_bindex, copyup_file, len);
	if (err)
		goto out_unlink;

	/* Set permissions. */
	if ((err = copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry)))
		goto out_unlink;

#ifdef CONFIG_UNION_FS_XATTR
	/* Selinux uses extended attributes for permissions. */
	if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry)))
		goto out_unlink;
#endif

	/* do not allow files getting deleted to be reinterposed */
	if (!d_deleted(dentry))
		unionfs_reinterpose(dentry);

	goto out_unlock;
	/****/

out_unlink:
	/* copyup failed, because we possibly ran out of space or
	 * quota, or something else happened so let's unlink; we don't
	 * really care about the return value of vfs_unlink
	 */
	vfs_unlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry);

	if (copyup_file) {
		/* need to close the file */

		fput(*copyup_file);
		branchput(sb, new_bindex);
	}

	/*
	 * TODO: should we reset the error to something like -EIO?
	 *
	 * If we don't reset, the user may get some non-sensical errors, but
	 * on the other hand, if we reset to EIO, we guarantee that the user
	 * will get a "confusing" error message.
	 */

out_unlock:
	unlock_dir(new_hidden_parent_dentry);

out_free:
	kfree(symbuf);

out:
	unionfs_read_unlock(sb);

	return err;
}