Exemple #1
0
STATIC int
base0fs_setattr(struct dentry *dentry, struct iattr *ia)
{
        int err = 0;
        struct dentry *lower_dentry;
        inode_t *inode;
        inode_t *lower_inode;

        print_entry_location();
        lower_dentry = base0fs_lower_dentry(dentry);
        inode = dentry->d_inode;
        lower_inode = INODE_TO_LOWER(inode);
        fist_checkinode(inode, "base0fs_setattr");


        err = notify_change(lower_dentry, 
#ifdef HAVE_3_ARG_NOTIFY_CHANGE
                DENTRY_TO_LVFSMNT(dentry),
#endif
                ia);

#if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA)
out:
#endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */
        /*
         * The lower file system might has changed the attributes, even if
         * notify_change above resulted in an error(!)  so we copy the
         * lower_inode's attributes (and a few more) to our inode.
         */
        fist_copy_attr_all(inode, lower_inode);

        fist_checkinode(inode, "post base0fs_setattr");
        print_exit_status(err);
        return err;
}
Exemple #2
0
STATIC int
base0fs_mknod(inode_t *dir, struct dentry *dentry, int mode, dev_t dev)
{
        int err;
        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_mknod-dir");

        lower_dir_dentry = base0fs_lock_parent(lower_dentry);

        err = VFS_MKNOD(lower_dir_dentry->d_inode,
                        lower_dentry,
                        mode,
                        dev);
        if (err || !lower_dentry->d_inode)
                goto out;

        err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0);
        if (err)
                goto out;
        fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);

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

        fist_checkinode(dir, "post base0fs_mknod-dir");
        print_exit_status(err);
        return err;
}
Exemple #3
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;
}
Exemple #4
0
STATIC int
base0fs_rename(inode_t *old_dir, struct dentry *old_dentry,
              inode_t *new_dir, struct dentry *new_dentry)
{
        int err;
        struct dentry *lower_old_dentry;
        struct dentry *lower_new_dentry;
        struct dentry *lower_old_dir_dentry;
        struct dentry *lower_new_dir_dentry;

        print_entry_location();

        lower_old_dentry = base0fs_lower_dentry(old_dentry);/* CPW: Moved below print_entry_location */
        lower_new_dentry = base0fs_lower_dentry(new_dentry);

        fist_checkinode(old_dir, "base0fs_rename-old_dir");
        fist_checkinode(new_dir, "base0fs_rename-new_dir");

        dget(lower_old_dentry);
        dget(lower_new_dentry);
        lower_old_dir_dentry = dget_parent(lower_old_dentry);
        lower_new_dir_dentry = dget_parent(lower_new_dentry);
        lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);


        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_lock;

        fist_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
        if (new_dir != old_dir)
                fist_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);

out_lock:
        // unlock_rename will dput the new/old parent dentries whose refcnts
        // were incremented via dget_parent above.
        dput(lower_new_dentry);
        dput(lower_old_dentry);
        unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);

        fist_checkinode(new_dir, "post base0fs_rename-new_dir");
        print_exit_status(err);
        return err;
}
Exemple #5
0
STATIC int
base0fs_link(struct dentry *old_dentry, inode_t *dir, struct dentry *new_dentry)
{
        int err;
        struct dentry *lower_old_dentry;
        struct dentry *lower_new_dentry;
        struct dentry *lower_dir_dentry;

        print_entry_location();
        lower_old_dentry = base0fs_lower_dentry(old_dentry);	/* CPW: Moved below print_entry_location */
        lower_new_dentry = base0fs_lower_dentry(new_dentry);

        fist_checkinode(dir, "base0fs_link-dir");
        fist_checkinode(old_dentry->d_inode, "base0fs_link-oldinode");

        dget(lower_old_dentry);
        dget(lower_new_dentry);
        lower_dir_dentry = base0fs_lock_parent(lower_new_dentry);


        err = VFS_LINK(lower_old_dentry,
                       lower_dir_dentry->d_inode,
                       lower_new_dentry);
        if (err || !lower_new_dentry->d_inode)
                goto out_lock;

        err = base0fs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
        if (err)
                goto out_lock;

        fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
        /* propagate number of hard-links */
        old_dentry->d_inode->i_nlink = INODE_TO_LOWER(old_dentry->d_inode)->i_nlink;

out_lock:
        unlock_dir(lower_dir_dentry);
        dput(lower_new_dentry);
        dput(lower_old_dentry);
        if (!new_dentry->d_inode)
                d_drop(new_dentry);

        print_exit_status(err);
        return err;
}
Exemple #6
0
/*
 * we now define delete_inode, because there are two VFS paths that may
 * destroy an inode: one of them calls clear inode before doing everything
 * else that's needed, and the other is fine.  This way we truncate the inode
 * size (and its pages) and then clear our own inode, which will do an iput
 * on our and the lower inode.
 */
static void unionfs_delete_inode(struct inode *inode)
{
	print_entry_location();

	fist_checkinode(inode, "unionfs_delete_inode IN");
	inode->i_size = 0;	/* every f/s seems to do that */
	clear_inode(inode);

	print_exit_location();
}
/*
 * we now define delete_inode, because there are two VFS paths that may
 * destroy an inode: one of them calls clear inode before doing everything
 * else that's needed, and the other is fine.  This way we truncate the inode
 * size (and its pages) and then clear our own inode, which will do an iput
 * on our and the lower inode.
 */
STATIC void
mini_fo_delete_inode(inode_t *inode)
{
	print_entry_location();

	fist_checkinode(inode, "mini_fo_delete_inode IN");
	inode->i_size = 0;		/* every f/s seems to do that */
	clear_inode(inode);

	print_exit_location();
}
Exemple #8
0
STATIC int
base0fs_create(inode_t *dir, struct dentry *dentry, int mode, struct nameidata *nd)
{
        int err;
        struct dentry *lower_dentry;
	struct vfsmount *lower_mount;
        struct dentry *lower_dir_dentry;
	FIST_ND_DECLARATIONS;

        print_entry_location();
        lower_dentry = base0fs_lower_dentry(dentry);
        BUG_ON(!lower_dentry);
        fist_checkinode(dir, "base0fs_create");
	lower_mount = DENTRY_TO_LVFSMNT(dentry);

        lower_dir_dentry = base0fs_lock_parent(lower_dentry);
        err = PTR_ERR(lower_dir_dentry);
        if (IS_ERR(lower_dir_dentry))
                goto out;

	FIST_ND_SAVE_ARGS(dentry, lower_dentry, lower_mount);
        err = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, nd);
	FIST_ND_RESTORE_ARGS;
        /* XXX this could potentially return a negative lower_dentry! */
        if (err)
                goto out_lock;

        err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0);
        if (err)
                goto out_lock;

        fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);


out_lock:
        unlock_dir(lower_dir_dentry);
out:
        fist_checkinode(dir, "post base0fs_create");
        print_exit_status(err);
        return err;
}
Exemple #9
0
STATIC int
base0fs_symlink(inode_t *dir, struct dentry *dentry, const char *symname)
{
        int err;
        struct dentry *lower_dentry;
        struct dentry *lower_dir_dentry;
        umode_t	mode;

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

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

        mode = S_IALLUGO;


        err = VFS_SYMLINK(lower_dir_dentry->d_inode, lower_dentry, symname, mode);


        if (err || !lower_dentry->d_inode)
                goto out_lock;
        err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0);
        if (err)
                goto out_lock;

        fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
        fist_checkinode(dir, "post base0fs_symlink-dir");

out_lock:
        unlock_dir(lower_dir_dentry);
        dput(lower_dentry);
        if (!dentry->d_inode)
                d_drop(dentry);

        print_exit_status(err);
        return err;
}
Exemple #10
0
STATIC int
base0fs_mkdir(inode_t *dir, struct dentry *dentry, int mode)
{
        int err;
        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_mkdir-dir");

        lower_dir_dentry = base0fs_lock_parent(lower_dentry);

        err = VFS_MKDIR(lower_dir_dentry->d_inode,
                        lower_dentry,
                        mode);
        if (err || !lower_dentry->d_inode)
                goto out;

        err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0);
        if (err)
                goto out;

        fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode);
        /* update number of links on parent directory */
        dir->i_nlink = lower_dir_dentry->d_inode->i_nlink;

        fist_checkinode(dir, "post base0fs_mkdir-dir");

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

        print_exit_status(err);
        return err;
}
Exemple #11
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;
}
Exemple #12
0
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		   struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *wh_dentry;

	print_entry_location();

	double_lock_dentry(old_dentry, new_dentry);

	fist_checkinode(old_dir, "unionfs_rename-old_dir");
	fist_checkinode(new_dir, "unionfs_rename-new_dir");
	fist_print_dentry("IN: unionfs_rename, old_dentry", old_dentry);
	fist_print_dentry("IN: unionfs_rename, new_dentry", new_dentry);

	if (!S_ISDIR(old_dentry->d_inode->i_mode))
	err = unionfs_partial_lookup(old_dentry);
	else
		err = may_rename_dir(old_dentry);
	if (err)
		goto out;
	err = unionfs_partial_lookup(new_dentry);
	if (err)
		goto out;

	/*
	 * if new_dentry is already hidden because of whiteout,
	 * simply override it even if the whiteouted dir is not empty.
	 */
	wh_dentry = lookup_whiteout(new_dentry);
	if (!IS_ERR(wh_dentry))
		DPUT(wh_dentry);
	else if (new_dentry->d_inode) {
		if (S_ISDIR(old_dentry->d_inode->i_mode) !=
		    S_ISDIR(new_dentry->d_inode->i_mode)) {
			err =
			    S_ISDIR(old_dentry->d_inode->
				    i_mode) ? -ENOTDIR : -EISDIR;
			goto out;
		}

		if (S_ISDIR(old_dentry->d_inode->i_mode)) {
			/* check if this unionfs directory is empty or not */
			err = check_empty(new_dentry, NULL);
			if (err)
				goto out;
			/* Handle the case where we are overwriting directories
			 * that are not really empty because of whiteout or
			 * non-whiteout entries.
			 */
		}
	}
#ifdef UNIONFS_DELETE_ALL
	if (IS_SET(old_dir->i_sb, DELETE_ALL))
		err = unionfs_rename_all(old_dir, old_dentry, new_dir,
					 new_dentry);
	else
#endif
		err = unionfs_rename_whiteout(old_dir, old_dentry, new_dir,
					      new_dentry);

      out:
	fist_checkinode(new_dir, "post unionfs_rename-new_dir");
	fist_print_dentry("OUT: unionfs_rename, old_dentry", old_dentry);

	if (err) {
		/* clear the new_dentry stuff created */
		d_drop(new_dentry);
	} else {
		/* force re-lookup since the dir on ro branch is not renamed,
		   and hidden dentries still indicate the un-renamed ones. */
		if (S_ISDIR(old_dentry->d_inode->i_mode))
			atomic_dec(&dtopd(old_dentry)->udi_generation);
		fist_print_dentry("OUT: unionfs_rename, new_dentry",
				  new_dentry);
	}

	unlock_dentry(new_dentry);
	unlock_dentry(old_dentry);
	print_exit_status(err);
	return err;
}
Exemple #13
0
struct dentry *unionfs_lookup_backend(struct dentry *dentry, int lookupmode)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *wh_hidden_dentry = NULL;
	struct dentry *hidden_dir_dentry = NULL;
	struct dentry *parent_dentry = NULL;
	int bindex, bstart, bend, bopaque;
	int dentry_count = 0;	/* Number of positive dentries. */
	int first_dentry_offset = -1;
	struct dentry *first_hidden_dentry = NULL;
	int locked_parent = 0;
	int locked_child = 0;

	int opaque;
	char *whname = NULL;
	const char *name;
	int namelen;

	print_entry("mode = %d", lookupmode);

	/* We should already have a lock on this dentry in the case of a
	 * partial lookup, or a revalidation. Otherwise it is returned from
	 * new_dentry_private_data already locked.  */
	if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL
	    || lookupmode == INTERPOSE_REVAL_NEG) {
		verify_locked(dentry);
	} else {
		BUG_ON(dtopd_nocheck(dentry) != NULL);
		locked_child = 1;
	}
	if (lookupmode != INTERPOSE_PARTIAL)
		if ((err = new_dentry_private_data(dentry)))
			goto out;
	/* must initialize dentry operations */
	dentry->d_op = &unionfs_dops;

	parent_dentry = GET_PARENT(dentry);
	/* We never partial lookup the root directory. */
	if (parent_dentry != dentry) {
		lock_dentry(parent_dentry);
		locked_parent = 1;
	} else {
		DPUT(parent_dentry);
		parent_dentry = NULL;
		goto out;
	}

	fist_print_dentry("IN unionfs_lookup (parent)", parent_dentry);
	fist_print_dentry("IN unionfs_lookup (child)", dentry);

	name = dentry->d_name.name;
	namelen = dentry->d_name.len;

	/* No dentries should get created for possible whiteout names. */
	if (!is_validname(name)) {
		err = -EPERM;
		goto out_free;
	}

	/* Now start the actual lookup procedure. */
	bstart = dbstart(parent_dentry);
	bend = dbend(parent_dentry);
	bopaque = dbopaque(parent_dentry);
	BUG_ON(bstart < 0);

	/* It would be ideal if we could convert partial lookups to only have
	 * to do this work when they really need to.  It could probably improve
	 * performance quite a bit, and maybe simplify the rest of the code. */
	if (lookupmode == INTERPOSE_PARTIAL) {
		bstart++;
		if ((bopaque != -1) && (bopaque < bend))
			bend = bopaque;
	}

	fist_dprint(8, "bstart = %d, bend = %d\n", bstart, bend);
	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry)
			continue;
		BUG_ON(hidden_dentry != NULL);

		hidden_dir_dentry = dtohd_index(parent_dentry, bindex);

		/* if the parent hidden dentry does not exist skip this */
		if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode))
			continue;

		/* also skip it if the parent isn't a directory. */
		if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode))
			continue;

		/* Reuse the whiteout name because its value doesn't change. */
		if (!whname) {
			whname = alloc_whname(name, namelen);
			if (IS_ERR(whname)) {
				err = PTR_ERR(whname);
				goto out_free;
			}
		}

		/* check if whiteout exists in this branch: lookup .wh.foo */
		wh_hidden_dentry = LOOKUP_ONE_LEN(whname, hidden_dir_dentry,
						  namelen + WHLEN);
		if (IS_ERR(wh_hidden_dentry)) {
			DPUT(first_hidden_dentry);
			err = PTR_ERR(wh_hidden_dentry);
			goto out_free;
		}

		if (wh_hidden_dentry->d_inode) {
			/* We found a whiteout so lets give up. */
			fist_dprint(8, "whiteout found in %d\n", bindex);
			if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) {
				set_dbend(dentry, bindex);
				set_dbopaque(dentry, bindex);
				DPUT(wh_hidden_dentry);
				break;
			}
			err = -EIO;
			printk(KERN_NOTICE "EIO: Invalid whiteout entry type"
			       " %d.\n", wh_hidden_dentry->d_inode->i_mode);
			DPUT(wh_hidden_dentry);
			DPUT(first_hidden_dentry);
			goto out_free;
		}

		DPUT(wh_hidden_dentry);
		wh_hidden_dentry = NULL;

		/* Now do regular lookup; lookup foo */
		hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry,
					       namelen);
		fist_print_generic_dentry("hidden result", hidden_dentry);
		if (IS_ERR(hidden_dentry)) {
			DPUT(first_hidden_dentry);
			err = PTR_ERR(hidden_dentry);
			goto out_free;
		}

		/* Store the first negative dentry specially, because if they
		 * are all negative we need this for future creates. */
		if (!hidden_dentry->d_inode) {
			if (!first_hidden_dentry && (dbstart(dentry) == -1)) {
				first_hidden_dentry = hidden_dentry;
				first_dentry_offset = bindex;
			} else {
				DPUT(hidden_dentry);
			}
			continue;
		}

		/* number of positive dentries */
		dentry_count++;

		/* store underlying dentry */
		if (dbstart(dentry) == -1)
			set_dbstart(dentry, bindex);
		set_dtohd_index(dentry, bindex, hidden_dentry);
		set_dbend(dentry, bindex);

		/* update parent directory's atime with the bindex */
		fist_copy_attr_atime(parent_dentry->d_inode,
				     hidden_dir_dentry->d_inode);

		/* We terminate file lookups here. */
		if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) {
			if (lookupmode == INTERPOSE_PARTIAL)
				continue;
			if (dentry_count == 1)
				goto out_positive;
			/* This can only happen with mixed D-*-F-* */
			BUG_ON(!S_ISDIR(dtohd(dentry)->d_inode->i_mode));
			continue;
		}

		opaque = is_opaque_dir(dentry, bindex);
		if (opaque < 0) {
			DPUT(first_hidden_dentry);
			err = opaque;
			goto out_free;
		}
		if (opaque) {
			set_dbend(dentry, bindex);
			set_dbopaque(dentry, bindex);
			break;
		}
	}

	if (dentry_count)
		goto out_positive;
	else
		goto out_negative;

      out_negative:
	if (lookupmode == INTERPOSE_PARTIAL)
		goto out;

	/* If we've only got negative dentries, then use the leftmost one. */
	if (lookupmode == INTERPOSE_REVAL) {
		if (dentry->d_inode) {
			itopd(dentry->d_inode)->uii_stale = 1;
		}
		goto out;
	}
	/* This should only happen if we found a whiteout. */
	if (first_dentry_offset == -1) {
		first_hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry,
						     namelen);
		first_dentry_offset = bindex;
		if (IS_ERR(first_hidden_dentry)) {
			err = PTR_ERR(first_hidden_dentry);
			goto out;
		}
	}
	set_dtohd_index(dentry, first_dentry_offset, first_hidden_dentry);
	set_dbstart(dentry, first_dentry_offset);
	set_dbend(dentry, first_dentry_offset);

	if (lookupmode == INTERPOSE_REVAL_NEG)
		BUG_ON(dentry->d_inode != NULL);
	else
		d_add(dentry, NULL);
	goto out;

/* This part of the code is for positive dentries. */
      out_positive:
	BUG_ON(dentry_count <= 0);

	/* If we're holding onto the first negative dentry throw it out. */
	DPUT(first_hidden_dentry);

	/* Partial lookups need to reinterpose, or throw away older negs. */
	if (lookupmode == INTERPOSE_PARTIAL) {
		if (dentry->d_inode) {
			unionfs_reinterpose(dentry);
			goto out;
		}

		/* This somehow turned positive, so it is as if we had a
		 * negative revalidation.  */
		lookupmode = INTERPOSE_REVAL_NEG;

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

	err = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
	if (err)
		goto out_drop;

	fist_checkinode(dentry->d_inode, "unionfs_lookup OUT: child");
	fist_checkinode(parent_dentry->d_inode, "unionfs_lookup OUT: dir");
	goto out;

      out_drop:
	d_drop(dentry);

      out_free:
	/* should dput all the underlying dentries on error condition */
	bstart = dbstart(dentry);
	if (bstart >= 0) {
		bend = dbend(dentry);
		for (bindex = bstart; bindex <= bend; bindex++)
			DPUT(dtohd_index(dentry, bindex));
	}
	KFREE(dtohd_ptr(dentry));
	dtohd_ptr(dentry) = NULL;
	set_dbstart(dentry, -1);
	set_dbend(dentry, -1);

      out:
	if (!err && dtopd(dentry)) {
		BUG_ON(dbend(dentry) > dtopd(dentry)->udi_bcount);
		BUG_ON(dbend(dentry) > sbmax(dentry->d_sb));
		BUG_ON(dbstart(dentry) < 0);
	}
	KFREE(whname);
	fist_print_dentry("OUT unionfs_lookup (parent)", parent_dentry);
	fist_print_dentry("OUT unionfs_lookup (child)", dentry);
	if (locked_parent)
		unlock_dentry(parent_dentry);
	DPUT(parent_dentry);
	if (locked_child)
		unlock_dentry(dentry);
	print_exit_status(err);
	return ERR_PTR(err);
}
Exemple #14
0
STATIC struct dentry *
base0fs_lookup(inode_t *dir,
	      struct dentry *dentry,
	      struct nameidata* nd_unused_in_this_fxn /* XXX: fix code if ever used */ )
{
        int err = 0;
        struct dentry *lower_dir_dentry;
        struct dentry *lower_dentry = NULL;
        struct vfsmount *lower_mount;
        const char *name;
        vnode_t *this_vnode;
        struct dentry *this_dir;
        unsigned int namelen;

        print_entry_location();
        lower_dir_dentry = base0fs_lower_dentry(dentry->d_parent);	/* CPW: Moved below print_entry_location */
        name = dentry->d_name.name;
        namelen = dentry->d_name.len;
        fist_checkinode(dir, "base0fs_lookup");

        this_vnode = dir;
        this_dir = lower_dir_dentry;

        fist_print_dentry("base0fs_lookup IN", dentry);
        fist_print_dentry("base0fs_lookup: dentry->d_parent IN", dentry->d_parent);
        fist_print_dentry("base0fs_lookup: lower_dir_dentry IN", lower_dir_dentry);
        fist_print_inode("base0fs_lookup: dir IN", dir);

        if (lower_dir_dentry->d_inode)
                fist_print_inode("base0fs_lookup: lower_dir_dentry->d_inode",
                                 lower_dir_dentry->d_inode);

        /* must initialize dentry operations */
        dentry->d_op = &base0fs_dops;

        ;

        /* increase refcount of base dentry (lookup_one[_len] will decrement) */
        // THIS IS RIGHT! (don't "fix" it)
        // NO THIS IS WRONG IN 2.3.99-pre6. lookup_one[_len] will NOT decrement
        // dget(lower_dir_dentry);

        lock_inode(lower_dir_dentry->d_inode);
        /* will allocate a new lower dentry if needed */
        lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen);
        unlock_inode(lower_dir_dentry->d_inode);


        if (IS_ERR(lower_dentry)) {
                /*
                 * this produces an unusual dentry: one that has neither an
                 * inode, nor a private structure attached to it. All cleanup
                 * methods (d_delete, d_release, etc) must be prepared to deal
                 * with such dentries. Ion 09/29/2001
                 */

                err = PTR_ERR(lower_dentry);
                goto out;
        }

	lower_mount = mntget(DENTRY_TO_LVFSMNT(dentry->d_parent));


        ;

        if (lower_dentry->d_inode && lower_dentry->d_inode->i_sb) {
            struct super_block *lower_sb = lower_dentry->d_inode->i_sb;
            if (lower_sb->s_dev == sProcDev || lower_sb->s_dev == sDevDev) {
                d_drop(dentry);         /* Don't leak our dentry. */
                return lower_dentry;
            }
        }

        /* update parent directory's atime */
        fist_copy_attr_atime(dir, lower_dir_dentry->d_inode);
        /* link the upper and lower dentries */
        DENTRY_TO_PRIVATE_SM(dentry) = (struct base0fs_dentry_info *) KMALLOC(sizeof(struct base0fs_dentry_info), GFP_KERNEL);
        if (!DENTRY_TO_PRIVATE(dentry)) {
                err = -ENOMEM;
                goto out_dput;
        }
        DENTRY_TO_PRIVATE(dentry)->wdi_dentry = lower_dentry;
	DENTRY_TO_PRIVATE(dentry)->wdi_mnt = lower_mount;


        /* lookup is special: it needs to handle negative dentries */
        if (!lower_dentry->d_inode) {
                d_add(dentry, NULL);
                fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry);
                goto out;
        }

        fist_dprint(6, "lookup \"%s\" -> inode %lu\n", name, lower_dentry->d_inode->i_ino);
        err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 1);
        if (err)
                goto out_free;

        fist_checkinode(dentry->d_inode, "base0fs_lookup OUT: dentry->d_inode:");
        fist_checkinode(dir, "base0fs_lookup OUT: dir");

        fist_print_dentry("base0fs_lookup OUT lower_dentry", lower_dentry);
        fist_print_inode("base0fs_lookup OUT lower_inode", lower_dentry->d_inode);


        /* All is well */
        goto out;

out_free:
        d_drop(dentry);		/* so that our bad dentry will get destroyed */


        KFREE(DENTRY_TO_PRIVATE(dentry));
        DENTRY_TO_PRIVATE_SM(dentry) = NULL;	/* be safe */

out_dput:
        if (lower_dentry)
                dput(lower_dentry);

out:
        fist_print_dentry("base0fs_lookup OUT", dentry);
        print_exit_status(err);
        return ERR_PTR(err);
}
Exemple #15
0
static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
{
	int err = 0;
	struct dentry *hidden_dentry;
	struct inode *inode = NULL;
	struct inode *hidden_inode = NULL;
	int bstart, bend, bindex;
	int i;
	int copyup = 0;

	print_entry_location();
	lock_dentry(dentry);
	bstart = dbstart(dentry);
	bend = dbend(dentry);
	inode = dentry->d_inode;

	for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry)
			continue;
		BUG_ON(hidden_dentry->d_inode == NULL);

		/* If the file is on a read only branch */
		if (is_robranch_super(dentry->d_sb, bindex)
		    || IS_RDONLY(hidden_dentry->d_inode)) {
			if (copyup || (bindex != bstart))
				continue;
			/* Only if its the leftmost file, copyup the file */
			for (i = bstart - 1; i >= 0; i--) {
				size_t size = dentry->d_inode->i_size;
				if (ia->ia_valid & ATTR_SIZE)
					size = ia->ia_size;
				err = copyup_dentry(dentry->d_parent->d_inode,
						    dentry, bstart, i, NULL,
						    size);

				if (!err) {
					copyup = 1;
					hidden_dentry = dtohd(dentry);
					break;
				}
				/* if error is in the leftmost f/s, pass it up */
				if (i == 0)
					goto out;
			}

		}
		err = notify_change(hidden_dentry, ia);
		if (err)
			goto out;
		break;
	}

	/* get the size from the first hidden inode */
	hidden_inode = itohi(dentry->d_inode);
	fist_checkinode(inode, "unionfs_setattr");
	fist_copy_attr_all(inode, hidden_inode);

      out:
	unlock_dentry(dentry);
	fist_checkinode(inode, "post unionfs_setattr");
	print_exit_status(err);
	return err;
}
Exemple #16
0
static int unionfs_create(struct inode *parent, struct dentry *dentry,
			  int mode, struct nameidata *nd)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *whiteout_dentry = NULL;
	struct dentry *new_hidden_dentry;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_create", 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 */
	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);
		whiteout_dentry = NULL;
		goto out;
	}

	if (whiteout_dentry->d_inode) {
		/* .wh.foo has been found. */
		/* First truncate it and then rename it to foo (hence having
		 * the same overall effect as a normal create.
		 *
		 * XXX: This is not strictly correct.  If we have unlinked the
		 * file and it still has a reference count, then we should
		 * actually unlink the whiteout so that user's data isn't
		 * hosed over.
		 */
		struct dentry *hidden_dir_dentry;
		struct iattr newattrs;

		down(&whiteout_dentry->d_inode->i_sem);
		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME
		    | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE
		    | ATTR_KILL_SUID | ATTR_KILL_SGID;

		newattrs.ia_mode = mode & ~current->fs->umask;
		newattrs.ia_uid = current->fsuid;
		newattrs.ia_gid = current->fsgid;

		if (whiteout_dentry->d_inode->i_size != 0) {
			newattrs.ia_valid |= ATTR_SIZE;
			newattrs.ia_size = 0;
		}

		err = notify_change(whiteout_dentry, &newattrs);

		up(&whiteout_dentry->d_inode->i_sem);

		if (err)
			printk(KERN_WARNING
			       "unionfs: %s:%d: notify_change failed: %d, ignoring..\n",
			       __FILE__, __LINE__, err);

		new_hidden_dentry = dtohd(dentry);
		DGET(new_hidden_dentry);

		hidden_dir_dentry = GET_PARENT(whiteout_dentry);
		lock_rename(hidden_dir_dentry, hidden_dir_dentry);

		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_rename(hidden_dir_dentry->d_inode,
				       whiteout_dentry,
				       hidden_dir_dentry->d_inode,
				       new_hidden_dentry);
		}
		if (!err) {
			fist_copy_attr_timesizes(parent,
						 new_hidden_dentry->d_parent->
						 d_inode);
			parent->i_nlink = get_nlinks(parent);
		}

		unlock_rename(hidden_dir_dentry, hidden_dir_dentry);
		DPUT(hidden_dir_dentry);

		DPUT(new_hidden_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			/* We were not able to create the file in this branch,
			 * so, we try to create it in one branch to left
			 */
			bstart--;
		} else {
			/* reset the unionfs dentry to point to the .wh.foo entry. */

			/* Discard any old reference. */
			DPUT(dtohd(dentry));

			/* Trade one reference to another. */
			set_dtohd_index(dentry, bstart, whiteout_dentry);
			whiteout_dentry = NULL;

			err = unionfs_interpose(dentry, parent->i_sb, 0);
			goto out;
		}
	}

	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(parent, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				if (IS_ERR(hidden_dentry))
					err = PTR_ERR(hidden_dentry);
				continue;
			}
		}

		fist_checkinode(parent, "unionfs_create");

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		/* We shouldn't create things in a read-only branch. */
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			//DQ: vfs_create has a different prototype in 2.6
			err = vfs_create(hidden_parent_dentry->d_inode,
					 hidden_dentry, mode, nd);
		}
		if (err || !hidden_dentry->d_inode) {
			unlock_dir(hidden_parent_dentry);

			/* break out of for loop if the error wasn't  -EROFS */
			if (!IS_COPYUP_ERR(err))
				break;
		} else {
			err = unionfs_interpose(dentry, parent->i_sb, 0);
			if (!err) {
				fist_copy_attr_timesizes(parent,
							 hidden_parent_dentry->
							 d_inode);
				/* update number of links on parent directory */
				parent->i_nlink = get_nlinks(parent);
			}
			unlock_dir(hidden_parent_dentry);
			break;
		}
	}

      out:
	DPUT(whiteout_dentry);
	KFREE(name);

	fist_print_dentry("OUT unionfs_create :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}