Ejemplo n.º 1
0
static int do_unionfs_rename(struct inode *old_dir,
				   struct dentry *old_dentry,
				   struct inode *new_dir,
				   struct dentry *new_dentry)
{
	int err = 0;
	int bindex, bwh_old;
	int old_bstart, old_bend;
	int new_bstart, new_bend;
	int do_copyup = -1;
	struct dentry *parent_dentry;
	int local_err = 0;
	int eio = 0;
	int revert = 0;
	struct dentry *wh_old = NULL;

	old_bstart = dbstart(old_dentry);
	bwh_old = old_bstart;
	old_bend = dbend(old_dentry);
	parent_dentry = old_dentry->d_parent;

	new_bstart = dbstart(new_dentry);
	new_bend = dbend(new_dentry);

	/* Rename source to destination. */
	err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart,
			&wh_old);
	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
		do_copyup = old_bstart - 1;
	} else
		revert = 1;

	/* Unlink all instances of destination that exist to the left of
	 * bstart of source. On error, revert back, goto out.
	 */
	for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
		struct dentry *unlink_dentry;
		struct dentry *unlink_dir_dentry;

		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		unlink_dir_dentry = lock_parent(unlink_dentry);
		if (!(err = is_robranch_super(old_dir->i_sb, bindex)))
			err = vfs_unlink(unlink_dir_dentry->d_inode,
				       unlink_dentry);

		fsstack_copy_attr_times(new_dentry->d_parent->d_inode,
				     unlink_dir_dentry->d_inode);
		/* propagate number of hard-links */
		new_dentry->d_parent->d_inode->i_nlink =
		    unionfs_get_nlinks(new_dentry->d_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				dput(unlink_dentry);
				unionfs_set_lower_dentry_idx(new_dentry, bindex, NULL);
			}
		} else if (IS_COPYUP_ERR(err)) {
			do_copyup = bindex - 1;
		} else if (revert) {
			dput(wh_old);
			goto revert;
		}
	}

	if (do_copyup != -1) {
		for (bindex = do_copyup; bindex >= 0; bindex--) {
			/* copyup the file into some left directory, so that
			 * you can rename it
			 */
			err = copyup_dentry(old_dentry->d_parent->d_inode,
					    old_dentry, old_bstart, bindex, NULL,
					    old_dentry->d_inode->i_size);
			if (!err) {
				dput(wh_old);
				bwh_old = bindex;
				err = do_rename(old_dir, old_dentry, new_dir,
						new_dentry, bindex, &wh_old);
				break;
			}
		}
	}

	/* make it opaque */
	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
		err = make_dir_opaque(old_dentry, dbstart(old_dentry));
		if (err)
			goto revert;
	}

	/* Create whiteout for source, only if:
	 * (1) There is more than one underlying instance of source.
	 * (2) We did a copy_up
	 */
	if ((old_bstart != old_bend) || (do_copyup != -1)) {
		struct dentry *hidden_parent;
		BUG_ON(!wh_old || wh_old->d_inode || bwh_old < 0);
		hidden_parent = lock_parent(wh_old);
		local_err = vfs_create(hidden_parent->d_inode, wh_old, S_IRUGO,
				       NULL);
		unlock_dir(hidden_parent);
		if (!local_err)
			set_dbopaque(old_dentry, bwh_old);
		else {
			/* We can't fix anything now, so we cop-out and use -EIO. */
			printk(KERN_ERR "We can't create a whiteout for the "
					"source in rename!\n");
			err = -EIO;
		}
	}

out:
	dput(wh_old);
	return err;

revert:
	/* Do revert here. */
	local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart);
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename: the new refresh "
				"failed.\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart);
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename: the old refresh "
				"failed.\n");
		eio = -EIO;
		goto revert_out;
	}

	if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
	    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
		printk(KERN_WARNING "Revert failed in rename: the object "
				"disappeared from under us!\n");
		eio = -EIO;
		goto revert_out;
	}

	if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
	    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
		printk(KERN_WARNING "Revert failed in rename: the object was "
				"created underneath us!\n");
		eio = -EIO;
		goto revert_out;
	}

	local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart,
					NULL);

	/* If we can't fix it, then we cop-out with -EIO. */
	if (local_err) {
		printk(KERN_WARNING "Revert failed in rename!\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
	if (local_err)
		eio = -EIO;
	local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
	if (local_err)
		eio = -EIO;

revert_out:
	if (eio)
		err = eio;
	return err;
}
Ejemplo n.º 2
0
static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct dentry *lower_parent_dentry = NULL;
	struct dentry *parent;
	int bindex = 0, bstart;
	char *name = NULL;
	int valid;

	unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
	parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
	unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);

	valid = __unionfs_d_revalidate(dentry, parent, false, 0);
	if (unlikely(!valid)) {
		err = -ESTALE;	/* same as what real_lookup does */
		goto out;
	}

	bstart = dbstart(dentry);

	lower_dentry = unionfs_lower_dentry(dentry);

	/* check for a whiteout in new dentry branch, and delete it */
	err = check_unlink_whiteout(dentry, lower_dentry, bstart);
	if (err > 0)	       /* whiteout found and removed successfully */
		err = 0;
	if (err) {
		/* exit if the error returned was NOT -EROFS */
		if (!IS_COPYUP_ERR(err))
			goto out;
		bstart--;
	}

	/* check if copyup's needed, and mkdir */
	for (bindex = bstart; bindex >= 0; bindex--) {
		int i;
		int bend = dbend(dentry);

		if (is_robranch_super(dentry->d_sb, bindex))
			continue;

		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry) {
			lower_dentry = create_parents(dir, dentry,
						      dentry->d_name.name,
						      bindex);
			if (!lower_dentry || IS_ERR(lower_dentry)) {
				printk(KERN_ERR "unionfs: lower dentry "
				       " NULL for bindex = %d\n", bindex);
				continue;
			}
		}

		lower_parent_dentry = lock_parent(lower_dentry);

		if (IS_ERR(lower_parent_dentry)) {
			err = PTR_ERR(lower_parent_dentry);
			goto out;
		}

		err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry,
				mode);

		unlock_dir(lower_parent_dentry);

		/* did the mkdir succeed? */
		if (err)
			break;

		for (i = bindex + 1; i <= bend; i++) {
			/* XXX: use path_put_lowers? */
			if (unionfs_lower_dentry_idx(dentry, i)) {
				dput(unionfs_lower_dentry_idx(dentry, i));
				unionfs_set_lower_dentry_idx(dentry, i, NULL);
			}
		}
		dbend(dentry) = bindex;

		/*
		 * Only INTERPOSE_LOOKUP can return a value other than 0 on
		 * err.
		 */
		err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
		if (!err) {
			unionfs_copy_attr_times(dir);
			fsstack_copy_inode_size(dir,
						lower_parent_dentry->d_inode);

			/* update number of links on parent directory */
			set_nlink(dir, unionfs_get_nlinks(dir));
		}

		err = make_dir_opaque(dentry, dbstart(dentry));
		if (err) {
			printk(KERN_ERR "unionfs: mkdir: error creating "
			       ".wh.__dir_opaque: %d\n", err);
			goto out;
		}

		/* we are done! */
		break;
	}

out:
	if (!dentry->d_inode)
		d_drop(dentry);

	kfree(name);

	if (!err) {
		unionfs_copy_attr_times(dentry->d_inode);
		unionfs_postcopyup_setmnt(dentry);
	}
	unionfs_check_inode(dir);
	unionfs_check_dentry(dentry);
	unionfs_unlock_dentry(dentry);
	unionfs_unlock_parent(dentry, parent);
	unionfs_read_unlock(dentry->d_sb);

	return err;
}
Ejemplo n.º 3
0
/*
 * Main rename code.  This is sufficiently complex, that it's documented in
 * Documentation/filesystems/unionfs/rename.txt.  This routine calls
 * __unionfs_rename() above to perform some of the work.
 */
static int do_unionfs_rename(struct inode *old_dir,
			     struct dentry *old_dentry,
			     struct dentry *old_parent,
			     struct inode *new_dir,
			     struct dentry *new_dentry,
			     struct dentry *new_parent)
{
	int err = 0;
	int bindex, bwh_old;
	int old_bstart, old_bend;
	int new_bstart, new_bend;
	int do_copyup = -1;
	int local_err = 0;
	int eio = 0;
	int revert = 0;

	old_bstart = dbstart(old_dentry);
	bwh_old = old_bstart;
	old_bend = dbend(old_dentry);

	new_bstart = dbstart(new_dentry);
	new_bend = dbend(new_dentry);

	/* Rename source to destination. */
	err = __unionfs_rename(old_dir, old_dentry, old_parent,
			       new_dir, new_dentry, new_parent,
			       old_bstart);
	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
		do_copyup = old_bstart - 1;
	} else {
		revert = 1;
	}

	/*
	 * Unlink all instances of destination that exist to the left of
	 * bstart of source. On error, revert back, goto out.
	 */
	for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
		struct dentry *unlink_dentry;
		struct dentry *unlink_dir_dentry;

		BUG_ON(bindex < 0);
		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		unlink_dir_dentry = lock_parent(unlink_dentry);
		err = is_robranch_super(old_dir->i_sb, bindex);
		if (!err)
			err = vfs_unlink(unlink_dir_dentry->d_inode,
					 unlink_dentry);

		fsstack_copy_attr_times(new_parent->d_inode,
					unlink_dir_dentry->d_inode);
		/* propagate number of hard-links */
		new_parent->d_inode->i_nlink =
			unionfs_get_nlinks(new_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				dput(unlink_dentry);
				unionfs_set_lower_dentry_idx(new_dentry,
							     bindex, NULL);
			}
		} else if (IS_COPYUP_ERR(err)) {
			do_copyup = bindex - 1;
		} else if (revert) {
			goto revert;
		}
	}

	if (do_copyup != -1) {
		for (bindex = do_copyup; bindex >= 0; bindex--) {
			/*
			 * copyup the file into some left directory, so that
			 * you can rename it
			 */
			err = copyup_dentry(old_parent->d_inode,
					    old_dentry, old_bstart, bindex,
					    old_dentry->d_name.name,
					    old_dentry->d_name.len, NULL,
					    i_size_read(old_dentry->d_inode));
			/* if copyup failed, try next branch to the left */
			if (err)
				continue;
			bwh_old = bindex;
			err = __unionfs_rename(old_dir, old_dentry, old_parent,
					       new_dir, new_dentry, new_parent,
					       bindex);
			break;
		}
	}

	/* make it opaque */
	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
		err = make_dir_opaque(old_dentry, dbstart(old_dentry));
		if (err)
			goto revert;
	}

	/*
	 * Create whiteout for source, only if:
	 * (1) There is more than one underlying instance of source.
	 * (2) We did a copy_up
	 */
	if ((old_bstart != old_bend) || (do_copyup != -1)) {
		if (bwh_old < 0) {
			printk(KERN_ERR "unionfs: rename error (bwh_old=%d)\n",
			       bwh_old);
			err = -EIO;
			goto out;
		}
		err = create_whiteout(old_dentry, bwh_old);
		if (err) {
			/* can't fix anything now, so we exit with -EIO */
			printk(KERN_ERR "unionfs: can't create a whiteout for "
			       "%s in rename!\n", old_dentry->d_name.name);
			err = -EIO;
		}
	}

out:
	return err;

revert:
	/* Do revert here. */
	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the new refresh failed\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the old refresh failed\n");
		eio = -EIO;
		goto revert_out;
	}

	if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
	    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object disappeared from under us!\n");
		eio = -EIO;
		goto revert_out;
	}

	if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
	    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object was created underneath us!\n");
		eio = -EIO;
		goto revert_out;
	}

	local_err = __unionfs_rename(new_dir, new_dentry, new_parent,
				     old_dir, old_dentry, old_parent,
				     old_bstart);

	/* If we can't fix it, then we cop-out with -EIO. */
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename!\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 bindex);
	if (local_err)
		eio = -EIO;
	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 bindex);
	if (local_err)
		eio = -EIO;

revert_out:
	if (eio)
		err = eio;
	return err;
}