Exemplo n.º 1
0
STATIC int
mini_fo_d_hash(dentry_t *dentry, qstr_t *name)
{
	int err = 0;
	dentry_t *hidden_dentry;
	dentry_t *hidden_sto_dentry;

	/* hidden_dentry = mini_fo_hidden_dentry(dentry);
	 * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */

	/* state 1, 3, 4, 5: build the hash for the storage dentry */
	if((dtopd(dentry)->state == MODIFIED) ||
	   (dtopd(dentry)->state == CREATED) ||
	   (dtopd(dentry)->state == DEL_REWRITTEN) ||
	   (dtopd(dentry)->state == DELETED)) {
		hidden_sto_dentry = dtohd2(dentry);
		if(hidden_sto_dentry &&
		   hidden_sto_dentry->d_op &&
		   hidden_sto_dentry->d_op->d_hash) {
			err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name);
		}
		goto out;
	}
	/* state 2: build the hash for the base dentry */
	if(dtopd(dentry)->state == UNMODIFIED) {
		hidden_dentry = dtohd(dentry);
		if(hidden_dentry &&
		   hidden_dentry->d_op &&
		   hidden_dentry->d_op->d_hash) {
			err = hidden_dentry->d_op->d_hash(hidden_dentry, name);
		}
		goto out;
	}
	/* state 6: build hash for the dentry that exists */
	if(dtopd(dentry)->state == NON_EXISTANT) {
		hidden_sto_dentry = dtohd2(dentry);
		if(hidden_sto_dentry &&
		   hidden_sto_dentry->d_op &&
		   hidden_sto_dentry->d_op->d_hash) {
			err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name);
			goto out;
		}
		hidden_dentry = dtohd(dentry);
		if(hidden_dentry &&
		   hidden_dentry->d_op &&
		   hidden_dentry->d_op->d_hash) {
			err = hidden_dentry->d_op->d_hash(hidden_dentry, name);
			goto out;
		}
	}

	printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n");

 out:
	return err;
}
Exemplo n.º 2
0
/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
int unionfs_removexattr(struct dentry *dentry, const char *name)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	char *encoded_name;
	print_entry_location();

	lock_dentry(dentry);
	hidden_dentry = dtohd(dentry);

	fist_dprint(8, "removexattr: name=\"%s\"\n", name);

	if (hidden_dentry->d_inode->i_op->removexattr) {
		encoded_name = (char *)name;

		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->removexattr(hidden_dentry,
							      encoded_name);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 3
0
void
mini_fo_d_release(dentry_t *dentry)
{
	dentry_t *hidden_dentry;
	dentry_t *hidden_sto_dentry;


	/* this could be a negative dentry, so check first */
	if (!dtopd(dentry)) {
		printk(KERN_CRIT "mini_fo_d_release: no private data.\n");
		goto out;
	}
	hidden_dentry = dtohd(dentry);
	hidden_sto_dentry = dtohd2(dentry);

	if(hidden_dentry) {
		/* decrement hidden dentry's counter and free its inode */
		dput(hidden_dentry);
	}
	if(hidden_sto_dentry) {
                /* decrement hidden dentry's counter and free its inode */
		dput(hidden_sto_dentry);
	}

	/* free private data (mini_fo_dentry_info) here */
	kfree(dtopd(dentry));
	__dtopd(dentry) = NULL;	/* just to be safe */
 out:
	return;
}
Exemplo n.º 4
0
/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
ssize_t unionfs_listxattr(struct dentry * dentry, char *list, size_t size)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	char *encoded_list = NULL;

	print_entry_location();
	lock_dentry(dentry);

	hidden_dentry = dtohd(dentry);

	if (hidden_dentry->d_inode->i_op->listxattr) {
		encoded_list = list;
		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->listxattr(hidden_dentry,
							    encoded_list, size);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 5
0
/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
int
unionfs_setxattr(struct dentry *dentry, const char *name, const void *value,
		 size_t size, int flags)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;

	print_entry_location();

	lock_dentry(dentry);
	hidden_dentry = dtohd(dentry);

	fist_dprint(8, "setxattr: name=\"%s\", value %lu bytes, flags=%x\n",
		    name, (unsigned long)size, flags);

	if (hidden_dentry->d_inode->i_op->setxattr) {
		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err = hidden_dentry->d_inode->i_op->
		    setxattr(hidden_dentry, name, value, size, flags);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 6
0
int unionfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
{
	int err;
	struct dentry *hidden_dentry;

	print_entry_location();
	lock_dentry(dentry);
	hidden_dentry = dtohd(dentry);
	fist_print_dentry("unionfs_readlink IN", dentry);

	if (!hidden_dentry->d_inode->i_op ||
	    !hidden_dentry->d_inode->i_op->readlink) {
		err = -EINVAL;
		goto out;
	}

	err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry,
						     buf, bufsiz);
	if (err > 0)
		fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode);

      out:
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 7
0
int
kdb3fs_d_delete(dentry_t *dentry)
{
	dentry_t *hidden_dentry;
	int err = 0;

	print_entry_location();
#if 0
	/* this could be a negative dentry, so check first */
	if (!dtopd(dentry)) {
		fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name);
		goto out;
	}

	if (!(hidden_dentry = dtohd(dentry))) {
		fist_dprint(6, "dentry without hidden_dentry: %*s", dentry->d_name.len, dentry->d_name.name);
		goto out;
	}

	//    fist_print_dentry("D_DELETE IN", dentry);
	/* added b/c of changes to dput(): it calls d_drop on us */
	if (hidden_dentry->d_op &&
	    hidden_dentry->d_op->d_delete) {
		err = hidden_dentry->d_op->d_delete(hidden_dentry);
	}
#endif
out:
	print_exit_status(err);
	return err;
}
Exemplo n.º 8
0
void
kdb3fs_d_release(dentry_t *dentry)
{
	dentry_t *hidden_dentry;

	print_entry_location();
#if 0	
	fist_print_dentry("kdb3fs_d_release IN dentry", dentry);

	/* this could be a negative dentry, so check first */
	if (!dtopd(dentry)) {
		fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name);
		goto out;
	}
	hidden_dentry = dtohd(dentry);
	fist_print_dentry("kdb3fs_d_release IN hidden_dentry", hidden_dentry);
	if (hidden_dentry->d_inode)
		fist_dprint(6, "kdb3fs_d_release: hidden_inode->i_count %d, i_num %lu.\n",
			    atomic_read(&hidden_dentry->d_inode->i_count),
			    hidden_dentry->d_inode->i_ino);

	/* free private data (kdb3fs_dentry_info) here */
	KFREE(dtopd(dentry));
	dtopd(dentry) = NULL;	/* just to be safe */
	/* decrement hidden dentry's counter and free its inode */
	dput(hidden_dentry);
#endif
out:
	print_exit_location();
}
Exemplo n.º 9
0
int
mini_fo_d_delete(dentry_t *dentry)
{
	dentry_t *hidden_dentry;
	dentry_t *hidden_sto_dentry;
	int err = 0;

	/* this could be a negative dentry, so check first */
	if (!dtopd(dentry)) {
		printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n");
		goto out;
	}
	hidden_dentry = dtohd(dentry);
	hidden_sto_dentry = dtohd2(dentry);

	if(hidden_dentry) {
		if(hidden_dentry->d_op &&
		   hidden_dentry->d_op->d_delete) {
			err = hidden_dentry->d_op->d_delete(hidden_dentry);
		}
	}
	if(hidden_sto_dentry) {
		if(hidden_sto_dentry->d_op &&
		   hidden_sto_dentry->d_op->d_delete) {
			err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry);
		}
	}

 out:
	return err;
}
Exemplo n.º 10
0
STATIC int
mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b)
{
	int err;
	dentry_t *hidden_dentry=NULL;

	/* hidden_dentry = mini_fo_hidden_dentry(dentry); */
	if(dtohd2(dentry))
		hidden_dentry = dtohd2(dentry);
	else if(dtohd(dentry))
		hidden_dentry = dtohd(dentry);

	if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) {
		err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b);
	} else {
		err = ((a->len != b->len) || memcmp(a->name, b->name, b->len));
	}

	return err;
}
Exemplo n.º 11
0
mini_fo_d_revalidate(dentry_t *dentry, int flags)
#endif
{
	int err1 = 1; /* valid = 1, invalid = 0 */
	int err2 = 1;
	dentry_t *hidden_dentry;
	dentry_t *hidden_sto_dentry;


	check_mini_fo_dentry(dentry);

	hidden_dentry  = dtohd(dentry);
	hidden_sto_dentry = dtohd2(dentry);

	if(hidden_dentry &&
	   hidden_dentry->d_op &&
	   hidden_dentry->d_op->d_revalidate) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd);
#else
		err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags);
#endif
	}
	if(hidden_sto_dentry &&
	   hidden_sto_dentry->d_op &&
	   hidden_sto_dentry->d_op->d_revalidate) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry,
							     nd);
#else
		err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry,
							     flags);
#endif
	}

	/* mk: if one of the lower level dentries are valid,
	 * the mini_fo dentry is too.
	 */
	return (err1 || err2);
}
Exemplo n.º 12
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;
}
Exemplo n.º 13
0
/* BKL held by caller.
 * dentry->d_inode->i_sem down
 * ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
 */
ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value,
			 size_t size)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	/* Define these anyway so we don't need as much ifdef'ed code. */
	char *encoded_name = NULL;
	char *encoded_value = NULL;

	print_entry_location();

	lock_dentry(dentry);

	hidden_dentry = dtohd(dentry);

	fist_dprint(8, "getxattr: name=\"%s\", value %lu bytes\n", name,
		    (unsigned long)size);

	if (hidden_dentry->d_inode->i_op->getxattr) {
		encoded_name = (char *)name;

		encoded_value = (char *)value;

		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->getxattr(hidden_dentry,
							   encoded_name,
							   encoded_value, size);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);

	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 14
0
int create_sto_reg_file(dentry_t *dentry, int mode)
#endif
{
	int err = 0;
	inode_t *dir;
	dentry_t *hidden_sto_dentry;
	dentry_t *hidden_sto_dir_dentry;

	if(exists_in_storage(dentry)) {
		printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n");
		err = -EINVAL;
		goto out;
	}
	err = get_neg_sto_dentry(dentry);

	if (err) {
		printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n");
		goto out;
	}

	dir = dentry->d_parent->d_inode;
	hidden_sto_dentry = dtohd2(dentry);

	/* lock parent */
	hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent);

	err = PTR_ERR(hidden_sto_dir_dentry);
        if (IS_ERR(hidden_sto_dir_dentry))
                goto out;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	err = vfs_create(hidden_sto_dir_dentry->d_inode,
			 hidden_sto_dentry,
			 mode, nd);
#else
	err = vfs_create(hidden_sto_dir_dentry->d_inode,
			 hidden_sto_dentry,
			 mode);
#endif
        if(err) {
		printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n");
                goto out_lock;
	}

	if(!dtohd2(dentry)->d_inode) {
		printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n");
                err = -EINVAL;
                goto out_lock;
        }

        /* interpose the new inode */
        if(dtost(dentry) == DELETED) {
                dtost(dentry) = DEL_REWRITTEN;
                err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0);
                if(err)
                        goto out_lock;
        }
        else if(dtost(dentry) == NON_EXISTANT) {
                dtost(dentry) = CREATED;
                err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0);
                if(err)
                        goto out_lock;
        }
        else if(dtost(dentry) == UNMODIFIED) {
                dtost(dentry) = MODIFIED;
                /* interpose on new inode */
                if(itohi2(dentry->d_inode) != NULL) {
                        printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n");
                        err = -EINVAL;
                        goto out_lock;
                }
                itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode);
	}
	fist_copy_attr_timesizes(dentry->d_parent->d_inode,
				 hidden_sto_dir_dentry->d_inode);

 out_lock:
        dput(hidden_sto_dir_dentry);
 out:
	return err;
}
Exemplo n.º 15
0
int create_sto_nod(dentry_t *dentry, int mode, int dev)
#endif
{
	int err = 0;
	inode_t *dir;
	dentry_t *hidden_sto_dentry;
	dentry_t *hidden_sto_dir_dentry;

	if(exists_in_storage(dentry)) {
		err = -EEXIST;
		goto out;
	}
	err = get_neg_sto_dentry(dentry);

	if (err) {
                printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n");
                goto out;
        }

	dir = dentry->d_parent->d_inode;
	hidden_sto_dentry = dtohd2(dentry);

	/* lock parent */
	hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent);

	err = PTR_ERR(hidden_sto_dir_dentry);
	if (IS_ERR(hidden_sto_dir_dentry))
		goto out;

	err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev);
	if(err)
		goto out_lock;

	if(!dtohd2(dentry)->d_inode) {
		printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n");
		err = -EINVAL; /* return something indicating failure */
		goto out_lock;
	}

	/* interpose the new inode */
	if(dtost(dentry) == DELETED) {
		dtost(dentry) = DEL_REWRITTEN;
		err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0);
		if(err)
			goto out_lock;
	}
	else if(dtost(dentry) == NON_EXISTANT) {
		dtost(dentry) = CREATED;
		err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0);
		if(err)
			goto out_lock;
	}
	else if(dtost(dentry) == UNMODIFIED) {
		dtost(dentry) = MODIFIED;
		/* interpose on new inode */
		if(itohi2(dentry->d_inode) != NULL) {
			printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n");
			err = -EINVAL;
			goto out_lock;
		}
		itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode);
	}

	fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode);

 out_lock:
	dput(hidden_sto_dir_dentry);
 out:
	return err;
}
Exemplo n.º 16
0
/* create the sto dir, setup states */
int create_sto_dir(dentry_t *dentry, int mode)
{
	int err = 0;
	inode_t *dir;
	dentry_t *hidden_sto_dentry;
        dentry_t *hidden_sto_dir_dentry;

	/* had to take the "!S_ISDIR(mode))" check out, because it failed */
	if(exists_in_storage(dentry)) {
                printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\
n");
                err = -EINVAL;
                goto out;
        }

	err = get_neg_sto_dentry(dentry);
	if(err) {
		err = -EINVAL;
		goto out;
	}

	dir = dentry->d_parent->d_inode;
	hidden_sto_dentry = dtohd2(dentry);

	/* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */
	hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent);

	err = PTR_ERR(hidden_sto_dir_dentry);
	if (IS_ERR(hidden_sto_dir_dentry))
		goto out;

	err = vfs_mkdir(hidden_sto_dir_dentry->d_inode,
			hidden_sto_dentry,
			mode);
	if(err) {
		printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n");
		goto out_lock;
	}

	if(!dtohd2(dentry)->d_inode) {
		printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n");
		err = -EINVAL;
		goto out_lock;
	}

	/* interpose the new inode */
	if(dtost(dentry) == DELETED) {
		dtost(dentry) = DEL_REWRITTEN;
		err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0);
		if(err)
			goto out_lock;
	}
	else if(dtopd(dentry)->state == NON_EXISTANT) {
		dtopd(dentry)->state = CREATED;
		err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0);
		if(err)
			goto out_lock;
	}
	else if(dtopd(dentry)->state == UNMODIFIED) {
		dtopd(dentry)->state = MODIFIED;
		/* interpose on new inode */
		if(itohi2(dentry->d_inode) != NULL) {
			printk(KERN_CRIT "mini_fo:  create_sto_dir: ERROR, invalid inode detected.\n");
			err = -EINVAL;
			goto out_lock;
		}
		itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode);
	}

	fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode);

	/* initalize the wol list */
	itopd(dentry->d_inode)->deleted_list_size = -1;
	itopd(dentry->d_inode)->renamed_list_size = -1;
	meta_build_lists(dentry);


 out_lock:
	/* was: unlock_dir(hidden_sto_dir_dentry); */
	dput(hidden_sto_dir_dentry);
 out:
	return err;
}
Exemplo n.º 17
0
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct dentry *hidden_old_dentry;
	struct dentry *hidden_wh_dentry = NULL;
	struct dentry *hidden_old_dir_dentry, *hidden_new_dir_dentry;
	char *name = NULL;
	struct iattr newattrs;

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

	/* create whiteout, get the leftmost underlying dentry and rename it */
	hidden_old_dentry = dtohd(dentry);

	/* lookup .wh.foo first, MUST NOT EXIST */
	name = KMALLOC(dentry->d_name.len + sizeof(".wh."), GFP_UNIONFS);
	if (!name) {
		err = -ENOMEM;
		goto out;
	}
	strcpy(name, ".wh.");
	strncat(name, dentry->d_name.name, dentry->d_name.len);
	name[4 + dentry->d_name.len] = '\0';

	hidden_wh_dentry =
	    LOOKUP_ONE_LEN(name, hidden_old_dentry->d_parent,
			   dentry->d_name.len + 4);
	if (IS_ERR(hidden_wh_dentry)) {
		err = PTR_ERR(hidden_wh_dentry);
		goto out;
	}
	ASSERT(hidden_wh_dentry->d_inode == NULL);

	DGET(hidden_old_dentry);

	hidden_old_dir_dentry = GET_PARENT(hidden_old_dentry);
	hidden_new_dir_dentry = GET_PARENT(hidden_wh_dentry);
	double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry);

	if (!(err = is_robranch(dentry))) {
		if (S_ISREG(hidden_old_dentry->d_inode->i_mode)) {
			err = vfs_rename(hidden_old_dir_dentry->d_inode,
					 hidden_old_dentry,
					 hidden_new_dir_dentry->d_inode,
					 hidden_wh_dentry);
		} else {
			err = vfs_unlink(hidden_old_dir_dentry->d_inode,
					 hidden_old_dentry);
			if (!err)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
				err = vfs_create(hidden_new_dir_dentry->d_inode,
						 hidden_wh_dentry, 0666, NULL);
#else
				err = vfs_create(hidden_new_dir_dentry->d_inode,
						 hidden_wh_dentry, 0666);
#endif
		}
	}

	double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
	DPUT(hidden_old_dentry);
	DPUT(hidden_wh_dentry);

	if (!err) {
		hidden_wh_dentry = LOOKUP_ONE_LEN(name,
						  hidden_old_dentry->d_parent,
						  dentry->d_name.len + 4);
		if (IS_ERR(hidden_wh_dentry)) {
			err = PTR_ERR(hidden_wh_dentry);
			goto out;
		}
		PASSERT(hidden_wh_dentry->d_inode);
		down(&hidden_wh_dentry->d_inode->i_sem);
		newattrs.ia_valid = ATTR_CTIME;
		if (hidden_wh_dentry->d_inode->i_size != 0) {
			newattrs.ia_valid |= ATTR_SIZE;
			newattrs.ia_size = 0;
		}
		/* We discard this error, because the entry is whited out
		 * even if we fail here. */
		notify_change(hidden_wh_dentry, &newattrs);
		up(&hidden_wh_dentry->d_inode->i_sem);
		DPUT(hidden_wh_dentry);
	}

	if (err) {
		if (dbstart(dentry) == 0)
			goto out;
		/* exit if the error returned was NOT -EROFS */
		if (!IS_COPYUP_ERR(err))
			goto out;
		err = create_whiteout(dentry, dbstart(dentry) - 1);
	} else {
		fist_copy_attr_all(dir, hidden_new_dir_dentry->d_inode);
	}

      out:
	KFREE(name);
	print_exit_status(err);
	return err;
}
Exemplo n.º 18
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);
}
Exemplo n.º 19
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;
}
Exemplo n.º 20
0
static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;
	int whiteout_unlinked = 0;
	uid_t saved_uid = current->fsuid;
	gid_t saved_gid = current->fsgid;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_mkdir", dentry);
	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);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		hidden_parent_dentry = lock_parent(whiteout_dentry);

		/* Set the uid and gid to trick the fs into allowing us to create
		 * the file */
		current->fsuid = hidden_parent_dentry->d_inode->i_uid;
		current->fsgid = hidden_parent_dentry->d_inode->i_gid;
		//found a.wh.foo entry, remove it then do vfs_mkdir
		if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
			err =
			    vfs_unlink(hidden_parent_dentry->d_inode,
				       whiteout_dentry);
		}
		DPUT(whiteout_dentry);

		current->fsuid = saved_uid;
		current->fsgid = saved_gid;

		unlock_dir(hidden_parent_dentry);

		if (err) {
			/* exit if the error returned was NOT -EROFS */
			if (!IS_COPYUP_ERR(err))
				goto out;
			bstart--;
		} else {
			whiteout_unlinked = 1;
		}
	}
	
	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			hidden_dentry = create_parents(parent, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				fist_dprint(8,
					    "hidden dentry NULL for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			err =
			    vfs_mkdir(hidden_parent_dentry->d_inode,
				      hidden_dentry, mode);
		}
		unlock_dir(hidden_parent_dentry);

		/* XXX this could potentially return a negative hidden_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 {
			int i;
			int bend = dbend(dentry);
			
			for (i = bindex + 1; i < bend; i++) {
				if (dtohd_index(dentry, i)) {
					DPUT(dtohd_index(dentry, i));
					set_dtohd_index(dentry, i, NULL);
				}
			}
			bend = bindex;
			set_dbend(dentry, bend);

			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);
			}
			whiteout_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE,
							 hidden_dentry,
							 sizeof
							 (UNIONFS_DIR_OPAQUE) -
							 1);
			if (IS_ERR(whiteout_dentry)) {
				err = PTR_ERR(whiteout_dentry);
				goto out;
			}
			down(&hidden_dentry->d_inode->i_sem);
			err = vfs_create(hidden_dentry->d_inode,
					 whiteout_dentry, 0600, NULL);
			up(&hidden_dentry->d_inode->i_sem);
			DPUT(whiteout_dentry);

			if (err) {
				fist_dprint(8,
					    "mkdir: error creating directory override entry: %d\n",
					    err);
				goto out;
			}
			break;
		}
	}

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

	KFREE(name);

	fist_print_dentry("OUT unionfs_mkdir :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 21
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;
}
Exemplo n.º 22
0
static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
			 dev_t dev)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;
	int whiteout_unlinked = 0;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_mknod", dentry);
	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);
		goto out;
	}

	if (!whiteout_dentry->d_inode) {
		DPUT(whiteout_dentry);
		whiteout_dentry = NULL;
	} else {
		/* found .wh.foo, unlink it */
		hidden_parent_dentry = lock_parent(whiteout_dentry);

		//found a.wh.foo entry, remove it then do vfs_mkdir
		if (!(err = is_robranch_super(dentry->d_sb, bstart)))
			err = vfs_unlink(hidden_parent_dentry->d_inode,
					 whiteout_dentry);
		DPUT(whiteout_dentry);

		unlock_dir(hidden_parent_dentry);

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

			bstart--;
		} else {
			whiteout_unlinked = 1;
		}
	}

	for (bindex = bstart; bindex >= 0; bindex--) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			hidden_dentry = create_parents(dir, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				fist_dprint(8,
					    "hidden dentry NULL for bindex = %d\n",
					    bindex);
				continue;
			}
		}

		hidden_parent_dentry = lock_parent(hidden_dentry);
		if (IS_ERR(hidden_parent_dentry)) {
			err = PTR_ERR(hidden_parent_dentry);
			goto out;
		}
		if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
			err = vfs_mknod(hidden_parent_dentry->d_inode,
					hidden_dentry, mode, dev);
		}
		/* XXX this could potentially return a negative hidden_dentry! */
		if (err || !hidden_dentry->d_inode) {
			unlock_dir(hidden_parent_dentry);
			/* break out of for, if error 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_parent_dentry->
							 d_inode);
				/* update number of links on parent directory */
				dir->i_nlink = get_nlinks(dir);
			}
			unlock_dir(hidden_parent_dentry);

			break;
		}
	}

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

	if (name) {
		KFREE(name);
	}

	fist_print_dentry("OUT unionfs_mknod :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
Exemplo n.º 23
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;
}
Exemplo n.º 24
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;
}