Example #1
0
/*
 * initial procedure of adding a new entry.
 * prepare writable branch and the parent dir, lock it,
 * and lookup whiteout for the new entry.
 */
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
		  struct dentry *src_dentry, struct au_pin *pin,
		  struct au_wr_dir_args *wr_dir_args)
{
	struct dentry *wh_dentry, *h_parent;
	struct super_block *sb;
	struct au_branch *br;
	int err;
	unsigned int udba;
	aufs_bindex_t bcpup;

	AuDbg("%.*s\n", AuDLNPair(dentry));

	err = au_wr_dir(dentry, src_dentry, wr_dir_args);
	bcpup = err;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err < 0))
		goto out;

	sb = dentry->d_sb;
	udba = au_opt_udba(sb);
	err = au_pin(pin, dentry, bcpup, udba,
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out;

	h_parent = au_pinned_h_parent(pin);
	if (udba != AuOpt_UDBA_NONE
	    && au_dbstart(dentry) == bcpup)
		err = au_may_add(dentry, bcpup, h_parent,
				 au_ftest_wrdir(wr_dir_args->flags, ISDIR));
	else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
		err = -ENAMETOOLONG;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out_unpin;

	br = au_sbr(sb, bcpup);
	if (dt) {
		struct path tmp = {
			.dentry	= h_parent,
			.mnt	= au_br_mnt(br)
		};
		au_dtime_store(dt, au_pinned_parent(pin), &tmp);
	}

	wh_dentry = NULL;
	if (bcpup != au_dbwh(dentry))
		goto out; /* success */

	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);

out_unpin:
	if (IS_ERR(wh_dentry))
		au_unpin(pin);
out:
	return wh_dentry;
}
Example #2
0
/*
 * decide the branch where we operate for @dentry. the branch index will be set
 * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
 * dir for reverting.
 * when a new whiteout is necessary, create it.
 */
static struct dentry*
lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
		    struct au_dtime *dt, struct au_pin *pin)
{
	struct dentry *wh_dentry;
	struct super_block *sb;
	struct path h_path;
	int err, need_wh;
	unsigned int udba;
	aufs_bindex_t bcpup;

	need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
	wh_dentry = ERR_PTR(need_wh);
	if (unlikely(need_wh < 0))
		goto out;

	sb = dentry->d_sb;
	udba = au_opt_udba(sb);
	bcpup = *rbcpup;
	err = au_pin(pin, dentry, bcpup, udba,
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out;

	h_path.dentry = au_pinned_h_parent(pin);
	if (udba != AuOpt_UDBA_NONE
	    && au_dbstart(dentry) == bcpup) {
		err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
		wh_dentry = ERR_PTR(err);
		if (unlikely(err))
			goto out_unpin;
	}

	h_path.mnt = au_sbr_mnt(sb, bcpup);
	au_dtime_store(dt, au_pinned_parent(pin), &h_path);
	wh_dentry = NULL;
	if (!need_wh)
		goto out; /* success, no need to create whiteout */

	wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
	if (IS_ERR(wh_dentry))
		goto out_unpin;

	/* returns with the parent is locked and wh_dentry is dget-ed */
	goto out; /* success */

out_unpin:
	au_unpin(pin);
out:
	return wh_dentry;
}
Example #3
0
/*
 * initial procedure of adding a new entry.
 * prepare writable branch and the parent dir, lock it,
 * lookup whiteout for the new entry.
 */
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
		  struct dentry *src_dentry, struct au_pin *pin,
		  struct au_wr_dir_args *wr_dir_args)
{
	struct dentry *wh_dentry, *h_parent;
	struct super_block *sb;
	int err;
	unsigned int mnt_flags;
	unsigned char pin_flags;
	aufs_bindex_t bstart, bcpup;
	struct au_ndx ndx;

	LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry);

	bstart = au_dbstart(dentry);
	err = au_wr_dir(dentry, src_dentry, wr_dir_args);
	bcpup = err;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err < 0))
		goto out;

	sb = dentry->d_sb;
	mnt_flags = au_mntflags(sb);
	pin_flags = AuPin_DI_LOCKED | AuPin_MNT_WRITE;
	if (dt && au_opt_test(mnt_flags, UDBA_INOTIFY))
		au_fset_pin(pin_flags, DO_GPARENT);
	err = au_pin(pin, dentry, bcpup, pin_flags);
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out;

	ndx.nfsmnt = au_nfsmnt(sb, bcpup);
	ndx.flags = 0;
	if (au_test_dlgt(mnt_flags))
		au_fset_ndx(ndx.flags, DLGT);
	ndx.nd = NULL;
	/* ndx.br = NULL; */
	/* ndx.nd_file = NULL; */

	h_parent = au_pinned_h_parent(pin);
	if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) {
		struct nameidata nd;

		if (ndx.nfsmnt) {
			/* todo: dirty? */
			ndx.nd = &nd;
			ndx.br = au_sbr(sb, bcpup);
			memset(&nd, 0, sizeof(nd));
			nd.flags = LOOKUP_CREATE;
			nd.intent.open.flags = O_EXCL;
		}
		err = au_may_add(dentry, bcpup, h_parent,
				 au_ftest_wrdir(wr_dir_args->flags, ISDIR),
				 &ndx);
		wh_dentry = ERR_PTR(err);
		if (unlikely(err))
			goto out_unpin;
		ndx.nd = NULL;
		ndx.br = NULL;
	}

	if (dt)
		au_dtime_store(dt, au_pinned_parent(pin), h_parent,
			       au_pinned_hdir(pin), au_pinned_hgdir(pin));

	wh_dentry = NULL;
	if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry))
		goto out; /* success */

	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx);

 out_unpin:
	if (IS_ERR(wh_dentry))
		au_unpin(pin);
 out:
	AuTraceErrPtr(wh_dentry);
	return wh_dentry;
}
Example #4
0
/*
 * initial procedure of adding a new entry.
 * prepare writable branch and the parent dir, lock it,
 * and lookup whiteout for the new entry.
 */
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
		  struct dentry *src_dentry, struct au_pin *pin,
		  struct au_wr_dir_args *wr_dir_args)
{
	struct dentry *wh_dentry, *h_parent;
	struct super_block *sb;
	struct au_branch *br;
	int err;
	unsigned int udba;
	aufs_bindex_t bcpup;

	AuDbg("%.*s\n", AuDLNPair(dentry));

	err = au_wr_dir(dentry, src_dentry, wr_dir_args);
	bcpup = err;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err < 0))
		goto out;

	sb = dentry->d_sb;
	udba = au_opt_udba(sb);
	err = au_pin(pin, dentry, bcpup, udba,
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out;

	h_parent = au_pinned_h_parent(pin);
	if (udba != AuOpt_UDBA_NONE
	    && au_dbstart(dentry) == bcpup)
		err = au_may_add(dentry, bcpup, h_parent,
				 au_ftest_wrdir(wr_dir_args->flags, ISDIR));
	else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN))
		err = -ENAMETOOLONG;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out_unpin;

	br = au_sbr(sb, bcpup);
	if (dt) {
		struct path tmp = {
			.dentry	= h_parent,
			.mnt	= br->br_mnt
		};
		au_dtime_store(dt, au_pinned_parent(pin), &tmp);
	}

	wh_dentry = NULL;
	if (bcpup != au_dbwh(dentry))
		goto out; /* success */

	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);

out_unpin:
	if (IS_ERR(wh_dentry))
		au_unpin(pin);
out:
	return wh_dentry;
}

/* ---------------------------------------------------------------------- */

enum { Mknod, Symlink, Creat };
struct simple_arg {
	int type;
	union {
		struct {
			int mode;
			struct nameidata *nd;
		} c;
		struct {
			const char *symname;
		} s;
		struct {
			int mode;
			dev_t dev;
		} m;
	} u;
};

static int add_simple(struct inode *dir, struct dentry *dentry,
		      struct simple_arg *arg)
{
	int err;
	aufs_bindex_t bstart;
	unsigned char created;
	struct au_dtime dt;
	struct au_pin pin;
	struct path h_path;
	struct dentry *wh_dentry, *parent;
	struct inode *h_dir;
	struct au_wr_dir_args wr_dir_args = {
		.force_btgt	= -1,
		.flags		= AuWrDir_ADD_ENTRY
	};

	AuDbg("%.*s\n", AuDLNPair(dentry));
	IMustLock(dir);

	parent = dentry->d_parent; /* dir inode is locked */
	err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
	if (unlikely(err))
		goto out;
	err = au_d_may_add(dentry);
	if (unlikely(err))
		goto out_unlock;
	di_write_lock_parent(parent);
	wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin,
				      &wr_dir_args);
	err = PTR_ERR(wh_dentry);
	if (IS_ERR(wh_dentry))
		goto out_parent;

	bstart = au_dbstart(dentry);
	h_path.dentry = au_h_dptr(dentry, bstart);
	h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
	h_dir = au_pinned_h_dir(&pin);
	switch (arg->type) {
	case Creat:
		err = vfsub_create(h_dir, &h_path, arg->u.c.mode);
		break;
	case Symlink:
		err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);
		break;
	case Mknod:
		err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);
		break;
	default:
		BUG();
	}
	created = !err;
	if (!err)
		err = epilog(dir, bstart, wh_dentry, dentry);

	/* revert */
	if (unlikely(created && err && h_path.dentry->d_inode)) {
		int rerr;
		rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
		if (rerr) {
			AuIOErr("%.*s revert failure(%d, %d)\n",
				AuDLNPair(dentry), err, rerr);
			err = -EIO;
		}
		au_dtime_revert(&dt);
	}

	au_unpin(&pin);
	dput(wh_dentry);

out_parent:
	di_write_unlock(parent);
out_unlock:
	if (unlikely(err)) {
		au_update_dbstart(dentry);
		d_drop(dentry);
	}
	aufs_read_unlock(dentry, AuLock_DW);
out:
	return err;
}

int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
	struct simple_arg arg = {
		.type = Mknod,
		.u.m = {
			.mode	= mode,
			.dev	= dev
		}
	};
	return add_simple(dir, dentry, &arg);
}

int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
	struct simple_arg arg = {
		.type = Symlink,
		.u.s.symname = symname
	};
	return add_simple(dir, dentry, &arg);
}

int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	struct simple_arg arg = {
		.type = Creat,
		.u.c = {
			.mode	= mode,
			.nd	= nd
		}
	};
	return add_simple(dir, dentry, &arg);
}

/* ---------------------------------------------------------------------- */

struct au_link_args {
	aufs_bindex_t bdst, bsrc;
	struct au_pin pin;
	struct path h_path;
	struct dentry *src_parent, *parent;
};

static int au_cpup_before_link(struct dentry *src_dentry,
			       struct au_link_args *a)
{
	int err;
	struct dentry *h_src_dentry;
	struct mutex *h_mtx;
	struct file *h_file;

	di_read_lock_parent(a->src_parent, AuLock_IR);
	err = au_test_and_cpup_dirs(src_dentry, a->bdst);
	if (unlikely(err))
		goto out;

	h_src_dentry = au_h_dptr(src_dentry, a->bsrc);
	h_mtx = &h_src_dentry->d_inode->i_mutex;
	err = au_pin(&a->pin, src_dentry, a->bdst,
		     au_opt_udba(src_dentry->d_sb),
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	if (unlikely(err))
		goto out;
	mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
	h_file = au_h_open_pre(src_dentry, a->bsrc);
	if (IS_ERR(h_file)) {
		err = PTR_ERR(h_file);
		h_file = NULL;
	} else
		err = au_sio_cpup_simple(src_dentry, a->bdst, a->bsrc,
					 AuCpup_DTIME /* | AuCpup_KEEPLINO */);
	mutex_unlock(h_mtx);
	au_h_open_post(src_dentry, a->bsrc, h_file);
	au_unpin(&a->pin);

out:
	di_read_unlock(a->src_parent, AuLock_IR);
	return err;
}

static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
{
	int err;
	unsigned char plink;
	struct inode *h_inode, *inode;
	struct dentry *h_src_dentry;
	struct super_block *sb;
	struct file *h_file;

	plink = 0;
	h_inode = NULL;
	sb = src_dentry->d_sb;
	inode = src_dentry->d_inode;
	if (au_ibstart(inode) <= a->bdst)
		h_inode = au_h_iptr(inode, a->bdst);
	if (!h_inode || !h_inode->i_nlink) {
		/* copyup src_dentry as the name of dentry. */
		au_set_dbstart(src_dentry, a->bdst);
		au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry));
		h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode;
		mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
		h_file = au_h_open_pre(src_dentry, a->bsrc);
		if (IS_ERR(h_file)) {
			err = PTR_ERR(h_file);
			h_file = NULL;
		} else
			err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc,
						 -1, AuCpup_KEEPLINO,
						 a->parent);
		mutex_unlock(&h_inode->i_mutex);
		au_h_open_post(src_dentry, a->bsrc, h_file);
		au_set_h_dptr(src_dentry, a->bdst, NULL);
		au_set_dbstart(src_dentry, a->bsrc);
	} else {
		/* the inode of src_dentry already exists on a.bdst branch */
		h_src_dentry = d_find_alias(h_inode);
		if (!h_src_dentry && au_plink_test(inode)) {
			plink = 1;
			h_src_dentry = au_plink_lkup(inode, a->bdst);
			err = PTR_ERR(h_src_dentry);
			if (IS_ERR(h_src_dentry))
				goto out;

			if (unlikely(!h_src_dentry->d_inode)) {
				dput(h_src_dentry);
				h_src_dentry = NULL;
			}

		}
		if (h_src_dentry) {
			err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
					 &a->h_path);
			dput(h_src_dentry);
		} else {
			AuIOErr("no dentry found for hi%lu on b%d\n",
				h_inode->i_ino, a->bdst);
			err = -EIO;
		}
	}

	if (!err && !plink)
		au_plink_append(inode, a->bdst, a->h_path.dentry);

out:
	AuTraceErr(err);
	return err;
}