/*
 * when an error happened, remove the created whiteout and revert everything.
 */
static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex,
		     aufs_bindex_t bwh, struct dentry *wh_dentry,
		     struct dentry *dentry, struct au_dtime *dt)
{
	int rerr;
	struct path h_path = {
		.dentry	= wh_dentry,
		.mnt	= au_sbr_mnt(dir->i_sb, bindex)
	};

	rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry);
	if (!rerr) {
		au_set_dbwh(dentry, bwh);
		au_dtime_revert(dt);
		return 0;
	}

	AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
		AuDLNPair(dentry), err, rerr);
	return -EIO;
}

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

int aufs_unlink(struct inode *dir, struct dentry *dentry)
{
	int err;
	aufs_bindex_t bwh, bindex, bstart;
	struct au_dtime dt;
	struct au_pin pin;
	struct path h_path;
	struct inode *inode, *h_dir;
	struct dentry *parent, *wh_dentry;

	IMustLock(dir);

	err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
	if (unlikely(err))
		goto out;
	err = au_d_hashed_positive(dentry);
	if (unlikely(err))
		goto out_unlock;
	inode = dentry->d_inode;
	IMustLock(inode);
	err = -EISDIR;
	if (unlikely(S_ISDIR(inode->i_mode)))
		goto out_unlock; /* possible? */

	bstart = au_dbstart(dentry);
	bwh = au_dbwh(dentry);
	bindex = -1;
	parent = dentry->d_parent; /* dir inode is locked */
	di_write_lock_parent(parent);
	wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin);
	err = PTR_ERR(wh_dentry);
	if (IS_ERR(wh_dentry))
		goto out_parent;

	h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
	h_path.dentry = au_h_dptr(dentry, bstart);
	dget(h_path.dentry);
	if (bindex == bstart) {
		h_dir = au_pinned_h_dir(&pin);
		err = vfsub_unlink(h_dir, &h_path, /*force*/0);
	} else {
		/* dir inode is locked */
		h_dir = wh_dentry->d_parent->d_inode;
		IMustLock(h_dir);
		err = 0;
	}

	if (!err) {
		vfsub_drop_nlink(inode);
		epilog(dir, dentry, bindex);

		/* update target timestamps */
		if (bindex == bstart) {
			vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
			inode->i_ctime = h_path.dentry->d_inode->i_ctime;
		} else
			/* todo: this timestamp may be reverted later */
			inode->i_ctime = h_dir->i_ctime;
		goto out_unpin; /* success */
	}

	/* revert */
	if (wh_dentry) {
		int rerr;

		rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt);
		if (rerr)
			err = rerr;
	}

out_unpin:
	au_unpin(&pin);
	dput(wh_dentry);
	dput(h_path.dentry);
out_parent:
	di_write_unlock(parent);
out_unlock:
	aufs_read_unlock(dentry, AuLock_DW);
out:
	return err;
}
Ejemplo n.º 2
0
int aufs_link(struct dentry *src_dentry, struct inode *dir,
              struct dentry *dentry)
{
    int err, rerr;
    struct au_dtime dt;
    struct au_link_args *a;
    struct dentry *wh_dentry, *h_src_dentry;
    struct inode *inode;
    struct super_block *sb;
    struct au_wr_dir_args wr_dir_args = {
        /* .force_btgt	= -1, */
        .flags		= AuWrDir_ADD_ENTRY
    };

    IMustLock(dir);
    inode = src_dentry->d_inode;
    IMustLock(inode);

    err = -ENOMEM;
    a = kzalloc(sizeof(*a), GFP_NOFS);
    if (unlikely(!a))
        goto out;

    a->parent = dentry->d_parent; /* dir inode is locked */
    err = aufs_read_and_write_lock2(dentry, src_dentry,
                                    AuLock_NOPLM | AuLock_GEN);
    if (unlikely(err))
        goto out_kfree;
    err = au_d_hashed_positive(src_dentry);
    if (unlikely(err))
        goto out_unlock;
    err = au_d_may_add(dentry);
    if (unlikely(err))
        goto out_unlock;

    a->src_parent = dget_parent(src_dentry);
    wr_dir_args.force_btgt = au_ibstart(inode);

    di_write_lock_parent(a->parent);
    wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);
    wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin,
                                  &wr_dir_args);
    err = PTR_ERR(wh_dentry);
    if (IS_ERR(wh_dentry))
        goto out_parent;

    err = 0;
    sb = dentry->d_sb;
    a->bdst = au_dbstart(dentry);
    a->h_path.dentry = au_h_dptr(dentry, a->bdst);
    a->h_path.mnt = au_sbr_mnt(sb, a->bdst);
    a->bsrc = au_ibstart(inode);
    h_src_dentry = au_h_d_alias(src_dentry, a->bsrc);
    if (!h_src_dentry) {
        a->bsrc = au_dbstart(src_dentry);
        h_src_dentry = au_h_d_alias(src_dentry, a->bsrc);
        AuDebugOn(!h_src_dentry);
    } else if (IS_ERR(h_src_dentry))
        goto out_parent;

    if (au_opt_test(au_mntflags(sb), PLINK)) {
        if (a->bdst < a->bsrc
                /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */)
            err = au_cpup_or_link(src_dentry, a);
        else
            err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
                             &a->h_path);
        dput(h_src_dentry);
    } else {
        /*
         * copyup src_dentry to the branch we process,
         * and then link(2) to it.
         */
        dput(h_src_dentry);
        if (a->bdst < a->bsrc
                /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) {
            au_unpin(&a->pin);
            di_write_unlock(a->parent);
            err = au_cpup_before_link(src_dentry, a);
            di_write_lock_parent(a->parent);
            if (!err)
                err = au_pin(&a->pin, dentry, a->bdst,
                             au_opt_udba(sb),
                             AuPin_DI_LOCKED | AuPin_MNT_WRITE);
            if (unlikely(err))
                goto out_wh;
        }
        if (!err) {
            h_src_dentry = au_h_dptr(src_dentry, a->bdst);
            err = -ENOENT;
            if (h_src_dentry && h_src_dentry->d_inode)
                err = vfsub_link(h_src_dentry,
                                 au_pinned_h_dir(&a->pin),
                                 &a->h_path);
        }
    }
    if (unlikely(err))
        goto out_unpin;

    if (wh_dentry) {
        a->h_path.dentry = wh_dentry;
        err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path,
                                  dentry);
        if (unlikely(err))
            goto out_revert;
    }

    dir->i_version++;
    if (au_ibstart(dir) == au_dbstart(dentry))
        au_cpup_attr_timesizes(dir);
    inc_nlink(inode);
    inode->i_ctime = dir->i_ctime;
    d_instantiate(dentry, au_igrab(inode));
    if (d_unhashed(a->h_path.dentry))
        /* some filesystem calls d_drop() */
        d_drop(dentry);
    goto out_unpin; /* success */

out_revert:
    rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0);
    if (unlikely(rerr)) {
        AuIOErr("%.*s reverting failed(%d, %d)\n",
                AuDLNPair(dentry), err, rerr);
        err = -EIO;
    }
    au_dtime_revert(&dt);
out_unpin:
    au_unpin(&a->pin);
out_wh:
    dput(wh_dentry);
out_parent:
    di_write_unlock(a->parent);
    dput(a->src_parent);
out_unlock:
    if (unlikely(err)) {
        au_update_dbstart(dentry);
        d_drop(dentry);
    }
    aufs_read_and_write_unlock2(dentry, src_dentry);
out_kfree:
    kfree(a);
out:
    return err;
}
Ejemplo n.º 3
0
int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
    int err, rerr;
    aufs_bindex_t bindex;
    unsigned char diropq;
    struct path h_path;
    struct dentry *wh_dentry, *parent, *opq_dentry;
    struct mutex *h_mtx;
    struct super_block *sb;
    struct {
        struct au_pin pin;
        struct au_dtime dt;
    } *a; /* reduce the stack usage */
    struct au_wr_dir_args wr_dir_args = {
        .force_btgt	= -1,
        .flags		= AuWrDir_ADD_ENTRY | AuWrDir_ISDIR
    };

    IMustLock(dir);

    err = -ENOMEM;
    a = kmalloc(sizeof(*a), GFP_NOFS);
    if (unlikely(!a))
        goto out;

    err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
    if (unlikely(err))
        goto out_free;
    err = au_d_may_add(dentry);
    if (unlikely(err))
        goto out_unlock;

    parent = dentry->d_parent; /* dir inode is locked */
    di_write_lock_parent(parent);
    wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
                                  &a->pin, &wr_dir_args);
    err = PTR_ERR(wh_dentry);
    if (IS_ERR(wh_dentry))
        goto out_parent;

    sb = dentry->d_sb;
    bindex = au_dbstart(dentry);
    h_path.dentry = au_h_dptr(dentry, bindex);
    h_path.mnt = au_sbr_mnt(sb, bindex);
    err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode);
    if (unlikely(err))
        goto out_unpin;

    /* make the dir opaque */
    diropq = 0;
    h_mtx = &h_path.dentry->d_inode->i_mutex;
    if (wh_dentry
            || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) {
        mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
        opq_dentry = au_diropq_create(dentry, bindex);
        mutex_unlock(h_mtx);
        err = PTR_ERR(opq_dentry);
        if (IS_ERR(opq_dentry))
            goto out_dir;
        dput(opq_dentry);
        diropq = 1;
    }

    err = epilog(dir, bindex, wh_dentry, dentry);
    if (!err) {
        inc_nlink(dir);
        goto out_unpin; /* success */
    }

    /* revert */
    if (diropq) {
        AuLabel(revert opq);
        mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
        rerr = au_diropq_remove(dentry, bindex);
        mutex_unlock(h_mtx);
        if (rerr) {
            AuIOErr("%.*s reverting diropq failed(%d, %d)\n",
                    AuDLNPair(dentry), err, rerr);
            err = -EIO;
        }
    }

out_dir:
    AuLabel(revert dir);
    rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path);
    if (rerr) {
        AuIOErr("%.*s reverting dir failed(%d, %d)\n",
                AuDLNPair(dentry), err, rerr);
        err = -EIO;
    }
    au_dtime_revert(&a->dt);
out_unpin:
    au_unpin(&a->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_free:
    kfree(a);
out:
    return err;
}
Ejemplo n.º 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 {
            umode_t mode;
            struct nameidata *nd;
        } c;
        struct {
            const char *symname;
        } s;
        struct {
            umode_t 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, umode_t 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, umode_t 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, -1,
                                 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;
}