示例#1
0
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
{
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry;
	int bindex;
	int err = 0;

	print_entry_location();

	if ((err = unionfs_partial_lookup(dentry)))
		goto out;

	bindex = dbstart(dentry);

	hidden_dentry = dtohd_index(dentry, bindex);
	if (!hidden_dentry)
		goto out;

	hidden_dir_dentry = lock_parent(hidden_dentry);

	/* avoid destroying the hidden inode if the file is in use */
	DGET(hidden_dentry);
	if (!(err = is_robranch_super(dentry->d_sb, bindex)))
		err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
	DPUT(hidden_dentry);
	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
	unlock_dir(hidden_dir_dentry);

	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
	}

	if (err) {
		if (dbstart(dentry) == 0) {
			goto out;
		}
		err = create_whiteout(dentry, dbstart(dentry) - 1);
	} else if (dbopaque(dentry) != -1) {
		/* There is a hidden lower-priority file with the same name. */
		err = create_whiteout(dentry, dbopaque(dentry));
	} else {
		err = create_whiteout(dentry, dbstart(dentry));
	}
      out:
	if (!err)
		dentry->d_inode->i_nlink--;
	/* We don't want to leave negative leftover dentries for revalidate. */
	if (!err && (dbopaque(dentry) != -1))
		update_bstart(dentry);

	print_exit_status(err);
	return err;

}
示例#2
0
/* this unionfs_write() does not modify data pages! */
ssize_t unionfs_write(struct file * file, const char *buf, size_t count,
		      loff_t * ppos)
{
	int err = -EINVAL;
	struct file *hidden_file = NULL;
	struct inode *inode;
	struct inode *hidden_inode;
	loff_t pos = *ppos;
	int bstart, bend;

	print_entry_location();

	if ((err = unionfs_file_revalidate(file, 1)))
		goto out;

	inode = file->f_dentry->d_inode;

	bstart = fbstart(file);
	bend = fbend(file);

	ASSERT(bstart != -1);
	PASSERT(ftopd(file));
	PASSERT(ftohf(file));

	hidden_file = ftohf(file);
	hidden_inode = hidden_file->f_dentry->d_inode;

	if (!hidden_file->f_op || !hidden_file->f_op->write)
		goto out;

	/* adjust for append -- seek to the end of the file */
	if (file->f_flags & O_APPEND)
		pos = inode->i_size;

	err = hidden_file->f_op->write(hidden_file, buf, count, &pos);

	/*
	 * copy ctime and mtime from lower layer attributes
	 * atime is unchanged for both layers
	 */
	if (err >= 0)
		fist_copy_attr_times(inode, hidden_inode);

	*ppos = pos;

	/* update this inode's size */
	if (pos > inode->i_size)
		inode->i_size = pos;

      out:
	print_exit_status(err);
	return err;
}
示例#3
0
/*
 * Unlink all destinations (if they exist) to the left of the left-most
 * source
 */
static int __rename_all_unlink(struct inode *old_dir, struct dentry *old_dentry,
			       struct inode *new_dir, struct dentry *new_dentry,
			       struct rename_info *info)
{
	int bindex;

	struct dentry *unlink_dentry;
	struct dentry *unlink_dir_dentry;

	int err = 0;

	print_entry_location();

	for (bindex = info->old_bstart - 1; bindex > info->new_bstart; bindex--) {
		unlink_dentry = dtohd_index(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		/* lock, unlink if possible, copyup times, unlock */
		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);

		fist_copy_attr_times(new_dentry->d_parent->d_inode,
				     unlink_dir_dentry->d_inode);
		new_dentry->d_parent->d_inode->i_nlink =
		    get_nlinks(new_dentry->d_parent->d_inode);

		unlock_dir(unlink_dir_dentry);

		if (!err) {
			if (bindex != info->new_bstart) {
				DPUT(unlink_dentry);
				set_dtohd_index(new_dentry, bindex, NULL);
			}
		} else if (IS_COPYUP_ERR(err)) {
			if (info->isdir) {
				err = -EXDEV;
				break;
			}
			info->do_copyup = bindex - 1;

			err = 0;	/* reset error */
		} else
			break;	/* err is set by is_ro_branch_super or vfs_unlink */
	}

	print_exit_status(err);
	return err;
}
示例#4
0
STATIC int
base0fs_unlink(inode_t *dir, struct dentry *dentry)
{
        int err = 0;
        inode_t *lower_dir;
        struct dentry *lower_dentry;
        struct dentry *lower_dir_dentry;

        print_entry_location();

        lower_dir = INODE_TO_LOWER(dir); /* CPW: Moved below print_entry_location */
        lower_dentry = base0fs_lower_dentry(dentry);

        BUG_ON(!lower_dentry);
        fist_checkinode(dir, "base0fs_unlink-dir");

        dget(dentry);
        lower_dir_dentry = base0fs_lock_parent(lower_dentry);


        /* avoid destroying the lower inode if the file is in use */
        dget(lower_dentry);
        err = VFS_UNLINK(lower_dir, lower_dentry);
        dput(lower_dentry);

        if (!err)			  /* vfs_unlink does that */
                d_delete(lower_dentry);

out_lock:
        fist_copy_attr_times(dir, lower_dir);
        /* propagate number of hard-links */
        dentry->d_inode->i_nlink = INODE_TO_LOWER(dentry->d_inode)->i_nlink;
    	fist_copy_attr_ctime(dentry->d_inode, dir);

        unlock_dir(lower_dir_dentry);

        /*
         * call d_drop so the system "forgets" about us
         */
        if (!err) {
                d_drop(dentry);
        }

        dput(dentry);

        fist_checkinode(dir, "post base0fs_unlink-dir");
        print_exit_status(err);
        return err;
}
示例#5
0
static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry,
			       struct unionfs_dir_state *namelist)
{
	int err;
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry = NULL;

	print_entry_location();
	fist_print_dentry("IN unionfs_rmdir_first: ", dentry);

	/* Here we need to remove whiteout entries. */
	err = delete_whiteouts(dentry, dbstart(dentry), namelist);
	if (err) {
		goto out;
	}

	hidden_dentry = dtohd(dentry);
	PASSERT(hidden_dentry);

	hidden_dir_dentry = lock_parent(hidden_dentry);

	/* avoid destroying the hidden inode if the file is in use */
	DGET(hidden_dentry);
	if (!(err = is_robranch(dentry))) {
		err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry);
	}
	DPUT(hidden_dentry);

	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
	/* propagate number of hard-links */
	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);

      out:
	if (hidden_dir_dentry) {
		unlock_dir(hidden_dir_dentry);
	}
	fist_print_dentry("OUT unionfs_rmdir_first: ", dentry);
	print_exit_status(err);
	return err;
}
示例#6
0
STATIC int
base0fs_rmdir(inode_t *dir, struct dentry *dentry)
{
        int err = 0;
        struct dentry *lower_dentry;
        struct dentry *lower_dir_dentry;

        print_entry_location();
        lower_dentry = base0fs_lower_dentry(dentry);	/* CPW: Moved below print_entry_location */
        fist_checkinode(dir, "base0fs_rmdir-dir");

        dget(dentry);
        lower_dir_dentry = base0fs_lock_parent(lower_dentry);

        /* avoid destroying the lower inode if the file is in use */
        dget(lower_dentry);
        err = VFS_RMDIR(lower_dir_dentry->d_inode, lower_dentry);
        dput(lower_dentry);

        if (!err)			  /* vfs_rmdir does that */
                d_delete(lower_dentry);

out_lock:
        fist_copy_attr_times(dir, lower_dir_dentry->d_inode);
        /* copy the nlink count for our dentry and our parent's dentry */
        dir->i_nlink =  lower_dir_dentry->d_inode->i_nlink;

        unlock_dir(lower_dir_dentry);

        /*
         * call d_drop so the system "forgets" about us
         */
        if (!err)
                d_drop(dentry);

        dput(dentry);

        print_exit_status(err);
        return err;
}
示例#7
0
static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry)
{
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry;
	int bstart, bend, bindex;
	int err = 0;
	int global_err = 0;

	print_entry_location();

	if ((err = unionfs_partial_lookup(dentry)))
		goto out;

	bstart = dbstart(dentry);
	bend = dbend(dentry);

	for (bindex = bend; bindex >= bstart; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry)
			continue;

		hidden_dir_dentry = lock_parent(hidden_dentry);

		/* avoid destroying the hidden inode if the file is in use */
		DGET(hidden_dentry);
		if (!(err = is_robranch_super(dentry->d_sb, bindex)))
			err = vfs_unlink(hidden_dir_dentry->d_inode,
					 hidden_dentry);
		DPUT(hidden_dentry);
		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		unlock_dir(hidden_dir_dentry);

		if (err) {
			/* passup the last error we got */
			if (!IS_COPYUP_ERR(err))
				goto out;
			global_err = err;
		}
	}

	/* check if encountered error in the above loop */
	if (global_err) {
		/* If we failed in the leftmost branch, then err will be set
		 * and we should move one over to create the whiteout.
		 * Otherwise, we should try in the leftmost branch. */
		if (err) {
			if (dbstart(dentry) == 0) {
				goto out;
			}
			err = create_whiteout(dentry, dbstart(dentry) - 1);
		} else {
			err = create_whiteout(dentry, dbstart(dentry));
		}
	} else if (dbopaque(dentry) != -1) {
		/* There is a hidden lower-priority file with the same name. */
		err = create_whiteout(dentry, dbopaque(dentry));
	}
      out:
	/* propagate number of hard-links */
	if (dentry->d_inode->i_nlink != 0) {
		dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
		if (!err && global_err)
			dentry->d_inode->i_nlink--;
	}
	/* We don't want to leave negative leftover dentries for revalidate. */
	if (!err && (global_err || dbopaque(dentry) != -1))
		update_bstart(dentry);

	print_exit_status(err);
	return err;
}
示例#8
0
static int unionfs_rmdir_all(struct inode *dir, struct dentry *dentry,
			     struct unionfs_dir_state *namelist)
{
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry;
	int bstart, bend, bindex;
	int err = 0;
	int global_err = 0;

	print_entry_location();
	fist_print_dentry("IN unionfs_rmdir_all: ", dentry);

	bstart = dbstart(dentry);
	bend = dbend(dentry);

	for (bindex = bend; bindex >= bstart; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry)
			continue;

		hidden_dir_dentry = lock_parent(hidden_dentry);
		if (S_ISDIR(hidden_dentry->d_inode->i_mode)) {
			delete_whiteouts(dentry, bindex, namelist);
			if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
				err =
				    vfs_rmdir(hidden_dir_dentry->d_inode,
					      hidden_dentry);
			}
		} else {
			err = -EISDIR;
		}

		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		unlock_dir(hidden_dir_dentry);
		if (err) {
			int local_err =
			    unionfs_refresh_hidden_dentry(dentry, bindex);
			if (local_err) {
				err = local_err;
				goto out;
			}

			if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY
			    && err != -EISDIR)
				goto out;

			global_err = err;
		}
	}

	/* check if encountered error in the above loop */
	if (global_err) {
		/* If we failed in the leftmost branch, then err will be set and we should
		 * move one over to create the whiteout.  Otherwise, we should try in the
		 * leftmost branch.
		 */
		if (err) {
			if (dbstart(dentry) == 0) {
				goto out;
			}
			err = create_whiteout(dentry, dbstart(dentry) - 1);
		} else {
			err = create_whiteout(dentry, dbstart(dentry));
		}
	} else {
		err = create_whiteout(dentry, dbstart(dentry));
	}

      out:
	/* propagate number of hard-links */
	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);

	fist_print_dentry("OUT unionfs_rmdir_all: ", dentry);
	print_exit_status(err);
	return err;
}
示例#9
0
/*
 * Finish off the rename, by either over writing the last destination or
 * unlinking the last destination to the left of us
 */
static int __rename_all_clobber(struct inode *old_dir,
				struct dentry *old_dentry,
				struct inode *new_dir,
				struct dentry *new_dentry,
				struct rename_info *info)
{
	int err = 0;

	print_entry_location();

	if (dtohd_index(old_dentry, info->new_bstart)) {
		/* rename the last source, knowing we're overwriting something */
		DPUT(info->wh_old);
		info->bwh_old = info->new_bstart;
		err =
		    do_rename(old_dir, old_dentry, new_dir, new_dentry,
			      info->new_bstart, &info->wh_old);
		if (IS_COPYUP_ERR(err)) {
			if (info->isdir) {
				err = -EXDEV;
				goto out;
			}
			if (info->rename_ok > info->new_bstart) {
				if ((info->do_copyup == -1)
				    || (info->new_bstart - 1 < info->do_copyup))
					info->do_copyup = info->new_bstart - 1;
			}
			if ((info->do_whiteout == -1)
			    || (info->new_bstart - 1 < info->do_whiteout)) {
				info->do_whiteout = info->new_bstart - 1;
			}
			err = 0;	// reset error
		}
	} else if (info->new_bstart < info->old_bstart) {
		/* the newly renamed file would get hidden, let's unlink the
		 * file to the left of it */
		struct dentry *unlink_dentry;
		struct dentry *unlink_dir_dentry;

		unlink_dentry = dtohd_index(new_dentry, info->new_bstart);

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

		fist_copy_attr_times(new_dentry->d_parent->d_inode,
				     unlink_dir_dentry->d_inode);
		new_dentry->d_parent->d_inode->i_nlink =
		    get_nlinks(new_dentry->d_parent->d_inode);

		unlock_dir(unlink_dir_dentry);

		if (IS_COPYUP_ERR(err)) {
			if (info->isdir) {
				err = -EXDEV;
				goto out;
			}
			if ((info->do_copyup == -1)
			    || (info->new_bstart - 1 < info->do_copyup))
				info->do_copyup = info->new_bstart - 1;

			err = 0;	// reset error
		}
	}

      out:
	print_exit_status(err);
	return err;
}
示例#10
0
static int unionfs_rename_whiteout(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;

	print_entry_location();

	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 = dtohd_index(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);
		}

		fist_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 =
		    get_nlinks(new_dentry->d_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				DPUT(unlink_dentry);
				set_dtohd_index(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;
			}
		}
	}

	/* 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 || IS_ERR(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
			    ("<0>We can't create a whiteout for the source in rename!\n");
			err = -EIO;
		}
	}

      out:
	DPUT(wh_old);
	print_exit_status(err);
	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 (!dtohd_index(new_dentry, bindex)
	    || !dtohd_index(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 (dtohd_index(old_dentry, bindex)
	    && dtohd_index(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;
	print_exit_status(err);
	return err;
}
示例#11
0
static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
			   const char *symname)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *whiteout_dentry = NULL;
	struct dentry *hidden_dir_dentry = NULL;
	umode_t mode;
	int bindex = 0, bstart;
	char *name = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_symlink", dentry);

	/* We start out in the leftmost branch. */
	bstart = dbstart(dentry);

	hidden_dentry = dtohd(dentry);

	/* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */
	name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found a .wh.foo entry, unlink it and then call vfs_symlink() */
		hidden_dir_dentry = lock_parent(whiteout_dentry);

		fist_print_generic_dentry("HDD", hidden_dir_dentry);
		fist_print_generic_dentry("WD", whiteout_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_unlink(hidden_dir_dentry->d_inode,
				       whiteout_dentry);
		}
		DPUT(whiteout_dentry);

		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		/* propagate number of hard-links */
		dir->i_nlink = get_nlinks(dir);

		unlock_dir(hidden_dir_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			/* should now try to create symlink in the another branch */
			bstart--;
		}
	}

	/* deleted whiteout if it was present, now do a normal vfs_symlink() with
	   possible recursive directory creation */
	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			/* if hidden_dentry is NULL, create the entire
			 * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when
			 * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a
			 * lone negative underlying dentry */
			hidden_dentry = create_parents(dir, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				if (IS_ERR(hidden_dentry)) {
					err = PTR_ERR(hidden_dentry);
				}
				fist_dprint(8,
					    "hidden dentry NULL (or error) for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_dir_dentry = lock_parent(hidden_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			mode = S_IALLUGO;
			err =
			    vfs_symlink(hidden_dir_dentry->d_inode,
					hidden_dentry, symname, mode);
		}
		unlock_dir(hidden_dir_dentry);

		if (err || !hidden_dentry->d_inode) {
			/* break out of for loop if error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			err = unionfs_interpose(dentry, dir->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(dir,
							 hidden_dir_dentry->
							 d_inode);
				/* update number of links on parent directory */
				dir->i_nlink = get_nlinks(dir);
			}
			break;
		}
	}

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

	KFREE(name);
	fist_print_dentry("OUT unionfs_symlink :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
示例#12
0
static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
			struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *hidden_old_dentry = NULL;
	struct dentry *hidden_new_dentry = NULL;
	struct dentry *hidden_dir_dentry = NULL;
	struct dentry *whiteout_dentry;
	char *name = NULL;

	print_entry_location();
	double_lock_dentry(new_dentry, old_dentry);

	hidden_new_dentry = dtohd(new_dentry);

	/* check if whiteout exists in the branch of new dentry, i.e. lookup
	 * .wh.foo first. If present, delete it */
	name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_new_dentry->d_parent,
			   new_dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found a .wh.foo entry, unlink it and then call vfs_link() */
		hidden_dir_dentry = lock_parent(whiteout_dentry);
		if (!
		    (err =
		     is_robranch_super(new_dentry->d_sb,
				       dbstart(new_dentry)))) {
			err =
			    vfs_unlink(hidden_dir_dentry->d_inode,
				       whiteout_dentry);
		}
		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		dir->i_nlink = get_nlinks(dir);
		unlock_dir(hidden_dir_dentry);
		hidden_dir_dentry = NULL;
		DPUT(whiteout_dentry);
		if (err)
			goto out;
	}

	if (dbstart(old_dentry) != dbstart(new_dentry)) {
		hidden_new_dentry =
		    create_parents(dir, new_dentry, dbstart(old_dentry));
		err = PTR_ERR(hidden_new_dentry);
		if (IS_COPYUP_ERR(err))
			goto docopyup;
		if (!hidden_new_dentry || IS_ERR(hidden_new_dentry))
			goto out;
	}
	hidden_new_dentry = dtohd(new_dentry);
	hidden_old_dentry = dtohd(old_dentry);

	BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
	hidden_dir_dentry = lock_parent(hidden_new_dentry);
	if (!(err = is_robranch(old_dentry)))
		err =
		    vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode,
			     hidden_new_dentry);
	unlock_dir(hidden_dir_dentry);

      docopyup:
	if (IS_COPYUP_ERR(err)) {
		int old_bstart = dbstart(old_dentry);
		int bindex;

		for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
			err =
			    copyup_dentry(old_dentry->d_parent->
					  d_inode, old_dentry,
					  old_bstart, bindex, NULL,
					  old_dentry->d_inode->i_size);
			if (!err) {
				hidden_new_dentry =
				    create_parents(dir, new_dentry, bindex);
				hidden_old_dentry = dtohd(old_dentry);
				hidden_dir_dentry =
				    lock_parent(hidden_new_dentry);
				/* do vfs_link */
				err =
				    vfs_link(hidden_old_dentry,
					     hidden_dir_dentry->d_inode,
					     hidden_new_dentry);
				unlock_dir(hidden_dir_dentry);
				goto check_link;
			}
		}
		goto out;
	}
      check_link:
	if (err || !hidden_new_dentry->d_inode)
		goto out;

	/* Its a hard link, so use the same inode */
	new_dentry->d_inode = IGRAB(old_dentry->d_inode);
	d_instantiate(new_dentry, new_dentry->d_inode);
	fist_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode);
	/* propagate number of hard-links */
	old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode);

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

	KFREE(name);

	unlock_dentry(new_dentry);
	unlock_dentry(old_dentry);

	print_exit_status(err);
	return err;
}