Beispiel #1
0
struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
                           unsigned int mode, dev_t dev)
{
    struct inode *inode = NULL;
    struct gfs2_inode *dip = ghs->gh_gl->gl_object;
    struct inode *dir = &dip->i_inode;
    struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
    struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 };
    int error;
    u64 generation;
    struct buffer_head *bh = NULL;

    if (!name->len || name->len > GFS2_FNAMESIZE)
        return ERR_PTR(-ENAMETOOLONG);

    gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs);
    error = gfs2_glock_nq(ghs);
    if (error)
        goto fail;

    error = create_ok(dip, name, mode);
    if (error)
        goto fail_gunlock;

    error = pick_formal_ino(sdp, &inum.no_formal_ino);
    if (error)
        goto fail_gunlock;

    error = alloc_dinode(dip, &inum.no_addr, &generation);
    if (error)
        goto fail_gunlock;

    error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops,
                              LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1);
    if (error)
        goto fail_gunlock;

    error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh);
    if (error)
        goto fail_gunlock2;

    inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode),
                              inum.no_addr,
                              inum.no_formal_ino, 0);
    if (IS_ERR(inode))
        goto fail_gunlock2;

    error = gfs2_inode_refresh(GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    error = gfs2_acl_create(dip, GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    error = gfs2_security_init(dip, GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    error = link_dinode(dip, name, GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    if (bh)
        brelse(bh);
    if (!inode)
        return ERR_PTR(-ENOMEM);
    return inode;

fail_gunlock2:
    gfs2_glock_dq_uninit(ghs + 1);
    if (inode)
        iput(inode);
fail_gunlock:
    gfs2_glock_dq(ghs);
fail:
    if (bh)
        brelse(bh);
    return ERR_PTR(error);
}

/**
 * gfs2_rmdiri - Remove a directory
 * @dip: The parent directory of the directory to be removed
 * @name: The name of the directory to be removed
 * @ip: The GFS2 inode of the directory to be removed
 *
 * Assumes Glocks on dip and ip are held
 *
 * Returns: errno
 */

int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name,
                struct gfs2_inode *ip)
{
    struct qstr dotname;
    int error;

    if (ip->i_di.di_entries != 2) {
        if (gfs2_consist_inode(ip))
            gfs2_dinode_print(ip);
        return -EIO;
    }

    error = gfs2_dir_del(dip, name);
    if (error)
        return error;

    error = gfs2_change_nlink(dip, -1);
    if (error)
        return error;

    gfs2_str2qstr(&dotname, ".");
    error = gfs2_dir_del(ip, &dotname);
    if (error)
        return error;

    gfs2_str2qstr(&dotname, "..");
    error = gfs2_dir_del(ip, &dotname);
    if (error)
        return error;

    /* It looks odd, but it really should be done twice */
    error = gfs2_change_nlink(ip, -1);
    if (error)
        return error;

    error = gfs2_change_nlink(ip, -1);
    if (error)
        return error;

    return error;
}

/*
 * gfs2_unlink_ok - check to see that a inode is still in a directory
 * @dip: the directory
 * @name: the name of the file
 * @ip: the inode
 *
 * Assumes that the lock on (at least) @dip is held.
 *
 * Returns: 0 if the parent/child relationship is correct, errno if it isn't
 */

int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
                   const struct gfs2_inode *ip)
{
    int error;

    if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
        return -EPERM;

    if ((dip->i_inode.i_mode & S_ISVTX) &&
            dip->i_inode.i_uid != current->fsuid &&
            ip->i_inode.i_uid != current->fsuid && !capable(CAP_FOWNER))
        return -EPERM;

    if (IS_APPEND(&dip->i_inode))
        return -EPERM;

    error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL);
    if (error)
        return error;

    error = gfs2_dir_check(&dip->i_inode, name, ip);
    if (error)
        return error;

    return 0;
}
Beispiel #2
0
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
			     umode_t mode, dev_t dev, const char *symname,
			     unsigned int size, int excl)
{
	const struct qstr *name = &dentry->d_name;
	struct gfs2_holder ghs[2];
	struct inode *inode = NULL;
	struct gfs2_inode *dip = GFS2_I(dir), *ip;
	struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
	struct gfs2_glock *io_gl;
	int error;
	u32 aflags = 0;
	int arq;

	if (!name->len || name->len > GFS2_FNAMESIZE)
		return -ENAMETOOLONG;

	error = gfs2_rs_alloc(dip);
	if (error)
		return error;

	error = gfs2_rindex_update(sdp);
	if (error)
		return error;

	error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
	if (error)
		goto fail;

	error = create_ok(dip, name, mode);
	if ((error == -EEXIST) && S_ISREG(mode) && !excl) {
		inode = gfs2_lookupi(dir, &dentry->d_name, 0);
		gfs2_glock_dq_uninit(ghs);
		d_instantiate(dentry, inode);
		return IS_ERR(inode) ? PTR_ERR(inode) : 0;
	}
	if (error)
		goto fail_gunlock;

	arq = error = gfs2_diradd_alloc_required(dir, name);
	if (error < 0)
		goto fail_gunlock;

	inode = new_inode(sdp->sd_vfs);
	error = -ENOMEM;
	if (!inode)
		goto fail_gunlock;

	ip = GFS2_I(inode);
	error = gfs2_rs_alloc(ip);
	if (error)
		goto fail_free_inode;

	inode->i_mode = mode;
	set_nlink(inode, S_ISDIR(mode) ? 2 : 1);
	inode->i_rdev = dev;
	inode->i_size = size;
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	gfs2_set_inode_blocks(inode, 1);
	munge_mode_uid_gid(dip, inode);
	ip->i_goal = dip->i_goal;
	ip->i_diskflags = 0;
	ip->i_eattr = 0;
	ip->i_height = 0;
	ip->i_depth = 0;
	ip->i_entries = 0;

	switch(mode & S_IFMT) {
	case S_IFREG:
		if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) ||
		    gfs2_tune_get(sdp, gt_new_files_jdata))
			ip->i_diskflags |= GFS2_DIF_JDATA;
		gfs2_set_aops(inode);
		break;
	case S_IFDIR:
		ip->i_diskflags |= (dip->i_diskflags & GFS2_DIF_INHERIT_JDATA);
		ip->i_diskflags |= GFS2_DIF_JDATA;
		ip->i_entries = 2;
		break;
	}
	gfs2_set_inode_flags(inode);

	if ((GFS2_I(sdp->sd_root_dir->d_inode) == dip) ||
	    (dip->i_diskflags & GFS2_DIF_TOPDIR))
		aflags |= GFS2_AF_ORLOV;

	error = alloc_dinode(ip, aflags);
	if (error)
		goto fail_free_inode;

	error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
	if (error)
		goto fail_free_inode;

	ip->i_gl->gl_object = ip;
	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1);
	if (error)
		goto fail_free_inode;

	error = gfs2_trans_begin(sdp, RES_DINODE, 0);
	if (error)
		goto fail_gunlock2;

	init_dinode(dip, ip, symname);
	gfs2_trans_end(sdp);

	error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
	if (error)
		goto fail_gunlock2;

	error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh);
	if (error)
		goto fail_gunlock2;

	ip->i_iopen_gh.gh_gl->gl_object = ip;
	gfs2_glock_put(io_gl);
	gfs2_set_iop(inode);
	insert_inode_hash(inode);

	error = gfs2_acl_create(dip, inode);
	if (error)
		goto fail_gunlock3;

	error = gfs2_security_init(dip, ip, name);
	if (error)
		goto fail_gunlock3;

	error = link_dinode(dip, name, ip, arq);
	if (error)
		goto fail_gunlock3;

	mark_inode_dirty(inode);
	gfs2_glock_dq_uninit(ghs);
	gfs2_glock_dq_uninit(ghs + 1);
	d_instantiate(dentry, inode);
	return 0;

fail_gunlock3:
	gfs2_glock_dq_uninit(ghs + 1);
	if (ip->i_gl)
		gfs2_glock_put(ip->i_gl);
	goto fail_gunlock;

fail_gunlock2:
	gfs2_glock_dq_uninit(ghs + 1);
fail_free_inode:
	if (ip->i_gl)
		gfs2_glock_put(ip->i_gl);
	gfs2_rs_delete(ip);
	free_inode_nonrcu(inode);
	inode = NULL;
fail_gunlock:
	gfs2_glock_dq_uninit(ghs);
	if (inode && !IS_ERR(inode)) {
		clear_nlink(inode);
		mark_inode_dirty(inode);
		set_bit(GIF_ALLOC_FAILED, &GFS2_I(inode)->i_flags);
		iput(inode);
	}
fail:
	return error;
}
Beispiel #3
0
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
                             unsigned int mode, dev_t dev, const char *symname,
                             unsigned int size)
{
    const struct qstr *name = &dentry->d_name;
    struct gfs2_holder ghs[2];
    struct inode *inode = NULL;
    struct gfs2_inode *dip = GFS2_I(dir);
    struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
    struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 };
    int error;
    u64 generation;
    struct buffer_head *bh = NULL;

    if (!name->len || name->len > GFS2_FNAMESIZE)
        return -ENAMETOOLONG;

    error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
    if (error)
        goto fail;

    error = create_ok(dip, name, mode);
    if (error)
        goto fail_gunlock;

    error = alloc_dinode(dip, &inum.no_addr, &generation);
    if (error)
        goto fail_gunlock;
    inum.no_formal_ino = generation;

    error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops,
                              LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1);
    if (error)
        goto fail_gunlock;

    error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, symname, size, &bh);
    if (error)
        goto fail_gunlock2;

    inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr,
                              inum.no_formal_ino, 0);
    if (IS_ERR(inode))
        goto fail_gunlock2;

    error = gfs2_inode_refresh(GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    error = gfs2_acl_create(dip, inode);
    if (error)
        goto fail_gunlock2;

    error = gfs2_security_init(dip, GFS2_I(inode), name);
    if (error)
        goto fail_gunlock2;

    error = link_dinode(dip, name, GFS2_I(inode));
    if (error)
        goto fail_gunlock2;

    if (bh)
        brelse(bh);

    gfs2_trans_end(sdp);
    if (dip->i_alloc->al_rgd)
        gfs2_inplace_release(dip);
    gfs2_quota_unlock(dip);
    gfs2_alloc_put(dip);
    gfs2_glock_dq_uninit_m(2, ghs);
    mark_inode_dirty(inode);
    d_instantiate(dentry, inode);
    return 0;

fail_gunlock2:
    gfs2_glock_dq_uninit(ghs + 1);
    if (inode && !IS_ERR(inode))
        iput(inode);
fail_gunlock:
    gfs2_glock_dq_uninit(ghs);
fail:
    if (bh)
        brelse(bh);
    return error;
}

/**
 * gfs2_create - Create a file
 * @dir: The directory in which to create the file
 * @dentry: The dentry of the new file
 * @mode: The mode of the new file
 *
 * Returns: errno
 */

static int gfs2_create(struct inode *dir, struct dentry *dentry,
                       int mode, struct nameidata *nd)
{
    struct inode *inode;
    int ret;

    for (;;) {
        ret = gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0);
        if (ret != -EEXIST || (nd && (nd->flags & LOOKUP_EXCL)))
            return ret;

        inode = gfs2_lookupi(dir, &dentry->d_name, 0);
        if (inode) {
            if (!IS_ERR(inode))
                break;
            return PTR_ERR(inode);
        }
    }

    d_instantiate(dentry, inode);
    return 0;
}

/**
 * gfs2_lookup - Look up a filename in a directory and return its inode
 * @dir: The directory inode
 * @dentry: The dentry of the new inode
 * @nd: passed from Linux VFS, ignored by us
 *
 * Called by the VFS layer. Lock dir and call gfs2_lookupi()
 *
 * Returns: errno
 */

static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
                                  struct nameidata *nd)
{
    struct inode *inode = NULL;

    inode = gfs2_lookupi(dir, &dentry->d_name, 0);
    if (inode && IS_ERR(inode))
        return ERR_CAST(inode);

    if (inode) {
        struct gfs2_glock *gl = GFS2_I(inode)->i_gl;
        struct gfs2_holder gh;
        int error;
        error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
        if (error) {
            iput(inode);
            return ERR_PTR(error);
        }
        gfs2_glock_dq_uninit(&gh);
        return d_splice_alias(inode, dentry);
    }
    d_add(dentry, inode);

    return NULL;
}

/**
 * gfs2_link - Link to a file
 * @old_dentry: The inode to link
 * @dir: Add link to this directory
 * @dentry: The name of the link
 *
 * Link the inode in "old_dentry" into the directory "dir" with the
 * name in "dentry".
 *
 * Returns: errno
 */

static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
                     struct dentry *dentry)
{
    struct gfs2_inode *dip = GFS2_I(dir);
    struct gfs2_sbd *sdp = GFS2_SB(dir);
    struct inode *inode = old_dentry->d_inode;
    struct gfs2_inode *ip = GFS2_I(inode);
    struct gfs2_holder ghs[2];
    struct buffer_head *dibh;
    int alloc_required;
    int error;

    if (S_ISDIR(inode->i_mode))
        return -EPERM;

    gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
    gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);

    error = gfs2_glock_nq(ghs); /* parent */
    if (error)
        goto out_parent;

    error = gfs2_glock_nq(ghs + 1); /* child */
    if (error)
        goto out_child;

    error = -ENOENT;
    if (inode->i_nlink == 0)
        goto out_gunlock;

    error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
    if (error)
        goto out_gunlock;

    error = gfs2_dir_check(dir, &dentry->d_name, NULL);
    switch (error) {
    case -ENOENT:
        break;
    case 0:
        error = -EEXIST;
    default:
        goto out_gunlock;
    }

    error = -EINVAL;
    if (!dip->i_inode.i_nlink)
        goto out_gunlock;
    error = -EFBIG;
    if (dip->i_entries == (u32)-1)
        goto out_gunlock;
    error = -EPERM;
    if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
        goto out_gunlock;
    error = -EINVAL;
    if (!ip->i_inode.i_nlink)
        goto out_gunlock;
    error = -EMLINK;
    if (ip->i_inode.i_nlink == (u32)-1)
        goto out_gunlock;

    alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name);
    if (error < 0)
        goto out_gunlock;
    error = 0;

    if (alloc_required) {
        struct gfs2_alloc *al = gfs2_alloc_get(dip);
        if (!al) {
            error = -ENOMEM;
            goto out_gunlock;
        }

        error = gfs2_quota_lock_check(dip);
        if (error)
            goto out_alloc;

        al->al_requested = sdp->sd_max_dirres;

        error = gfs2_inplace_reserve(dip);
        if (error)
            goto out_gunlock_q;

        error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
                                 gfs2_rg_blocks(al) +
                                 2 * RES_DINODE + RES_STATFS +
                                 RES_QUOTA, 0);
        if (error)
            goto out_ipres;
    } else {
        error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0);
        if (error)
            goto out_ipres;
    }

    error = gfs2_meta_inode_buffer(ip, &dibh);
    if (error)
        goto out_end_trans;

    error = gfs2_dir_add(dir, &dentry->d_name, ip);
    if (error)
        goto out_brelse;

    gfs2_trans_add_bh(ip->i_gl, dibh, 1);
    inc_nlink(&ip->i_inode);
    ip->i_inode.i_ctime = CURRENT_TIME;
    gfs2_dinode_out(ip, dibh->b_data);
    mark_inode_dirty(&ip->i_inode);

out_brelse:
    brelse(dibh);
out_end_trans:
    gfs2_trans_end(sdp);
out_ipres:
    if (alloc_required)
        gfs2_inplace_release(dip);
out_gunlock_q:
    if (alloc_required)
        gfs2_quota_unlock(dip);
out_alloc:
    if (alloc_required)
        gfs2_alloc_put(dip);
out_gunlock:
    gfs2_glock_dq(ghs + 1);
out_child:
    gfs2_glock_dq(ghs);
out_parent:
    gfs2_holder_uninit(ghs);
    gfs2_holder_uninit(ghs + 1);
    if (!error) {
        ihold(inode);
        d_instantiate(dentry, inode);
        mark_inode_dirty(inode);
    }
    return error;
}

/*
 * gfs2_unlink_ok - check to see that a inode is still in a directory
 * @dip: the directory
 * @name: the name of the file
 * @ip: the inode
 *
 * Assumes that the lock on (at least) @dip is held.
 *
 * Returns: 0 if the parent/child relationship is correct, errno if it isn't
 */

static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
                          const struct gfs2_inode *ip)
{
    int error;

    if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
        return -EPERM;

    if ((dip->i_inode.i_mode & S_ISVTX) &&
            dip->i_inode.i_uid != current_fsuid() &&
            ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER))
        return -EPERM;

    if (IS_APPEND(&dip->i_inode))
        return -EPERM;

    error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
    if (error)
        return error;

    error = gfs2_dir_check(&dip->i_inode, name, ip);
    if (error)
        return error;

    return 0;
}