Esempio n. 1
0
/*
 * This function replicates the directory structure up-to given dentry
 * in the bindex branch.
 */
struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
			      const char *name, int bindex)
{
	int err;
	struct dentry *child_dentry;
	struct dentry *parent_dentry;
	struct dentry *lower_parent_dentry = NULL;
	struct dentry *lower_dentry = NULL;
	const char *childname;
	unsigned int childnamelen;
	int nr_dentry;
	int count = 0;
	int old_bstart;
	int old_bend;
	struct dentry **path = NULL;
	struct super_block *sb;

	verify_locked(dentry);

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

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

	lower_dentry = ERR_PTR(-ENOMEM);

	/* There is no sense allocating any less than the minimum. */
	nr_dentry = 1;
	path = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL);
	if (unlikely(!path))
		goto out;

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

	/*
	 * 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 = dget_parent(child_dentry);

		/* find out the lower_parent_dentry in the given branch */
		lower_parent_dentry =
			unionfs_lower_dentry_idx(parent_dentry, bindex);

		/* grow path table */
		if (count == nr_dentry) {
			void *p;

			nr_dentry *= 2;
			p = krealloc(path, nr_dentry * sizeof(struct dentry *),
				     GFP_KERNEL);
			if (unlikely(!p)) {
				lower_dentry = ERR_PTR(-ENOMEM);
				goto out;
			}
			path = p;
		}

		/* store the child dentry */
		path[count++] = child_dentry;
	} while (!lower_parent_dentry);
	count--;

	sb = dentry->d_sb;

	/*
	 * This code goes between the begin/end labels and basically
	 * emulates a while(child_dentry != dentry), only cleaner and
	 * shorter than what would be a much longer while loop.
	 */
begin:
	/* get lower parent dir in the current branch */
	lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
	dput(parent_dentry);

	/* 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 */
		lower_dentry = lookup_one_len(childname, lower_parent_dentry,
					      childnamelen);
		if (IS_ERR(lower_dentry))
			goto out;
	} else {
		/*
		 * Is the name a whiteout of the child name ?  lookup the
		 * whiteout child in the underlying file system
		 */
		lower_dentry = lookup_one_len(name, lower_parent_dentry,
					      strlen(name));
		if (IS_ERR(lower_dentry))
			goto out;

		/* Replace the current dentry (if any) with the new one */
		dput(unionfs_lower_dentry_idx(dentry, bindex));
		unionfs_set_lower_dentry_idx(dentry, bindex,
					     lower_dentry);

		__cleanup_dentry(dentry, bindex, old_bstart, old_bend);
		goto out;
	}

	if (lower_dentry->d_inode) {
		/*
		 * since this already exists we dput to avoid
		 * multiple references on the same dentry
		 */
		dput(lower_dentry);
	} else {
		struct sioq_args args;

		/* it's a negative dentry, create a new dir */
		lower_parent_dentry = lock_parent(lower_dentry);

		args.mkdir.parent = lower_parent_dentry->d_inode;
		args.mkdir.dentry = lower_dentry;
		args.mkdir.mode = child_dentry->d_inode->i_mode;

		run_sioq(__unionfs_mkdir, &args);
		err = args.err;

		if (!err)
			err = copyup_permissions(dir->i_sb, child_dentry,
						 lower_dentry);
		unlock_dir(lower_parent_dentry);
		if (err) {
			dput(lower_dentry);
			lower_dentry = ERR_PTR(err);
			goto out;
		}

	}

	__set_inode(child_dentry, lower_dentry, bindex);
	__set_dentry(child_dentry, lower_dentry, bindex);
	/*
	 * update times of this dentry, but also the parent, because if
	 * we changed, the parent may have changed too.
	 */
	fsstack_copy_attr_times(parent_dentry->d_inode,
				lower_parent_dentry->d_inode);
	unionfs_copy_attr_times(child_dentry->d_inode);

	parent_dentry = child_dentry;
	child_dentry = path[--count];
	goto begin;
out:
	/* cleanup any leftover locks from the do/while loop above */
	if (IS_ERR(lower_dentry))
		while (count)
			dput(path[count--]);
	kfree(path);
	return lower_dentry;
}
Esempio n. 2
0
/* This function replicates the directory structure upto given dentry
 * in the bindex branch.
 */
static 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;
	struct super_block *sb;

	verify_locked(dentry);

	/* There is no sense allocating any less than the minimum. */
	kmalloc_size = malloc_sizes[0].cs_size;
	num_dentry = kmalloc_size / sizeof(struct dentry *);

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

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

	hidden_dentry = ERR_PTR(-ENOMEM);
	path = kzalloc(kmalloc_size, GFP_KERNEL);
	if (!path)
		goto out;

	/* 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;
		unionfs_lock_dentry(parent_dentry);

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

		/* store the child dentry */
		path[count++] = child_dentry;

		/* grow path table */
		if (count == num_dentry) {
			old_kmalloc_size = kmalloc_size;
			kmalloc_size *= 2;
			num_dentry = kmalloc_size / sizeof(struct dentry *);

			tmp_path = kzalloc(kmalloc_size, GFP_KERNEL);
			if (!tmp_path) {
				hidden_dentry = ERR_PTR(-ENOMEM);
				goto out;
			}
			memcpy(tmp_path, path, old_kmalloc_size);
			kfree(path);
			path = tmp_path;
			tmp_path = NULL;
		}

	} while (!hidden_parent_dentry);
	count--;

	sb = dentry->d_sb;

	/* This is basically while(child_dentry != dentry).  This loop is
	 * horrible to follow and should be replaced with cleaner code.
	 */
	while (1) {
		/* get hidden parent dir in the current branch */
		hidden_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
		unionfs_unlock_dentry(parent_dentry);

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

			/* 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(unionfs_lower_dentry_idx(dentry, bindex));
			unionfs_set_lower_dentry_idx(dentry, bindex, hidden_dentry);

			__cleanup_dentry(dentry, bindex, old_bstart, old_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 {
			struct sioq_args args;

			/* its a negative dentry, create a new dir */
			hidden_parent_dentry = lock_parent(hidden_dentry);

			args.mkdir.parent = hidden_parent_dentry->d_inode;
			args.mkdir.dentry = hidden_dentry;
			args.mkdir.mode = child_dentry->d_inode->i_mode;

			run_sioq(__unionfs_mkdir, &args);
			err = args.err;

			if (!err)
				err = copyup_permissions(dir->i_sb,
						child_dentry, hidden_dentry);
			unlock_dir(hidden_parent_dentry);
			if (err) {
				dput(hidden_dentry);
				hidden_dentry = ERR_PTR(err);
				goto out;
			}

		}

		__set_inode(child_dentry, hidden_dentry, bindex);
		__set_dentry(child_dentry, hidden_dentry, bindex);

		parent_dentry = child_dentry;
		child_dentry = path[--count];
	}
out:
	kfree(path);
	return hidden_dentry;
}