예제 #1
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;
}
예제 #2
0
/* purge a dentry's lower-branch states (dput/mntput, etc.) */
static void __cleanup_dentry(struct dentry *dentry, int bindex,
			     int old_bstart, int old_bend)
{
	int loop_start;
	int loop_end;
	int new_bstart = -1;
	int new_bend = -1;
	int i;

	loop_start = min(old_bstart, bindex);
	loop_end = max(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
	 */
	for (i = loop_start; i <= loop_end; i++) {
		if (!unionfs_lower_dentry_idx(dentry, i))
			continue;

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

		if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) {
			dput(unionfs_lower_dentry_idx(dentry, i));
			unionfs_set_lower_dentry_idx(dentry, i, NULL);

			unionfs_mntput(dentry, i);
			unionfs_set_lower_mnt_idx(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;
	dbstart(dentry) = new_bstart;
	dbend(dentry) = new_bend;

}
예제 #3
0
/*
 * Post-copyup helper to ensure we have valid mnts: set lower mnt of
 * dentry+parents to the first parent node that has an mnt.
 */
void unionfs_postcopyup_setmnt(struct dentry *dentry)
{
	struct dentry *parent, *hasone;
	int bindex = dbstart(dentry);

	if (unionfs_lower_mnt_idx(dentry, bindex))
		return;
	hasone = dentry->d_parent;
	/* this loop should stop at root dentry */
	while (!unionfs_lower_mnt_idx(hasone, bindex))
		hasone = hasone->d_parent;
	parent = dentry;
	while (!unionfs_lower_mnt_idx(parent, bindex)) {
		unionfs_set_lower_mnt_idx(parent, bindex,
					  unionfs_mntget(hasone, bindex));
		parent = parent->d_parent;
	}
}
예제 #4
0
static void unionfs_d_release(struct dentry *dentry)
{
	int bindex, bstart, bend;

	/* There is no reason to lock the dentry, because we have the only
	 * reference, but the printing functions verify that we have a lock
	 * on the dentry before calling dbstart, etc.
	 */
	unionfs_lock_dentry(dentry);

	/* this could be a negative dentry, so check first */
	if (!UNIONFS_D(dentry)) {
		printk(KERN_DEBUG "dentry without private data: %.*s",
		       dentry->d_name.len, dentry->d_name.name);
		goto out;
	} else if (dbstart(dentry) < 0) {
		/* this is due to a failed lookup */
		printk(KERN_DEBUG "dentry without hidden dentries : %.*s",
		       dentry->d_name.len, dentry->d_name.name);
		goto out_free;
	}

	/* Release all the hidden dentries */
	bstart = dbstart(dentry);
	bend = dbend(dentry);
	for (bindex = bstart; bindex <= bend; bindex++) {
		dput(unionfs_lower_dentry_idx(dentry, bindex));
		mntput(unionfs_lower_mnt_idx(dentry, bindex));

		unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
		unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
	}
	/* free private data (unionfs_dentry_info) here */
	kfree(UNIONFS_D(dentry)->lower_paths);
	UNIONFS_D(dentry)->lower_paths = NULL;

out_free:
	/* No need to unlock it, because it is disappeared. */
	free_dentry_private_data(UNIONFS_D(dentry));
	dentry->d_fsdata = NULL;	/* just to be safe */

out:
	return;
}
예제 #5
0
파일: lookup.c 프로젝트: Mr-Aloof/wl500g
/*
 * Main (and complex) driver function for Unionfs's lookup
 *
 * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error
 * PTR if d_splice returned a different dentry.
 *
 * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's
 * inode info must be locked.  If lookupmode is INTERPOSE_LOOKUP (i.e., a
 * newly looked-up dentry), then unionfs_lookup_backend will return a locked
 * dentry's info, which the caller must unlock.
 */
struct dentry *unionfs_lookup_full(struct dentry *dentry,
				   struct dentry *parent, int lookupmode)
{
	int err = 0;
	struct dentry *lower_dentry = NULL;
	struct vfsmount *lower_mnt;
	struct vfsmount *lower_dir_mnt;
	struct dentry *wh_lower_dentry = NULL;
	struct dentry *lower_dir_dentry = NULL;
	struct dentry *d_interposed = NULL;
	int bindex, bstart, bend, bopaque;
	int opaque, num_positive = 0;
	const char *name;
	int namelen;
	int pos_start, pos_end;

	/*
	 * 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.
	 */
	verify_locked(dentry);
	verify_locked(parent);

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

	/* We never partial lookup the root directory. */
	if (IS_ROOT(dentry))
		goto out;

	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);
	bend = dbend(parent);
	bopaque = dbopaque(parent);
	BUG_ON(bstart < 0);

	/* adjust bend to bopaque if needed */
	if ((bopaque >= 0) && (bopaque < bend))
		bend = bopaque;

	/* lookup all possible dentries */
	for (bindex = bstart; bindex <= bend; bindex++) {

		lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		lower_mnt = unionfs_lower_mnt_idx(dentry, bindex);

		/* skip if we already have a positive lower dentry */
		if (lower_dentry) {
			if (dbstart(dentry) < 0)
				dbstart(dentry) = bindex;
			if (bindex > dbend(dentry))
				dbend(dentry) = bindex;
			if (lower_dentry->d_inode)
				num_positive++;
			continue;
		}

		lower_dir_dentry =
			unionfs_lower_dentry_idx(parent, bindex);
		/* if the lower dentry's parent does not exist, skip this */
		if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
			continue;

		/* also skip it if the parent isn't a directory. */
		if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
			continue; /* XXX: should be BUG_ON */

		/* check for whiteouts: stop lookup if found */
		wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry);
		if (IS_ERR(wh_lower_dentry)) {
			err = PTR_ERR(wh_lower_dentry);
			goto out_free;
		}
		if (wh_lower_dentry->d_inode) {
			dbend(dentry) = dbopaque(dentry) = bindex;
			if (dbstart(dentry) < 0)
				dbstart(dentry) = bindex;
			dput(wh_lower_dentry);
			break;
		}
		dput(wh_lower_dentry);

		/* Now do regular lookup; lookup @name */
		lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex);
		lower_mnt = NULL; /* XXX: needed? */

		lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt,
					    name, &lower_mnt);

		if (IS_ERR(lower_dentry)) {
			err = PTR_ERR(lower_dentry);
			goto out_free;
		}
		unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
		if (!lower_mnt)
			lower_mnt = unionfs_mntget(dentry->d_sb->s_root,
						   bindex);
		unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);

		/* adjust dbstart/end */
		if (dbstart(dentry) < 0)
			dbstart(dentry) = bindex;
		if (bindex > dbend(dentry))
			dbend(dentry) = bindex;
		/*
		 * We always store the lower dentries above, and update
		 * dbstart/dbend, even if the whole unionfs dentry is
		 * negative (i.e., no lower inodes).
		 */
		if (!lower_dentry->d_inode)
			continue;
		num_positive++;

		/*
		 * check if we just found an opaque directory, if so, stop
		 * lookups here.
		 */
		if (!S_ISDIR(lower_dentry->d_inode->i_mode))
			continue;
		opaque = is_opaque_dir(dentry, bindex);
		if (opaque < 0) {
			err = opaque;
			goto out_free;
		} else if (opaque) {
			dbend(dentry) = dbopaque(dentry) = bindex;
			break;
		}
		dbend(dentry) = bindex;

		/* update parent directory's atime with the bindex */
		fsstack_copy_attr_atime(parent->d_inode,
					lower_dir_dentry->d_inode);
	}

	/* sanity checks, then decide if to process a negative dentry */
	BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);
	BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);

	if (num_positive > 0)
		goto out_positive;

	/*** handle NEGATIVE dentries ***/

	/*
	 * If negative, keep only first lower negative dentry, to save on
	 * memory.
	 */
	if (dbstart(dentry) < dbend(dentry)) {
		path_put_lowers(dentry, dbstart(dentry) + 1,
				dbend(dentry), false);
		dbend(dentry) = dbstart(dentry);
	}
	if (lookupmode == INTERPOSE_PARTIAL)
		goto out;
	if (lookupmode == INTERPOSE_LOOKUP) {
		/*
		 * If all we found was a whiteout in the first available
		 * branch, then create a negative dentry for a possibly new
		 * file to be created.
		 */
		if (dbopaque(dentry) < 0)
			goto out;
		/* XXX: need to get mnt here */
		bindex = dbstart(dentry);
		if (unionfs_lower_dentry_idx(dentry, bindex))
			goto out;
		lower_dir_dentry =
			unionfs_lower_dentry_idx(parent, bindex);
		if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
			goto out;
		if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
			goto out; /* XXX: should be BUG_ON */
		/* XXX: do we need to cross bind mounts here? */
		lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen);
		if (IS_ERR(lower_dentry)) {
			err = PTR_ERR(lower_dentry);
			goto out;
		}
		/* XXX: need to mntget/mntput as needed too! */
		unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
		/* XXX: wrong mnt for crossing bind mounts! */
		lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex);
		unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);

		goto out;
	}

	/* if we're revalidating a positive dentry, don't make it negative */
	if (lookupmode != INTERPOSE_REVAL)
		d_add(dentry, NULL);

	goto out;

out_positive:
	/*** handle POSITIVE dentries ***/

	/*
	 * This unionfs dentry is positive (at least one lower inode
	 * exists), so scan entire dentry from beginning to end, and remove
	 * any negative lower dentries, if any.  Then, update dbstart/dbend
	 * to reflect the start/end of positive dentries.
	 */
	pos_start = pos_end = -1;
	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_dentry = unionfs_lower_dentry_idx(dentry,
							bindex);
		if (lower_dentry && lower_dentry->d_inode) {
			if (pos_start < 0)
				pos_start = bindex;
			if (bindex > pos_end)
				pos_end = bindex;
			continue;
		}
		path_put_lowers(dentry, bindex, bindex, false);
	}
	if (pos_start >= 0)
		dbstart(dentry) = pos_start;
	if (pos_end >= 0)
		dbend(dentry) = pos_end;

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

		/*
		 * This dentry was positive, so it is as if we had a
		 * negative revalidation.
		 */
		lookupmode = INTERPOSE_REVAL_NEG;
		update_bstart(dentry);
	}

	/*
	 * Interpose can return a dentry if d_splice returned a different
	 * dentry.
	 */
	d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
	if (IS_ERR(d_interposed))
		err = PTR_ERR(d_interposed);
	else if (d_interposed)
		dentry = d_interposed;

	if (!err)
		goto out;
	d_drop(dentry);

out_free:
	/* should dput/mntput all the underlying dentries on error condition */
	if (dbstart(dentry) >= 0)
		path_put_lowers_all(dentry, false);
	/* free lower_paths unconditionally */
	kfree(UNIONFS_D(dentry)->lower_paths);
	UNIONFS_D(dentry)->lower_paths = NULL;

out:
	if (dentry && UNIONFS_D(dentry)) {
		BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);
		BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);
	}
	if (d_interposed && UNIONFS_D(d_interposed)) {
		BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0);
		BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0);
	}

	if (!err && d_interposed)
		return d_interposed;
	return ERR_PTR(err);
}
예제 #6
0
struct dentry *unionfs_lookup_backend(struct dentry *dentry, struct nameidata *nd,
				      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;
	struct vfsmount *first_hidden_mnt = NULL;
	int locked_parent = 0;
	int locked_child = 0;

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

	/* 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(UNIONFS_D(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 = dget_parent(dentry);
	/* We never partial lookup the root directory. */
	if (parent_dentry != dentry) {
		unionfs_lock_dentry(parent_dentry);
		locked_parent = 1;
	} else {
		dput(parent_dentry);
		parent_dentry = NULL;
		goto out;
	}

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

	for (bindex = bstart; bindex <= bend; bindex++) {
		hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
		if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry)
			continue;
		BUG_ON(hidden_dentry != NULL);

		hidden_dir_dentry = unionfs_lower_dentry_idx(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 + UNIONFS_WHLEN);
		if (IS_ERR(wh_hidden_dentry)) {
			dput(first_hidden_dentry);
			mntput(first_hidden_mnt);
			err = PTR_ERR(wh_hidden_dentry);
			goto out_free;
		}

		if (wh_hidden_dentry->d_inode) {
			/* We found a whiteout so lets give up. */
			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);
			mntput(first_hidden_mnt);
			goto out_free;
		}

		dput(wh_hidden_dentry);
		wh_hidden_dentry = NULL;

		/* Now do regular lookup; lookup foo */
		nd->dentry = unionfs_lower_dentry_idx(dentry, bindex);
		/* FIXME: fix following line for mount point crossing */
		nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);

		hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry,
					       namelen, nd);
		if (IS_ERR(hidden_dentry)) {
			dput(first_hidden_dentry);
			mntput(first_hidden_mnt);
			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;
				/* FIXME: following line needs to be changed
				 * to allow mountpoint crossing
				 */
				first_hidden_mnt = mntget(
					unionfs_lower_mnt_idx(parent_dentry,
								bindex));
				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);
		unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry);
		/* FIXME: the following line needs to get fixed to allow
		 * mountpoint crossing
		 */
		unionfs_set_lower_mnt_idx(dentry, bindex,
			mntget(unionfs_lower_mnt_idx(parent_dentry, bindex)));
		set_dbend(dentry, bindex);

		/* update parent directory's atime with the bindex */
		fsstack_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(unionfs_lower_dentry(dentry)->d_inode->i_mode));
			continue;
		}

		opaque = is_opaque_dir(dentry, bindex);
		if (opaque < 0) {
			dput(first_hidden_dentry);
			mntput(first_hidden_mnt);
			err = opaque;
			goto out_free;
		} else 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)
			UNIONFS_I(dentry->d_inode)->stale = 1;

		goto out;
	}
	/* This should only happen if we found a whiteout. */
	if (first_dentry_offset == -1) {
		nd->dentry = dentry;
		/* FIXME: fix following line for mount point crossing */
		nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);

		first_hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry,
						     namelen, nd);
		first_dentry_offset = bindex;
		if (IS_ERR(first_hidden_dentry)) {
			err = PTR_ERR(first_hidden_dentry);
			goto out;
		}
		
		/* FIXME: the following line needs to be changed to allow
		 * mountpoint crossing
		 */
		first_hidden_mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex));
	}
	unionfs_set_lower_dentry_idx(dentry, first_dentry_offset, first_hidden_dentry);
	unionfs_set_lower_mnt_idx(dentry, first_dentry_offset, first_hidden_mnt);
	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 & corresponding
	 * vfsmount - throw it out.
	 */
	dput(first_hidden_dentry);
	mntput(first_hidden_mnt);

	/* 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;

	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(unionfs_lower_dentry_idx(dentry, bindex));
			mntput(unionfs_lower_mnt_idx(dentry, bindex));
		}
	}
	kfree(UNIONFS_D(dentry)->lower_paths);
	UNIONFS_D(dentry)->lower_paths = NULL;
	set_dbstart(dentry, -1);
	set_dbend(dentry, -1);

out:
	if (!err && UNIONFS_D(dentry)) {
		BUG_ON(dbend(dentry) > UNIONFS_D(dentry)->bcount);
		BUG_ON(dbend(dentry) > sbmax(dentry->d_sb));
		BUG_ON(dbstart(dentry) < 0);
	}
	kfree(whname);
	if (locked_parent)
		unionfs_unlock_dentry(parent_dentry);
	dput(parent_dentry);
	if (locked_child)
		unionfs_unlock_dentry(dentry);
	return ERR_PTR(err);
}
예제 #7
0
파일: main.c 프로젝트: mrtos/Logitech-Revue
/*
 * our custom d_alloc_root work-alike
 *
 * we can't use d_alloc_root if we want to use our own interpose function
 * unchanged, so we simply call our own "fake" d_alloc_root
 */
static struct dentry *unionfs_d_alloc_root(struct super_block *sb)
{
    struct dentry *ret = NULL;

    if (sb) {
        static const struct qstr name = {
            .name = "/",
            .len = 1
        };

        ret = d_alloc(NULL, &name);
        if (likely(ret)) {
            ret->d_op = &unionfs_dops;
            ret->d_sb = sb;
            ret->d_parent = ret;
        }
    }
    return ret;
}

/*
 * There is no need to lock the unionfs_super_info's rwsem as there is no
 * way anyone can have a reference to the superblock at this point in time.
 */
static int unionfs_read_super(struct super_block *sb, void *raw_data,
                              int silent)
{
    int err = 0;
    struct unionfs_dentry_info *lower_root_info = NULL;
    int bindex, bstart, bend;

    if (!raw_data) {
        printk(KERN_ERR
               "unionfs: read_super: missing data argument\n");
        err = -EINVAL;
        goto out;
    }

    /* Allocate superblock private data */
    sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);
    if (unlikely(!UNIONFS_SB(sb))) {
        printk(KERN_CRIT "unionfs: read_super: out of memory\n");
        err = -ENOMEM;
        goto out;
    }

    UNIONFS_SB(sb)->bend = -1;
    atomic_set(&UNIONFS_SB(sb)->generation, 1);
    init_rwsem(&UNIONFS_SB(sb)->rwsem);
    UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */

    lower_root_info = unionfs_parse_options(sb, raw_data);
    if (IS_ERR(lower_root_info)) {
        printk(KERN_ERR
               "unionfs: read_super: error while parsing options "
               "(err = %ld)\n", PTR_ERR(lower_root_info));
        err = PTR_ERR(lower_root_info);
        lower_root_info = NULL;
        goto out_free;
    }
    if (lower_root_info->bstart == -1) {
        err = -ENOENT;
        goto out_free;
    }

    /* set the lower superblock field of upper superblock */
    bstart = lower_root_info->bstart;
    BUG_ON(bstart != 0);
    sbend(sb) = bend = lower_root_info->bend;
    for (bindex = bstart; bindex <= bend; bindex++) {
        struct dentry *d = lower_root_info->lower_paths[bindex].dentry;
        atomic_inc(&d->d_sb->s_active);
        unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
    }

    /* max Bytes is the maximum bytes from highest priority branch */
    sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;

    /*
     * Our c/m/atime granularity is 1 ns because we may stack on file
     * systems whose granularity is as good.  This is important for our
     * time-based cache coherency.
     */
    sb->s_time_gran = 1;

    sb->s_op = &unionfs_sops;

    /* See comment next to the definition of unionfs_d_alloc_root */
    sb->s_root = unionfs_d_alloc_root(sb);
    if (unlikely(!sb->s_root)) {
        err = -ENOMEM;
        goto out_dput;
    }

    /* link the upper and lower dentries */
    sb->s_root->d_fsdata = NULL;
    err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
    if (unlikely(err))
        goto out_freedpd;

    /* Set the lower dentries for s_root */
    for (bindex = bstart; bindex <= bend; bindex++) {
        struct dentry *d;
        struct vfsmount *m;

        d = lower_root_info->lower_paths[bindex].dentry;
        m = lower_root_info->lower_paths[bindex].mnt;

        unionfs_set_lower_dentry_idx(sb->s_root, bindex, d);
        unionfs_set_lower_mnt_idx(sb->s_root, bindex, m);
    }
    dbstart(sb->s_root) = bstart;
    dbend(sb->s_root) = bend;

    /* Set the generation number to one, since this is for the mount. */
    atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);

    /*
     * Call interpose to create the upper level inode.  Only
     * INTERPOSE_LOOKUP can return a value other than 0 on err.
     */
    err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0));
    unionfs_unlock_dentry(sb->s_root);
    if (!err)
        goto out;
    /* else fall through */

out_freedpd:
    if (UNIONFS_D(sb->s_root)) {
        kfree(UNIONFS_D(sb->s_root)->lower_paths);
        free_dentry_private_data(sb->s_root);
    }
    dput(sb->s_root);

out_dput:
    if (lower_root_info && !IS_ERR(lower_root_info)) {
        for (bindex = lower_root_info->bstart;
                bindex <= lower_root_info->bend; bindex++) {
            struct dentry *d;
            struct vfsmount *m;

            d = lower_root_info->lower_paths[bindex].dentry;
            m = lower_root_info->lower_paths[bindex].mnt;

            dput(d);
            /* initializing: can't use unionfs_mntput here */
            mntput(m);
            /* drop refs we took earlier */
            atomic_dec(&d->d_sb->s_active);
        }
        kfree(lower_root_info->lower_paths);
        kfree(lower_root_info);
        lower_root_info = NULL;
    }

out_free:
    kfree(UNIONFS_SB(sb)->data);
    kfree(UNIONFS_SB(sb));
    sb->s_fs_info = NULL;

out:
    if (lower_root_info && !IS_ERR(lower_root_info)) {
        kfree(lower_root_info->lower_paths);
        kfree(lower_root_info);
    }
    return err;
}
예제 #8
0
/*
 * There is no need to lock the unionfs_super_info's rwsem as there is no
 * way anyone can have a reference to the superblock at this point in time.
 */
static int unionfs_read_super(struct super_block *sb, void *raw_data,
			      int silent)
{
	int err = 0;
	struct unionfs_dentry_info *lower_root_info = NULL;
	int bindex, bstart, bend;
	struct inode *inode = NULL;

	if (!raw_data) {
		printk(KERN_ERR
		       "unionfs: read_super: missing data argument\n");
		err = -EINVAL;
		goto out;
	}

	/* Allocate superblock private data */
	sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);
	if (unlikely(!UNIONFS_SB(sb))) {
		printk(KERN_CRIT "unionfs: read_super: out of memory\n");
		err = -ENOMEM;
		goto out;
	}

	UNIONFS_SB(sb)->bend = -1;
	atomic_set(&UNIONFS_SB(sb)->generation, 1);
	init_rwsem(&UNIONFS_SB(sb)->rwsem);
	UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */

	lower_root_info = unionfs_parse_options(sb, raw_data);
	if (IS_ERR(lower_root_info)) {
		printk(KERN_ERR
		       "unionfs: read_super: error while parsing options "
		       "(err = %ld)\n", PTR_ERR(lower_root_info));
		err = PTR_ERR(lower_root_info);
		lower_root_info = NULL;
		goto out_free;
	}
	if (lower_root_info->bstart == -1) {
		err = -ENOENT;
		goto out_free;
	}

	/* set the lower superblock field of upper superblock */
	bstart = lower_root_info->bstart;
	BUG_ON(bstart != 0);
	sbend(sb) = bend = lower_root_info->bend;
	for (bindex = bstart; bindex <= bend; bindex++) {
		struct dentry *d = lower_root_info->lower_paths[bindex].dentry;
		atomic_inc(&d->d_sb->s_active);
		unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
	}

	/* max Bytes is the maximum bytes from highest priority branch */
	sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;

	/*
	 * Our c/m/atime granularity is 1 ns because we may stack on file
	 * systems whose granularity is as good.  This is important for our
	 * time-based cache coherency.
	 */
	sb->s_time_gran = 1;

	sb->s_op = &unionfs_sops;

	/* get a new inode and allocate our root dentry */
	inode = unionfs_iget(sb, iunique(sb, UNIONFS_ROOT_INO));
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_dput;
	}
	sb->s_root = d_make_root(inode);
	if (unlikely(!sb->s_root)) {
		err = -ENOMEM;
		goto out_iput;
	}
	d_set_d_op(sb->s_root, &unionfs_dops);

	/* link the upper and lower dentries */
	sb->s_root->d_fsdata = NULL;
	err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);
	if (unlikely(err))
		goto out_freedpd;

	/* if get here: cannot have error */

	/* Set the lower dentries for s_root */
	for (bindex = bstart; bindex <= bend; bindex++) {
		struct dentry *d;
		struct vfsmount *m;

		d = lower_root_info->lower_paths[bindex].dentry;
		m = lower_root_info->lower_paths[bindex].mnt;

		unionfs_set_lower_dentry_idx(sb->s_root, bindex, d);
		unionfs_set_lower_mnt_idx(sb->s_root, bindex, m);
	}
	dbstart(sb->s_root) = bstart;
	dbend(sb->s_root) = bend;

	/* Set the generation number to one, since this is for the mount. */
	atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);

	if (atomic_read(&inode->i_count) <= 1)
		unionfs_fill_inode(sb->s_root, inode);

	/*
	 * No need to call interpose because we already have a positive
	 * dentry, which was instantiated by d_alloc_root.  Just need to
	 * d_rehash it.
	 */
	d_rehash(sb->s_root);

	unionfs_unlock_dentry(sb->s_root);
	goto out; /* all is well */

out_freedpd:
	if (UNIONFS_D(sb->s_root)) {
		kfree(UNIONFS_D(sb->s_root)->lower_paths);
		free_dentry_private_data(sb->s_root);
	}
	dput(sb->s_root);

out_iput:
	iput(inode);

out_dput:
	if (lower_root_info && !IS_ERR(lower_root_info)) {
		for (bindex = lower_root_info->bstart;
		     bindex <= lower_root_info->bend; bindex++) {
			struct dentry *d;
			d = lower_root_info->lower_paths[bindex].dentry;
			/* drop refs we took earlier */
			atomic_dec(&d->d_sb->s_active);
			path_put(&lower_root_info->lower_paths[bindex]);
		}
		kfree(lower_root_info->lower_paths);
		kfree(lower_root_info);
		lower_root_info = NULL;
	}

out_free:
	kfree(UNIONFS_SB(sb)->data);
	kfree(UNIONFS_SB(sb));
	sb->s_fs_info = NULL;

out:
	if (lower_root_info && !IS_ERR(lower_root_info)) {
		kfree(lower_root_info->lower_paths);
		kfree(lower_root_info);
	}
	return err;
}