Пример #1
0
/* set lower inode ptr and update bstart & bend if necessary */
static void __set_inode(struct dentry *upper, struct dentry *lower,
			int bindex)
{
	unionfs_set_lower_inode_idx(upper->d_inode, bindex,
				    igrab(lower->d_inode));
	if (likely(ibstart(upper->d_inode) > bindex))
		ibstart(upper->d_inode) = bindex;
	if (likely(ibend(upper->d_inode) < bindex))
		ibend(upper->d_inode) = bindex;

}
Пример #2
0
/* copy a/m/ctime from the lower branch with the newest times */
void unionfs_copy_attr_times(struct inode *upper)
{
	int bindex;
	struct inode *lower;

	if (!upper)
		return;
	if (ibstart(upper) < 0) {
#ifdef CONFIG_UNION_FS_DEBUG
		WARN_ON(ibstart(upper) < 0);
#endif /* CONFIG_UNION_FS_DEBUG */
		return;
	}
	for (bindex = ibstart(upper); bindex <= ibend(upper); bindex++) {
		lower = unionfs_lower_inode_idx(upper, bindex);
		if (!lower)
			continue; /* not all lower dir objects may exist */
		if (unlikely(timespec_compare(&upper->i_mtime,
					      &lower->i_mtime) < 0))
			upper->i_mtime = lower->i_mtime;
		if (unlikely(timespec_compare(&upper->i_ctime,
					      &lower->i_ctime) < 0))
			upper->i_ctime = lower->i_ctime;
		if (unlikely(timespec_compare(&upper->i_atime,
					      &lower->i_atime) < 0))
			upper->i_atime = lower->i_atime;
	}
}
Пример #3
0
/* like interpose above, but for an already existing dentry */
void unionfs_reinterpose(struct dentry *dentry)
{
    struct dentry *lower_dentry;
    struct inode *inode;
    int bindex, bstart, bend;

    verify_locked(dentry);

    /* This is pre-allocated inode */
    inode = dentry->d_inode;

    bstart = dbstart(dentry);
    bend = dbend(dentry);
    for (bindex = bstart; bindex <= bend; bindex++) {
        lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
        if (!lower_dentry)
            continue;

        if (!lower_dentry->d_inode)
            continue;
        if (unionfs_lower_inode_idx(inode, bindex))
            continue;
        unionfs_set_lower_inode_idx(inode, bindex,
                                    igrab(lower_dentry->d_inode));
    }
    ibstart(inode) = dbstart(dentry);
    ibend(inode) = dbend(dentry);
}
Пример #4
0
void unionfs_reinterpose(struct dentry *dentry)
{
	struct dentry *hidden_dentry;
	struct inode *inode;
	int bindex, bstart, bend;

	print_entry_location();
	verify_locked(dentry);
	fist_print_dentry("IN: unionfs_reinterpose: ", dentry);

	/* This is pre-allocated inode */
	inode = dentry->d_inode;

	bstart = dbstart(dentry);
	bend = dbend(dentry);
	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry)
			continue;

		if (!hidden_dentry->d_inode)
			continue;
		if (itohi_index(inode, bindex))
			continue;
		set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode));
	}
	ibstart(inode) = dbstart(dentry);
	ibend(inode) = dbend(dentry);

	fist_print_dentry("OUT: unionfs_reinterpose: ", dentry);
	fist_print_inode("OUT: unionfs_reinterpose: ", inode);

	print_exit_location();
}
Пример #5
0
/*
 * return to user-space the branch indices containing the file in question
 *
 * We use fd_set and therefore we are limited to the number of the branches
 * to FD_SETSIZE, which is currently 1024 - plenty for most people
 */
static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent,
				   unsigned int cmd, unsigned long arg)
{
	int err = 0;
	fd_set branchlist;
	int bstart = 0, bend = 0, bindex = 0;
	int orig_bstart, orig_bend;
	struct dentry *dentry, *lower_dentry;
	struct vfsmount *mnt;

	dentry = file->f_path.dentry;
	orig_bstart = dbstart(dentry);
	orig_bend = dbend(dentry);
	err = unionfs_partial_lookup(dentry, parent);
	if (err)
		goto out;
	bstart = dbstart(dentry);
	bend = dbend(dentry);

	FD_ZERO(&branchlist);

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry)
			continue;
		if (likely(lower_dentry->d_inode))
			FD_SET(bindex, &branchlist);
		/* purge any lower objects after partial_lookup */
		if (bindex < orig_bstart || bindex > orig_bend) {
			dput(lower_dentry);
			unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
			iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
			unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
						    NULL);
			mnt = unionfs_lower_mnt_idx(dentry, bindex);
			if (!mnt)
				continue;
			unionfs_mntput(dentry, bindex);
			unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
		}
	}
	/* restore original dentry's offsets */
	dbstart(dentry) = orig_bstart;
	dbend(dentry) = orig_bend;
	ibstart(dentry->d_inode) = orig_bstart;
	ibend(dentry->d_inode) = orig_bend;

	err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set));
	if (unlikely(err))
		err = -EFAULT;

out:
	return err < 0 ? err : bend;
}
Пример #6
0
static void unionfs_fill_inode(struct dentry *dentry,
                               struct inode *inode)
{
    struct inode *lower_inode;
    struct dentry *lower_dentry;
    int bindex, bstart, bend;

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

    for (bindex = bstart; bindex <= bend; bindex++) {
        lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
        if (!lower_dentry) {
            unionfs_set_lower_inode_idx(inode, bindex, NULL);
            continue;
        }

        /* Initialize the lower inode to the new lower inode. */
        if (!lower_dentry->d_inode)
            continue;

        unionfs_set_lower_inode_idx(inode, bindex,
                                    igrab(lower_dentry->d_inode));
    }

    ibstart(inode) = dbstart(dentry);
    ibend(inode) = dbend(dentry);

    /* Use attributes from the first branch. */
    lower_inode = unionfs_lower_inode(inode);

    /* Use different set of inode ops for symlinks & directories */
    if (S_ISLNK(lower_inode->i_mode))
        inode->i_op = &unionfs_symlink_iops;
    else if (S_ISDIR(lower_inode->i_mode))
        inode->i_op = &unionfs_dir_iops;

    /* Use different set of file ops for directories */
    if (S_ISDIR(lower_inode->i_mode))
        inode->i_fop = &unionfs_dir_fops;

    /* properly initialize special inodes */
    if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
            S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
        init_special_inode(inode, lower_inode->i_mode,
                           lower_inode->i_rdev);

    /* all well, copy inode attributes */
    unionfs_copy_attr_all(inode, lower_inode);
    fsstack_copy_inode_size(inode, lower_inode);
}
Пример #7
0
/*
 * Post-copyup helper to release all non-directory source objects of a
 * copied-up file.  Regular files should have only one lower object.
 */
void unionfs_postcopyup_release(struct dentry *dentry)
{
	int bstart, bend;

	BUG_ON(S_ISDIR(dentry->d_inode->i_mode));
	bstart = dbstart(dentry);
	bend = dbend(dentry);

	path_put_lowers(dentry, bstart + 1, bend, false);
	iput_lowers(dentry->d_inode, bstart + 1, bend, false);

	dbend(dentry) = bstart;
	ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart;
}
Пример #8
0
static int unionfs_permission(struct inode *inode, int mask,
			      struct nameidata *nd)
{
	struct inode *hidden_inode = NULL;
	int err = 0;
	int bindex, bstart, bend;
	const int is_file = !S_ISDIR(inode->i_mode);
	const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);

	print_entry_location();

	bstart = ibstart(inode);
	bend = ibend(inode);

	fist_print_inode("IN unionfs_permission", inode);

	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_inode = itohi_index(inode, bindex);
		if (!hidden_inode)
			continue;

		/* check the condition for D-F-D underlying files/directories,
		 * we dont have to check for files, if we are checking for
		 * directories.
		 */
		if (!is_file && !S_ISDIR(hidden_inode->i_mode))
			continue;
		/* We use our own special version of permission, such that 
		 * only the first branch returns -EROFS. */
		err = inode_permission(hidden_inode, mask, nd, bindex);
		/* The permissions are an intersection of the overall directory
		 * permissions, so we fail if one fails. */
		if (err)
			goto out;
		/* only the leftmost file matters. */
		if (is_file || write_mask) {
			if (is_file && write_mask) {
				err = get_write_access(hidden_inode);
				if (!err)
					put_write_access(hidden_inode);
			}
			break;
		}
	}

      out:
	print_exit_status(err);
	return err;
}
Пример #9
0
/* perform a delayed copyup of a read-write file on a read-only branch */
static int do_delayed_copyup(struct file *file, struct dentry *parent)
{
	int bindex, bstart, bend, err = 0;
	struct dentry *dentry = file->f_path.dentry;
	struct inode *parent_inode = parent->d_inode;

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

	BUG_ON(!S_ISREG(dentry->d_inode->i_mode));

	unionfs_check_file(file);
	for (bindex = bstart - 1; bindex >= 0; bindex--) {
		if (!d_deleted(dentry))
			err = copyup_file(parent_inode, file, bstart,
					  bindex,
					  i_size_read(dentry->d_inode));
		else
			err = copyup_deleted_file(file, dentry, parent,
						  bstart, bindex);
		/* if succeeded, set lower open-file flags and break */
		if (!err) {
			struct file *lower_file;
			lower_file = unionfs_lower_file_idx(file, bindex);
			lower_file->f_flags = file->f_flags;
			break;
		}
	}
	if (err || (bstart <= fbstart(file)))
		goto out;
	bend = fbend(file);
	for (bindex = bstart; bindex <= bend; bindex++) {
		if (unionfs_lower_file_idx(file, bindex)) {
			branchput(dentry->d_sb, bindex);
			fput(unionfs_lower_file_idx(file, bindex));
			unionfs_set_lower_file_idx(file, bindex, NULL);
		}
	}
	path_put_lowers(dentry, bstart, bend, false);
	iput_lowers(dentry->d_inode, bstart, bend, false);
	/* for reg file, we only open it "once" */
	fbend(file) = fbstart(file);
	dbend(dentry) = dbstart(dentry);
	ibend(dentry->d_inode) = ibstart(dentry->d_inode);

out:
	unionfs_check_file(file);
	return err;
}
Пример #10
0
void __show_inode_times(const struct inode *inode,
			const char *file, const char *fxn, int line)
{
	struct inode *lower_inode;
	int bindex;

	for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
		lower_inode = unionfs_lower_inode_idx(inode, bindex);
		if (unlikely(!lower_inode))
			continue;
		pr_debug("IT(%lu:%d): %s:%s:%d "
			 "um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\n",
			 inode->i_ino, bindex,
			 file, fxn, line,
			 inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,
			 lower_inode->i_mtime.tv_sec,
			 lower_inode->i_mtime.tv_nsec,
			 inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,
			 lower_inode->i_ctime.tv_sec,
			 lower_inode->i_ctime.tv_nsec);
	}
}
Пример #11
0
/* This function replicates the directory structure upto given dentry
 * in the bindex branch.  */
struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry,
				    const char *name, int bindex)
{
	int err;
	struct dentry *child_dentry;
	struct dentry *parent_dentry;
	struct dentry *hidden_parent_dentry = NULL;
	struct dentry *hidden_dentry = NULL;
	const char *childname;
	unsigned int childnamelen;

	int old_kmalloc_size;
	int kmalloc_size;
	int num_dentry;
	int count;

	int old_bstart;
	int old_bend;
	struct dentry **path = NULL;
	struct dentry **tmp_path;

	print_entry_location();

	verify_locked(dentry);

	/* There is no sense allocating any less than the minimum. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	kmalloc_size = malloc_sizes[0].cs_size;
#else
	kmalloc_size = 32;
#endif
	num_dentry = kmalloc_size / sizeof(struct dentry *);

	if ((err = is_robranch_super(dir->i_sb, bindex))) {
		hidden_dentry = ERR_PTR(err);
		goto out;
	}

	fist_print_dentry("IN: create_parents_named", dentry);
	fist_dprint(8, "name = %s\n", name);

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

	path = (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL);
	memset(path, 0, kmalloc_size);

	/* assume the negative dentry of unionfs as the parent dentry */
	parent_dentry = dentry;

	count = 0;
	/* This loop finds the first parent that exists in the given branch.
	 * We start building the directory structure from there.  At the end
	 * of the loop, the following should hold:
	 *      child_dentry is the first nonexistent child
	 *      parent_dentry is the first existent parent
	 *      path[0] is the = deepest child
	 *      path[count] is the first child to create
	 */
	do {
		child_dentry = parent_dentry;

		/* find the parent directory dentry in unionfs */
		parent_dentry = child_dentry->d_parent;
		lock_dentry(parent_dentry);

		/* find out the hidden_parent_dentry in the given branch */
		hidden_parent_dentry = dtohd_index(parent_dentry, bindex);

		/* store the child dentry */
		path[count++] = child_dentry;
		if (count == num_dentry) {
			old_kmalloc_size = kmalloc_size;
			kmalloc_size *= 2;
			num_dentry = kmalloc_size / sizeof(struct dentry *);

			tmp_path =
			    (struct dentry **)KMALLOC(kmalloc_size, GFP_KERNEL);
			if (!tmp_path) {
				hidden_dentry = ERR_PTR(-ENOMEM);
				goto out;
			}
			memset(tmp_path, 0, kmalloc_size);
			memcpy(tmp_path, path, old_kmalloc_size);
			KFREE(path);
			path = tmp_path;
			tmp_path = NULL;
		}

	} while (!hidden_parent_dentry);
	count--;

	/* This is basically while(child_dentry != dentry).  This loop is
	 * horrible to follow and should be replaced with cleaner code. */
	while (1) {
		PASSERT(child_dentry);
		PASSERT(parent_dentry);
		PASSERT(parent_dentry->d_inode);

		// get hidden parent dir in the current branch
		hidden_parent_dentry = dtohd_index(parent_dentry, bindex);
		unlock_dentry(parent_dentry);
		PASSERT(hidden_parent_dentry);
		PASSERT(hidden_parent_dentry->d_inode);

		// init the values to lookup
		childname = child_dentry->d_name.name;
		childnamelen = child_dentry->d_name.len;

		if (child_dentry != dentry) {
			// lookup child in the underlying file system
			hidden_dentry =
			    LOOKUP_ONE_LEN(childname, hidden_parent_dentry,
					   childnamelen);
			if (IS_ERR(hidden_dentry))
				goto out;
		} else {
			int loop_start;
			int loop_end;
			int new_bstart = -1;
			int new_bend = -1;
			int i;

			/* is the name a whiteout of the childname ? */
			//lookup the whiteout child in the underlying file system
			hidden_dentry =
			    LOOKUP_ONE_LEN(name, hidden_parent_dentry,
					   strlen(name));
			if (IS_ERR(hidden_dentry))
				goto out;

			/* Replace the current dentry (if any) with the new one. */
			DPUT(dtohd_index(dentry, bindex));
			set_dtohd_index(dentry, bindex, hidden_dentry);

			loop_start =
			    (old_bstart < bindex) ? old_bstart : bindex;
			loop_end = (old_bend > bindex) ? old_bend : bindex;

			/* This loop sets the bstart and bend for the new
			 * dentry by traversing from left to right.
			 * It also dputs all negative dentries except
			 * bindex (the newly looked dentry
			 */
			for (i = loop_start; i <= loop_end; i++) {
				if (!dtohd_index(dentry, i))
					continue;

				if (i == bindex) {
					new_bend = i;
					if (new_bstart < 0)
						new_bstart = i;
					continue;
				}

				if (!dtohd_index(dentry, i)->d_inode) {
					DPUT(dtohd_index(dentry, i));
					set_dtohd_index(dentry, i, NULL);
				} else {
					if (new_bstart < 0)
						new_bstart = i;
					new_bend = i;
				}
			}

			if (new_bstart < 0)
				new_bstart = bindex;
			if (new_bend < 0)
				new_bend = bindex;
			set_dbstart(dentry, new_bstart);
			set_dbend(dentry, new_bend);
			break;
		}

		if (hidden_dentry->d_inode) {
			/* since this already exists we dput to avoid
			 * multiple references on the same dentry */
			DPUT(hidden_dentry);
		} else {
			uid_t saved_uid = current->fsuid;
			gid_t saved_gid = current->fsgid;

			/* its a negative dentry, create a new dir */
			hidden_parent_dentry = lock_parent(hidden_dentry);
			current->fsuid = hidden_parent_dentry->d_inode->i_uid;
			current->fsgid = hidden_parent_dentry->d_inode->i_gid;
			err = vfs_mkdir(hidden_parent_dentry->d_inode,
					hidden_dentry, S_IRWXUGO);
			current->fsuid = saved_uid;
			current->fsgid = saved_gid;
			unlock_dir(hidden_parent_dentry);
			if (err || !hidden_dentry->d_inode) {
				DPUT(hidden_dentry);
				hidden_dentry = ERR_PTR(err);
				goto out;
			}
			err =
			    copyup_permissions(dir->i_sb, child_dentry,
					       hidden_dentry);
			if (err) {
				DPUT(hidden_dentry);
				hidden_dentry = ERR_PTR(err);
				goto out;
			}
			set_itohi_index(child_dentry->d_inode, bindex,
					igrab(hidden_dentry->d_inode));
			if (ibstart(child_dentry->d_inode) > bindex)
				ibstart(child_dentry->d_inode) = bindex;
			if (ibend(child_dentry->d_inode) < bindex)
				ibend(child_dentry->d_inode) = bindex;

			set_dtohd_index(child_dentry, bindex, hidden_dentry);
			if (dbstart(child_dentry) > bindex)
				set_dbstart(child_dentry, bindex);
			if (dbend(child_dentry) < bindex)
				set_dbend(child_dentry, bindex);
		}

		parent_dentry = child_dentry;
		child_dentry = path[--count];
	}
      out:
	KFREE(path);
	fist_print_dentry("OUT: create_parents_named", dentry);
	print_exit_pointer(hidden_dentry);
	return hidden_dentry;
}
Пример #12
0
/*
 * __unionfs_check_{inode,dentry,file} perform exhaustive sanity checking on
 * the fan-out of various Unionfs objects.  We check that no lower objects
 * exist  outside the start/end branch range; that all objects within are
 * non-NULL (with some allowed exceptions); that for every lower file
 * there's a lower dentry+inode; that the start/end ranges match for all
 * corresponding lower objects; that open files/symlinks have only one lower
 * objects, but directories can have several; and more.
 */
void __unionfs_check_inode(const struct inode *inode,
			   const char *fname, const char *fxn, int line)
{
	int bindex;
	int istart, iend;
	struct inode *lower_inode;
	struct super_block *sb;
	int printed_caller = 0;
	void *poison_ptr;

	/* for inodes now */
	BUG_ON(!inode);
	sb = inode->i_sb;
	istart = ibstart(inode);
	iend = ibend(inode);
	/* don't check inode if no lower branches */
	if (istart < 0 && iend < 0)
		return;
	if (unlikely(istart > iend)) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" Ci0: inode=%p istart/end=%d:%d\n",
			 inode, istart, iend);
	}
	if (unlikely((istart == -1 && iend != -1) ||
		     (istart != -1 && iend == -1))) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" Ci1: inode=%p istart/end=%d:%d\n",
			 inode, istart, iend);
	}
	if (!S_ISDIR(inode->i_mode)) {
		if (unlikely(iend != istart)) {
			PRINT_CALLER(fname, fxn, line);
			pr_debug(" Ci2: inode=%p istart=%d iend=%d\n",
				 inode, istart, iend);
		}
	}

	for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {
		if (unlikely(!UNIONFS_I(inode))) {
			PRINT_CALLER(fname, fxn, line);
			pr_debug(" Ci3: no inode_info %p\n", inode);
			return;
		}
		if (unlikely(!UNIONFS_I(inode)->lower_inodes)) {
			PRINT_CALLER(fname, fxn, line);
			pr_debug(" Ci4: no lower_inodes %p\n", inode);
			return;
		}
		lower_inode = unionfs_lower_inode_idx(inode, bindex);
		if (lower_inode) {
			memset(&poison_ptr, POISON_INUSE, sizeof(void *));
			if (unlikely(bindex < istart || bindex > iend)) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" Ci5: inode/linode=%p:%p bindex=%d "
					 "istart/end=%d:%d\n", inode,
					 lower_inode, bindex, istart, iend);
			} else if (unlikely(lower_inode == poison_ptr)) {
				/* freed inode! */
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" Ci6: inode/linode=%p:%p bindex=%d "
					 "istart/end=%d:%d\n", inode,
					 lower_inode, bindex, istart, iend);
			}
			continue;
		}
		/* if we get here, then lower_inode == NULL */
		if (bindex < istart || bindex > iend)
			continue;
		/*
		 * directories can have NULL lower inodes in b/t start/end,
		 * but NOT if at the start/end range.
		 */
		if (unlikely(S_ISDIR(inode->i_mode) &&
			     bindex > istart && bindex < iend))
			continue;
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" Ci7: inode/linode=%p:%p "
			 "bindex=%d istart/end=%d:%d\n",
			 inode, lower_inode, bindex, istart, iend);
	}
}
Пример #13
0
void __unionfs_check_dentry(const struct dentry *dentry,
			    const char *fname, const char *fxn, int line)
{
	int bindex;
	int dstart, dend, istart, iend;
	struct dentry *lower_dentry;
	struct inode *inode, *lower_inode;
	struct super_block *sb;
	struct vfsmount *lower_mnt;
	int printed_caller = 0;
	void *poison_ptr;

	BUG_ON(!dentry);
	sb = dentry->d_sb;
	inode = dentry->d_inode;
	dstart = dbstart(dentry);
	dend = dbend(dentry);
	/* don't check dentry/mnt if no lower branches */
	if (dstart < 0 && dend < 0)
		goto check_inode;
	BUG_ON(dstart > dend);

	if (unlikely((dstart == -1 && dend != -1) ||
		     (dstart != -1 && dend == -1))) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" CD0: dentry=%p dstart/end=%d:%d\n",
			 dentry, dstart, dend);
	}
	/*
	 * check for NULL dentries inside the start/end range, or
	 * non-NULL dentries outside the start/end range.
	 */
	for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (lower_dentry) {
			if (unlikely(bindex < dstart || bindex > dend)) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CD1: dentry/lower=%p:%p(%p) "
					 "bindex=%d dstart/end=%d:%d\n",
					 dentry, lower_dentry,
					 (lower_dentry ? lower_dentry->d_inode :
					  (void *) -1L),
					 bindex, dstart, dend);
			}
		} else {	/* lower_dentry == NULL */
			if (bindex < dstart || bindex > dend)
				continue;
			/*
			 * Directories can have NULL lower inodes in b/t
			 * start/end, but NOT if at the start/end range.
			 * Ignore this rule, however, if this is a NULL
			 * dentry or a deleted dentry.
			 */
			if (unlikely(!d_deleted((struct dentry *) dentry) &&
				     inode &&
				     !(inode && S_ISDIR(inode->i_mode) &&
				       bindex > dstart && bindex < dend))) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CD2: dentry/lower=%p:%p(%p) "
					 "bindex=%d dstart/end=%d:%d\n",
					 dentry, lower_dentry,
					 (lower_dentry ?
					  lower_dentry->d_inode :
					  (void *) -1L),
					 bindex, dstart, dend);
			}
		}
	}

	/* check for vfsmounts same as for dentries */
	for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {
		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
		if (lower_mnt) {
			if (unlikely(bindex < dstart || bindex > dend)) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CM0: dentry/lmnt=%p:%p bindex=%d "
					 "dstart/end=%d:%d\n", dentry,
					 lower_mnt, bindex, dstart, dend);
			}
		} else {	/* lower_mnt == NULL */
			if (bindex < dstart || bindex > dend)
				continue;
			/*
			 * Directories can have NULL lower inodes in b/t
			 * start/end, but NOT if at the start/end range.
			 * Ignore this rule, however, if this is a NULL
			 * dentry.
			 */
			if (unlikely(inode &&
				     !(inode && S_ISDIR(inode->i_mode) &&
				       bindex > dstart && bindex < dend))) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CM1: dentry/lmnt=%p:%p "
					 "bindex=%d dstart/end=%d:%d\n",
					 dentry, lower_mnt, bindex,
					 dstart, dend);
			}
		}
	}

check_inode:
	/* for inodes now */
	if (!inode)
		return;
	istart = ibstart(inode);
	iend = ibend(inode);
	/* don't check inode if no lower branches */
	if (istart < 0 && iend < 0)
		return;
	BUG_ON(istart > iend);
	if (unlikely((istart == -1 && iend != -1) ||
		     (istart != -1 && iend == -1))) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" CI0: dentry/inode=%p:%p istart/end=%d:%d\n",
			 dentry, inode, istart, iend);
	}
	if (unlikely(istart != dstart)) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" CI1: dentry/inode=%p:%p istart=%d dstart=%d\n",
			 dentry, inode, istart, dstart);
	}
	if (unlikely(iend != dend)) {
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" CI2: dentry/inode=%p:%p iend=%d dend=%d\n",
			 dentry, inode, iend, dend);
	}

	if (!S_ISDIR(inode->i_mode)) {
		if (unlikely(dend != dstart)) {
			PRINT_CALLER(fname, fxn, line);
			pr_debug(" CI3: dentry/inode=%p:%p dstart=%d dend=%d\n",
				 dentry, inode, dstart, dend);
		}
		if (unlikely(iend != istart)) {
			PRINT_CALLER(fname, fxn, line);
			pr_debug(" CI4: dentry/inode=%p:%p istart=%d iend=%d\n",
				 dentry, inode, istart, iend);
		}
	}

	for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {
		lower_inode = unionfs_lower_inode_idx(inode, bindex);
		if (lower_inode) {
			memset(&poison_ptr, POISON_INUSE, sizeof(void *));
			if (unlikely(bindex < istart || bindex > iend)) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CI5: dentry/linode=%p:%p bindex=%d "
					 "istart/end=%d:%d\n", dentry,
					 lower_inode, bindex, istart, iend);
			} else if (unlikely(lower_inode == poison_ptr)) {
				/* freed inode! */
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" CI6: dentry/linode=%p:%p bindex=%d "
					 "istart/end=%d:%d\n", dentry,
					 lower_inode, bindex, istart, iend);
			}
			continue;
		}
		/* if we get here, then lower_inode == NULL */
		if (bindex < istart || bindex > iend)
			continue;
		/*
		 * directories can have NULL lower inodes in b/t start/end,
		 * but NOT if at the start/end range.
		 */
		if (unlikely(S_ISDIR(inode->i_mode) &&
			     bindex > istart && bindex < iend))
			continue;
		PRINT_CALLER(fname, fxn, line);
		pr_debug(" CI7: dentry/linode=%p:%p "
			 "bindex=%d istart/end=%d:%d\n",
			 dentry, lower_inode, bindex, istart, iend);
	}

	/*
	 * If it's a directory, then intermediate objects b/t start/end can
	 * be NULL.  But, check that all three are NULL: lower dentry, mnt,
	 * and inode.
	 */
	if (dstart >= 0 && dend >= 0 && S_ISDIR(inode->i_mode))
		for (bindex = dstart+1; bindex < dend; bindex++) {
			lower_inode = unionfs_lower_inode_idx(inode, bindex);
			lower_dentry = unionfs_lower_dentry_idx(dentry,
								bindex);
			lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);
			if (unlikely(!((lower_inode && lower_dentry &&
					lower_mnt) ||
				       (!lower_inode &&
					!lower_dentry && !lower_mnt)))) {
				PRINT_CALLER(fname, fxn, line);
				pr_debug(" Cx: lmnt/ldentry/linode=%p:%p:%p "
					 "bindex=%d dstart/end=%d:%d\n",
					 lower_mnt, lower_dentry, lower_inode,
					 bindex, dstart, dend);
			}
		}
	/* check if lower inode is newer than upper one (it shouldn't) */
	if (unlikely(is_newer_lower(dentry) && !is_negative_lower(dentry))) {
		PRINT_CALLER(fname, fxn, line);
		for (bindex = ibstart(inode); bindex <= ibend(inode);
		     bindex++) {
			lower_inode = unionfs_lower_inode_idx(inode, bindex);
			if (unlikely(!lower_inode))
				continue;
			pr_debug(" CI8: bindex=%d mtime/lmtime=%lu.%lu/%lu.%lu "
				 "ctime/lctime=%lu.%lu/%lu.%lu\n",
				 bindex,
				 inode->i_mtime.tv_sec,
				 inode->i_mtime.tv_nsec,
				 lower_inode->i_mtime.tv_sec,
				 lower_inode->i_mtime.tv_nsec,
				 inode->i_ctime.tv_sec,
				 inode->i_ctime.tv_nsec,
				 lower_inode->i_ctime.tv_sec,
				 lower_inode->i_ctime.tv_nsec);
		}
	}
}
Пример #14
0
/*
 * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise.
 */
int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	int valid = 1;		/* default is valid (1); invalid is 0. */
	struct dentry *hidden_dentry;
	int bindex, bstart, bend;
	int sbgen, dgen;
	int positive = 0;
	int locked = 0;
	int restart = 0;
	int interpose_flag;

	print_util_entry_location();

      restart:
	verify_locked(dentry);

	/* if the dentry is unhashed, do NOT revalidate */
	if (d_deleted(dentry)) {
		fist_dprint(6, "unhashed dentry being revalidated: %*s\n",
			    dentry->d_name.len, dentry->d_name.name);
		goto out;
	}

	BUG_ON(dbstart(dentry) == -1);
	if (dentry->d_inode)
		positive = 1;
	dgen = atomic_read(&dtopd(dentry)->udi_generation);
	sbgen = atomic_read(&stopd(dentry->d_sb)->usi_generation);
	/* If we are working on an unconnected dentry, then there is no
	 * revalidation to be done, because this file does not exist within the
	 * namespace, and Unionfs operates on the namespace, not data.
	 */
	if (sbgen != dgen) {
		struct dentry *result;
		int pdgen;

		unionfs_read_lock(dentry->d_sb);
		locked = 1;

		/* The root entry should always be valid */
		BUG_ON(IS_ROOT(dentry));

		/* We can't work correctly if our parent isn't valid. */
		pdgen = atomic_read(&dtopd(dentry->d_parent)->udi_generation);
		if (!restart && (pdgen != sbgen)) {
			unionfs_read_unlock(dentry->d_sb);
			locked = 0;
			/* We must be locked before our parent. */
			if (!
			    (dentry->d_parent->d_op->
			     d_revalidate(dentry->d_parent, nd))) {
				valid = 0;
				goto out;
			}
			restart = 1;
			goto restart;
		}
		BUG_ON(pdgen != sbgen);

		/* Free the pointers for our inodes and this dentry. */
		bstart = dbstart(dentry);
		bend = dbend(dentry);
		if (bstart >= 0) {
			struct dentry *hidden_dentry;
			for (bindex = bstart; bindex <= bend; bindex++) {
				hidden_dentry =
				    dtohd_index_nocheck(dentry, bindex);
				if (!hidden_dentry)
					continue;
				DPUT(hidden_dentry);
			}
		}
		set_dbstart(dentry, -1);
		set_dbend(dentry, -1);

		interpose_flag = INTERPOSE_REVAL_NEG;
		if (positive) {
			interpose_flag = INTERPOSE_REVAL;
			down(&dentry->d_inode->i_sem);
			bstart = ibstart(dentry->d_inode);
			bend = ibend(dentry->d_inode);
			if (bstart >= 0) {
				struct inode *hidden_inode;
				for (bindex = bstart; bindex <= bend; bindex++) {
					hidden_inode =
					    itohi_index(dentry->d_inode,
							bindex);
					if (!hidden_inode)
						continue;
					IPUT(hidden_inode);
				}
			}
			KFREE(itohi_ptr(dentry->d_inode));
			itohi_ptr(dentry->d_inode) = NULL;
			ibstart(dentry->d_inode) = -1;
			ibend(dentry->d_inode) = -1;
			up(&dentry->d_inode->i_sem);
		}

		result = unionfs_lookup_backend(dentry, interpose_flag);
		if (result) {
			if (IS_ERR(result)) {
				valid = 0;
				goto out;
			}
			/* current unionfs_lookup_backend() doesn't return
			   a valid dentry */
			DPUT(dentry);
			dentry = result;
		}

		if (positive && itopd(dentry->d_inode)->uii_stale) {
			make_stale_inode(dentry->d_inode);
			d_drop(dentry);
			valid = 0;
			goto out;
		}
		goto out;
	}

	/* The revalidation must occur across all branches */
	bstart = dbstart(dentry);
	bend = dbend(dentry);
	BUG_ON(bstart == -1);
	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry || !hidden_dentry->d_op
		    || !hidden_dentry->d_op->d_revalidate)
			continue;

		if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd))
			valid = 0;
	}

	if (!dentry->d_inode)
		valid = 0;
	if (valid)
		fist_copy_attr_all(dentry->d_inode, itohi(dentry->d_inode));

      out:
	if (locked)
		unionfs_read_unlock(dentry->d_sb);
	fist_print_dentry("revalidate out", dentry);
	print_util_exit_status(valid);
	return valid;
}
Пример #15
0
/* sb we pass is unionfs's super_block */
int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
{
	struct inode *hidden_inode;
	struct dentry *hidden_dentry;
	int err = 0;
	struct inode *inode;
	int is_negative_dentry = 1;
	int bindex, bstart, bend;

	print_entry("flag = %d", flag);

	verify_locked(dentry);

	fist_print_dentry("In unionfs_interpose", dentry);

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

	/* Make sure that we didn't get a negative dentry. */
	for (bindex = bstart; bindex <= bend; bindex++) {
		if (dtohd_index(dentry, bindex) &&
		    dtohd_index(dentry, bindex)->d_inode) {
			is_negative_dentry = 0;
			break;
		}
	}
	BUG_ON(is_negative_dentry);

	/* We allocate our new inode below, by calling iget.
	 * iget will call our read_inode which will initialize some
	 * of the new inode's fields
	 */

	/* On revalidate we've already got our own inode and just need
	 * to fix it up. */
	if (flag == INTERPOSE_REVAL) {
		inode = dentry->d_inode;
		itopd(inode)->b_start = -1;
		itopd(inode)->b_end = -1;
		atomic_set(&itopd(inode)->uii_generation,
			   atomic_read(&stopd(sb)->usi_generation));

		itohi_ptr(inode) =
		    KZALLOC(sbmax(sb) * sizeof(struct inode *), GFP_KERNEL);
		if (!itohi_ptr(inode)) {
			err = -ENOMEM;
			goto out;
		}
	} else {
		ino_t ino;
		/* get unique inode number for unionfs */
#ifdef UNIONFS_IMAP
		if (stopd(sb)->usi_persistent) {
			err = read_uin(sb, bindex,
				       dtohd_index(dentry,
						   bindex)->d_inode->i_ino,
				       O_CREAT, &ino);
			if (err)
				goto out;
		} else
#endif
			ino = iunique(sb, UNIONFS_ROOT_INO);

		inode = IGET(sb, ino);
		if (!inode) {
			err = -EACCES;	/* should be impossible??? */
			goto out;
		}
	}

	down(&inode->i_sem);
	if (atomic_read(&inode->i_count) > 1)
		goto skip;

	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = dtohd_index(dentry, bindex);
		if (!hidden_dentry) {
			set_itohi_index(inode, bindex, NULL);
			continue;
		}
		/* Initialize the hidden inode to the new hidden inode. */
		if (!hidden_dentry->d_inode)
			continue;
		set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode));
	}

	ibstart(inode) = dbstart(dentry);
	ibend(inode) = dbend(dentry);

	/* Use attributes from the first branch. */
	hidden_inode = itohi(inode);

	/* Use different set of inode ops for symlinks & directories */
	if (S_ISLNK(hidden_inode->i_mode))
		inode->i_op = &unionfs_symlink_iops;
	else if (S_ISDIR(hidden_inode->i_mode))
		inode->i_op = &unionfs_dir_iops;

	/* Use different set of file ops for directories */
	if (S_ISDIR(hidden_inode->i_mode))
		inode->i_fop = &unionfs_dir_fops;

	/* properly initialize special inodes */
	if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) ||
	    S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode))
		init_special_inode(inode, hidden_inode->i_mode,
				   hidden_inode->i_rdev);

	/* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */
	if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops) {
		fist_dprint(7, "fixing inode 0x%p a_ops (0x%p -> 0x%p)\n",
			    inode, inode->i_mapping->a_ops,
			    hidden_inode->i_mapping->a_ops);
		inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops;
	}

	/* all well, copy inode attributes */
	fist_copy_attr_all(inode, hidden_inode);

      skip:
	/* only (our) lookup wants to do a d_add */
	switch (flag) {
	case INTERPOSE_DEFAULT:
	case INTERPOSE_REVAL_NEG:
		d_instantiate(dentry, inode);
		break;
	case INTERPOSE_LOOKUP:
		err = PTR_ERR(d_splice_alias(inode, dentry));
		break;
	case INTERPOSE_REVAL:
		/* Do nothing. */
		break;
	default:
		printk(KERN_ERR "Invalid interpose flag passed!");
		BUG();
	}

	fist_print_dentry("Leaving unionfs_interpose", dentry);
	fist_print_inode("Leaving unionfs_interpose", inode);
	up(&inode->i_sem);

      out:
	print_exit_status(err);
	return err;
}
Пример #16
0
/*
 * Find a writeable branch to create new object in.  Checks all writeble
 * branches of the parent inode, from istart to iend order; if none are
 * suitable, also tries branch 0 (which may require a copyup).
 *
 * Return a lower_dentry we can use to create object in, or ERR_PTR.
 */
static struct dentry *find_writeable_branch(struct inode *parent,
					    struct dentry *dentry)
{
	int err = -EINVAL;
	int bindex, istart, iend;
	struct dentry *lower_dentry = NULL;

	istart = ibstart(parent);
	iend = ibend(parent);
	if (istart < 0)
		goto out;

begin:
	for (bindex = istart; bindex <= iend; bindex++) {
		/* skip non-writeable branches */
		err = is_robranch_super(dentry->d_sb, bindex);
		if (err) {
			err = -EROFS;
			continue;
		}
		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!lower_dentry)
			continue;
		/*
		 * check for whiteouts in writeable branch, and remove them
		 * if necessary.
		 */
		err = check_unlink_whiteout(dentry, lower_dentry, bindex);
		if (err > 0)	/* ignore if whiteout found and removed */
			err = 0;
		if (err)
			continue;
		/* if get here, we can write to the branch */
		break;
	}
	/*
	 * If istart wasn't already branch 0, and we got any error, then try
	 * branch 0 (which may require copyup)
	 */
	if (err && istart > 0) {
		istart = iend = 0;
		goto begin;
	}

	/*
	 * If we tried even branch 0, and still got an error, abort.  But if
	 * the error was an EROFS, then we should try to copyup.
	 */
	if (err && err != -EROFS)
		goto out;

	/*
	 * If we get here, then check if copyup needed.  If lower_dentry is
	 * NULL, create the entire dentry directory structure in branch 0.
	 */
	if (!lower_dentry) {
		bindex = 0;
		lower_dentry = create_parents(parent, dentry,
					      dentry->d_name.name, bindex);
		if (IS_ERR(lower_dentry)) {
			err = PTR_ERR(lower_dentry);
			goto out;
		}
	}
	err = 0;		/* all's well */
out:
	if (err)
		return ERR_PTR(err);
	return lower_dentry;
}
Пример #17
0
/*
 * Don't grab the superblock read-lock in unionfs_permission, which prevents
 * a deadlock with the branch-management "add branch" code (which grabbed
 * the write lock).  It is safe to not grab the read lock here, because even
 * with branch management taking place, there is no chance that
 * unionfs_permission, or anything it calls, will use stale branch
 * information.
 */
static int unionfs_permission(struct inode *inode, int mask)
{
	struct inode *lower_inode = NULL;
	int err = 0;
	int bindex, bstart, bend;
	int is_file;
	const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);
	struct inode *inode_grabbed;

	inode_grabbed = igrab(inode);
	is_file = !S_ISDIR(inode->i_mode);

	if (!UNIONFS_I(inode)->lower_inodes) {
		if (is_file)	/* dirs can be unlinked but chdir'ed to */
			err = -ESTALE;	/* force revalidate */
		goto out;
	}
	bstart = ibstart(inode);
	bend = ibend(inode);
	if (unlikely(bstart < 0 || bend < 0)) {
		/*
		 * With branch-management, we can get a stale inode here.
		 * If so, we return ESTALE back to link_path_walk, which
		 * would discard the dcache entry and re-lookup the
		 * dentry+inode.  This should be equivalent to issuing
		 * __unionfs_d_revalidate_chain on nd.dentry here.
		 */
		if (is_file)	/* dirs can be unlinked but chdir'ed to */
			err = -ESTALE;	/* force revalidate */
		goto out;
	}

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_inode = unionfs_lower_inode_idx(inode, bindex);
		if (!lower_inode)
			continue;

		/*
		 * check the condition for D-F-D underlying files/directories,
		 * we don't have to check for files, if we are checking for
		 * directories.
		 */
		if (!is_file && !S_ISDIR(lower_inode->i_mode))
			continue;

		/*
		 * We check basic permissions, but we ignore any conditions
		 * such as readonly file systems or branches marked as
		 * readonly, because those conditions should lead to a
		 * copyup taking place later on.  However, if user never had
		 * access to the file, then no copyup could ever take place.
		 */
		err = __inode_permission(lower_inode, mask);
		if (err && err != -EACCES && err != EPERM && bindex > 0) {
			umode_t mode = lower_inode->i_mode;
			if ((is_robranch_super(inode->i_sb, bindex) ||
			     __is_rdonly(lower_inode)) &&
			    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
				err = 0;
			if (IS_COPYUP_ERR(err))
				err = 0;
		}

		/*
		 * NFS HACK: NFSv2/3 return EACCES on readonly-exported,
		 * locally readonly-mounted file systems, instead of EROFS
		 * like other file systems do.  So we have no choice here
		 * but to intercept this and ignore it for NFS branches
		 * marked readonly.  Specifically, we avoid using NFS's own
		 * "broken" ->permission method, and rely on
		 * generic_permission() to do basic checking for us.
		 */
		if (err && err == -EACCES &&
		    is_robranch_super(inode->i_sb, bindex) &&
		    lower_inode->i_sb->s_magic == NFS_SUPER_MAGIC)
			err = generic_permission(lower_inode, mask);

		/*
		 * The permissions are an intersection of the overall directory
		 * permissions, so we fail if one fails.
		 */
		if (err)
			goto out;

		/* only the leftmost file matters. */
		if (is_file || write_mask) {
			if (is_file && write_mask) {
				err = get_write_access(lower_inode);
				if (!err)
					put_write_access(lower_inode);
			}
			break;
		}
	}
	/* sync times which may have changed (asynchronously) below */
	unionfs_copy_attr_times(inode);

out:
	unionfs_check_inode(inode);
	iput(inode_grabbed);
	return err;
}
Пример #18
0
/*
 * returns 1 if valid, 0 otherwise.
 */
int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	int valid = 1;		/* default is valid (1); invalid is 0. */
	struct dentry *hidden_dentry;
	int bindex, bstart, bend;
	int sbgen, dgen;
	int positive = 0;
	int locked = 0;
	int restart = 0;
	int interpose_flag;

	struct nameidata lowernd; /* TODO: be gentler to the stack */

	if (nd)
		memcpy(&lowernd, nd, sizeof(struct nameidata));
	else
		memset(&lowernd, 0, sizeof(struct nameidata));

restart:
	verify_locked(dentry);

	/* if the dentry is unhashed, do NOT revalidate */
	if (d_deleted(dentry)) {
		printk(KERN_DEBUG "unhashed dentry being revalidated: %*s\n",
		       dentry->d_name.len, dentry->d_name.name);
		goto out;
	}

	BUG_ON(dbstart(dentry) == -1);
	if (dentry->d_inode)
		positive = 1;
	dgen = atomic_read(&UNIONFS_D(dentry)->generation);
	sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
	/* If we are working on an unconnected dentry, then there is no
	 * revalidation to be done, because this file does not exist within the
	 * namespace, and Unionfs operates on the namespace, not data.
	 */
	if (sbgen != dgen) {
		struct dentry *result;
		int pdgen;

		unionfs_read_lock(dentry->d_sb);
		locked = 1;

		/* The root entry should always be valid */
		BUG_ON(IS_ROOT(dentry));

		/* We can't work correctly if our parent isn't valid. */
		pdgen = atomic_read(&UNIONFS_D(dentry->d_parent)->generation);
		if (!restart && (pdgen != sbgen)) {
			unionfs_read_unlock(dentry->d_sb);
			locked = 0;
			/* We must be locked before our parent. */
			if (!
			    (dentry->d_parent->d_op->
			     d_revalidate(dentry->d_parent, nd))) {
				valid = 0;
				goto out;
			}
			restart = 1;
			goto restart;
		}
		BUG_ON(pdgen != sbgen);

		/* Free the pointers for our inodes and this dentry. */
		bstart = dbstart(dentry);
		bend = dbend(dentry);
		if (bstart >= 0) {
			struct dentry *hidden_dentry;
			for (bindex = bstart; bindex <= bend; bindex++) {
				hidden_dentry =
				    unionfs_lower_dentry_idx(dentry, bindex);
				dput(hidden_dentry);
			}
		}
		set_dbstart(dentry, -1);
		set_dbend(dentry, -1);

		interpose_flag = INTERPOSE_REVAL_NEG;
		if (positive) {
			interpose_flag = INTERPOSE_REVAL;
			mutex_lock(&dentry->d_inode->i_mutex);
			bstart = ibstart(dentry->d_inode);
			bend = ibend(dentry->d_inode);
			if (bstart >= 0) {
				struct inode *hidden_inode;
				for (bindex = bstart; bindex <= bend; bindex++) {
					hidden_inode =
					    unionfs_lower_inode_idx(dentry->d_inode,
							bindex);
					iput(hidden_inode);
				}
			}
			kfree(UNIONFS_I(dentry->d_inode)->lower_inodes);
			UNIONFS_I(dentry->d_inode)->lower_inodes = NULL;
			ibstart(dentry->d_inode) = -1;
			ibend(dentry->d_inode) = -1;
			mutex_unlock(&dentry->d_inode->i_mutex);
		}

		result = unionfs_lookup_backend(dentry, &lowernd, interpose_flag);
		if (result) {
			if (IS_ERR(result)) {
				valid = 0;
				goto out;
			}
			/* current unionfs_lookup_backend() doesn't return
			 * a valid dentry
			 */
			dput(dentry);
			dentry = result;
		}

		if (positive && UNIONFS_I(dentry->d_inode)->stale) {
			make_bad_inode(dentry->d_inode);
			d_drop(dentry);
			valid = 0;
			goto out;
		}
		goto out;
	}

	/* The revalidation must occur across all branches */
	bstart = dbstart(dentry);
	bend = dbend(dentry);
	BUG_ON(bstart == -1);
	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (!hidden_dentry || !hidden_dentry->d_op
		    || !hidden_dentry->d_op->d_revalidate)
			continue;

		if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd))
			valid = 0;
	}

	if (!dentry->d_inode)
		valid = 0;

	if (valid) {
		fsstack_copy_attr_all(dentry->d_inode,
				unionfs_lower_inode(dentry->d_inode),
				unionfs_get_nlinks);
		fsstack_copy_inode_size(dentry->d_inode,
				unionfs_lower_inode(dentry->d_inode));
	}

out:
	if (locked)
		unionfs_read_unlock(dentry->d_sb);
	return valid;
}