diff options
-rw-r--r-- | fs/gfs2/inode.c | 711 | ||||
-rw-r--r-- | fs/gfs2/ops_inode.c | 711 |
2 files changed, 711 insertions, 711 deletions
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 5d48baf..b95ee5d 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -35,717 +35,6 @@ #include "trans.h" #include "util.h" -struct gfs2_skip_data { - u64 no_addr; - int skipped; - int non_block; -}; - -static int iget_test(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_skip_data *data = opaque; - - if (ip->i_no_addr == data->no_addr) { - if (data->non_block && - inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { - data->skipped = 1; - return 0; - } - return 1; - } - return 0; -} - -static int iget_set(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_skip_data *data = opaque; - - if (data->skipped) - return -ENOENT; - inode->i_ino = (unsigned long)(data->no_addr); - ip->i_no_addr = data->no_addr; - return 0; -} - -struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block) -{ - unsigned long hash = (unsigned long)no_addr; - struct gfs2_skip_data data; - - data.no_addr = no_addr; - data.skipped = 0; - data.non_block = non_block; - return ilookup5(sb, hash, iget_test, &data); -} - -static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr, - int non_block) -{ - struct gfs2_skip_data data; - unsigned long hash = (unsigned long)no_addr; - - data.no_addr = no_addr; - data.skipped = 0; - data.non_block = non_block; - return iget5_locked(sb, hash, iget_test, iget_set, &data); -} - -/** - * gfs2_set_iop - Sets inode operations - * @inode: The inode with correct i_mode filled in - * - * GFS2 lookup code fills in vfs inode contents based on info obtained - * from directory entry inside gfs2_inode_lookup(). - */ - -static void gfs2_set_iop(struct inode *inode) -{ - struct gfs2_sbd *sdp = GFS2_SB(inode); - umode_t mode = inode->i_mode; - - if (S_ISREG(mode)) { - inode->i_op = &gfs2_file_iops; - if (gfs2_localflocks(sdp)) - inode->i_fop = &gfs2_file_fops_nolock; - else - inode->i_fop = &gfs2_file_fops; - } else if (S_ISDIR(mode)) { - inode->i_op = &gfs2_dir_iops; - if (gfs2_localflocks(sdp)) - inode->i_fop = &gfs2_dir_fops_nolock; - else - inode->i_fop = &gfs2_dir_fops; - } else if (S_ISLNK(mode)) { - inode->i_op = &gfs2_symlink_iops; - } else { - inode->i_op = &gfs2_file_iops; - init_special_inode(inode, inode->i_mode, inode->i_rdev); - } -} - -/** - * gfs2_inode_lookup - Lookup an inode - * @sb: The super block - * @no_addr: The inode number - * @type: The type of the inode - * non_block: Can we block on inodes that are being freed? - * - * Returns: A VFS inode, or an error - */ - -struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, - u64 no_addr, u64 no_formal_ino, int non_block) -{ - struct inode *inode; - struct gfs2_inode *ip; - struct gfs2_glock *io_gl = NULL; - int error; - - inode = gfs2_iget(sb, no_addr, non_block); - ip = GFS2_I(inode); - - if (!inode) - return ERR_PTR(-ENOBUFS); - - if (inode->i_state & I_NEW) { - struct gfs2_sbd *sdp = GFS2_SB(inode); - ip->i_no_formal_ino = no_formal_ino; - - error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); - if (unlikely(error)) - goto fail; - ip->i_gl->gl_object = ip; - - error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); - if (unlikely(error)) - goto fail_put; - - set_bit(GIF_INVALID, &ip->i_flags); - error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); - if (unlikely(error)) - goto fail_iopen; - - ip->i_iopen_gh.gh_gl->gl_object = ip; - gfs2_glock_put(io_gl); - io_gl = NULL; - - if (type == DT_UNKNOWN) { - /* Inode glock must be locked already */ - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) - goto fail_refresh; - } else { - inode->i_mode = DT2IF(type); - } - - gfs2_set_iop(inode); - unlock_new_inode(inode); - } - - return inode; - -fail_refresh: - ip->i_iopen_gh.gh_gl->gl_object = NULL; - gfs2_glock_dq_uninit(&ip->i_iopen_gh); -fail_iopen: - if (io_gl) - gfs2_glock_put(io_gl); -fail_put: - ip->i_gl->gl_object = NULL; - gfs2_glock_put(ip->i_gl); -fail: - iget_failed(inode); - return ERR_PTR(error); -} - -struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, - u64 *no_formal_ino, unsigned int blktype) -{ - struct super_block *sb = sdp->sd_vfs; - struct gfs2_holder i_gh; - struct inode *inode = NULL; - int error; - - /* Must not read in block until block type is verified */ - error = gfs2_glock_nq_num(sdp, no_addr, &gfs2_inode_glops, - LM_ST_EXCLUSIVE, GL_SKIP, &i_gh); - if (error) - return ERR_PTR(error); - - error = gfs2_check_blk_type(sdp, no_addr, blktype); - if (error) - goto fail; - - inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, 1); - if (IS_ERR(inode)) - goto fail; - - /* Two extra checks for NFS only */ - if (no_formal_ino) { - error = -ESTALE; - if (GFS2_I(inode)->i_no_formal_ino != *no_formal_ino) - goto fail_iput; - - error = -EIO; - if (GFS2_I(inode)->i_diskflags & GFS2_DIF_SYSTEM) - goto fail_iput; - - error = 0; - } - -fail: - gfs2_glock_dq_uninit(&i_gh); - return error ? ERR_PTR(error) : inode; -fail_iput: - iput(inode); - goto fail; -} - - -struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) -{ - struct qstr qstr; - struct inode *inode; - gfs2_str2qstr(&qstr, name); - inode = gfs2_lookupi(dip, &qstr, 1); - /* gfs2_lookupi has inconsistent callers: vfs - * related routines expect NULL for no entry found, - * gfs2_lookup_simple callers expect ENOENT - * and do not check for NULL. - */ - if (inode == NULL) - return ERR_PTR(-ENOENT); - else - return inode; -} - - -/** - * gfs2_lookupi - Look up a filename in a directory and return its inode - * @d_gh: An initialized holder for the directory glock - * @name: The name of the inode to look for - * @is_root: If 1, ignore the caller's permissions - * @i_gh: An uninitialized holder for the new inode glock - * - * This can be called via the VFS filldir function when NFS is doing - * a readdirplus and the inode which its intending to stat isn't - * already in cache. In this case we must not take the directory glock - * again, since the readdir call will have already taken that lock. - * - * Returns: errno - */ - -struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, - int is_root) -{ - struct super_block *sb = dir->i_sb; - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_holder d_gh; - int error = 0; - struct inode *inode = NULL; - int unlock = 0; - - if (!name->len || name->len > GFS2_FNAMESIZE) - return ERR_PTR(-ENAMETOOLONG); - - if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) || - (name->len == 2 && memcmp(name->name, "..", 2) == 0 && - dir == sb->s_root->d_inode)) { - igrab(dir); - return dir; - } - - if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) { - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); - if (error) - return ERR_PTR(error); - unlock = 1; - } - - if (!is_root) { - error = gfs2_permission(dir, MAY_EXEC, 0); - if (error) - goto out; - } - - inode = gfs2_dir_search(dir, name); - if (IS_ERR(inode)) - error = PTR_ERR(inode); -out: - if (unlock) - gfs2_glock_dq_uninit(&d_gh); - if (error == -ENOENT) - return NULL; - return inode ? inode : ERR_PTR(error); -} - -/** - * create_ok - OK to create a new on-disk inode here? - * @dip: Directory in which dinode is to be created - * @name: Name of new dinode - * @mode: - * - * Returns: errno - */ - -static int create_ok(struct gfs2_inode *dip, const struct qstr *name, - unsigned int mode) -{ - int error; - - error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0); - if (error) - return error; - - /* Don't create entries in an unlinked directory */ - if (!dip->i_inode.i_nlink) - return -ENOENT; - - error = gfs2_dir_check(&dip->i_inode, name, NULL); - switch (error) { - case -ENOENT: - error = 0; - break; - case 0: - return -EEXIST; - default: - return error; - } - - if (dip->i_entries == (u32)-1) - return -EFBIG; - if (S_ISDIR(mode) && dip->i_inode.i_nlink == (u32)-1) - return -EMLINK; - - return 0; -} - -static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, - unsigned int *uid, unsigned int *gid) -{ - if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && - (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { - if (S_ISDIR(*mode)) - *mode |= S_ISUID; - else if (dip->i_inode.i_uid != current_fsuid()) - *mode &= ~07111; - *uid = dip->i_inode.i_uid; - } else - *uid = current_fsuid(); - - if (dip->i_inode.i_mode & S_ISGID) { - if (S_ISDIR(*mode)) - *mode |= S_ISGID; - *gid = dip->i_inode.i_gid; - } else - *gid = current_fsgid(); -} - -static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - int error; - - if (gfs2_alloc_get(dip) == NULL) - return -ENOMEM; - - dip->i_alloc->al_requested = RES_DINODE; - error = gfs2_inplace_reserve(dip); - if (error) - goto out; - - error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS, 0); - if (error) - goto out_ipreserv; - - error = gfs2_alloc_di(dip, no_addr, generation); - - gfs2_trans_end(sdp); - -out_ipreserv: - gfs2_inplace_release(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -/** - * init_dinode - Fill in a new dinode structure - * @dip: the directory this inode is being created in - * @gl: The glock covering the new inode - * @inum: the inode number - * @mode: the file permissions - * @uid: - * @gid: - * - */ - -static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - const struct gfs2_inum_host *inum, unsigned int mode, - unsigned int uid, unsigned int gid, - const u64 *generation, dev_t dev, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_dinode *di; - struct buffer_head *dibh; - struct timespec tv = CURRENT_TIME; - - dibh = gfs2_meta_new(gl, inum->no_addr); - gfs2_trans_add_bh(gl, dibh, 1); - gfs2_metatype_set(dibh, GFS2_METATYPE_DI, GFS2_FORMAT_DI); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - di = (struct gfs2_dinode *)dibh->b_data; - - di->di_num.no_formal_ino = cpu_to_be64(inum->no_formal_ino); - di->di_num.no_addr = cpu_to_be64(inum->no_addr); - di->di_mode = cpu_to_be32(mode); - di->di_uid = cpu_to_be32(uid); - di->di_gid = cpu_to_be32(gid); - di->di_nlink = 0; - di->di_size = 0; - di->di_blocks = cpu_to_be64(1); - di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); - di->di_major = cpu_to_be32(MAJOR(dev)); - di->di_minor = cpu_to_be32(MINOR(dev)); - di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); - di->di_generation = cpu_to_be64(*generation); - di->di_flags = 0; - - if (S_ISREG(mode)) { - if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) || - gfs2_tune_get(sdp, gt_new_files_jdata)) - di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA); - } else if (S_ISDIR(mode)) { - di->di_flags |= cpu_to_be32(dip->i_diskflags & - GFS2_DIF_INHERIT_JDATA); - } - - di->__pad1 = 0; - di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0); - di->di_height = 0; - di->__pad2 = 0; - di->__pad3 = 0; - di->di_depth = 0; - di->di_entries = 0; - memset(&di->__pad4, 0, sizeof(di->__pad4)); - di->di_eattr = 0; - di->di_atime_nsec = cpu_to_be32(tv.tv_nsec); - di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); - di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); - memset(&di->di_reserved, 0, sizeof(di->di_reserved)); - - set_buffer_uptodate(dibh); - - *bhp = dibh; -} - -static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - unsigned int mode, const struct gfs2_inum_host *inum, - const u64 *generation, dev_t dev, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - unsigned int uid, gid; - int error; - - munge_mode_uid_gid(dip, &mode, &uid, &gid); - if (!gfs2_alloc_get(dip)) - return -ENOMEM; - - error = gfs2_quota_lock(dip, uid, gid); - if (error) - goto out; - - error = gfs2_quota_check(dip, uid, gid); - if (error) - goto out_quota; - - error = gfs2_trans_begin(sdp, RES_DINODE + RES_QUOTA, 0); - if (error) - goto out_quota; - - init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); - gfs2_quota_change(dip, +1, uid, gid); - gfs2_trans_end(sdp); - -out_quota: - gfs2_quota_unlock(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_alloc *al; - int alloc_required; - struct buffer_head *dibh; - int error; - - al = gfs2_alloc_get(dip); - if (!al) - return -ENOMEM; - - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto fail; - - error = alloc_required = gfs2_diradd_alloc_required(&dip->i_inode, name); - if (alloc_required < 0) - goto fail_quota_locks; - if (alloc_required) { - error = gfs2_quota_check(dip, dip->i_inode.i_uid, dip->i_inode.i_gid); - if (error) - goto fail_quota_locks; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(dip); - if (error) - goto fail_quota_locks; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_length + - 2 * RES_DINODE + - RES_STATFS + RES_QUOTA, 0); - if (error) - goto fail_ipreserv; - } else { - error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0); - if (error) - goto fail_quota_locks; - } - - error = gfs2_dir_add(&dip->i_inode, name, ip); - if (error) - goto fail_end_trans; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto fail_end_trans; - ip->i_inode.i_nlink = 1; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - return 0; - -fail_end_trans: - gfs2_trans_end(sdp); - -fail_ipreserv: - if (dip->i_alloc->al_rgd) - gfs2_inplace_release(dip); - -fail_quota_locks: - gfs2_quota_unlock(dip); - -fail: - gfs2_alloc_put(dip); - return error; -} - -static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, - const struct qstr *qstr) -{ - int err; - size_t len; - void *value; - char *name; - - err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, - &name, &value, &len); - - if (err) { - if (err == -EOPNOTSUPP) - return 0; - return err; - } - - err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0, - GFS2_EATYPE_SECURITY); - kfree(value); - kfree(name); - - return err; -} - -/** - * gfs2_createi - Create a new inode - * @ghs: An array of two holders - * @name: The name of the new file - * @mode: the permissions on the new inode - * - * @ghs[0] is an initialized holder for the directory - * @ghs[1] is the holder for the inode lock - * - * If the return value is not NULL, the glocks on both the directory and the new - * file are held. A transaction has been started and an inplace reservation - * is held, as well. - * - * Returns: An inode - */ - -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 = 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, &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); - return inode; - -fail_gunlock2: - gfs2_glock_dq_uninit(ghs + 1); - if (inode && !IS_ERR(inode)) - iput(inode); -fail_gunlock: - gfs2_glock_dq(ghs); -fail: - if (bh) - brelse(bh); - return ERR_PTR(error); -} - -static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - struct inode *inode = &ip->i_inode; - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - setattr_copy(inode, attr); - mark_inode_dirty(inode); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - return 0; -} - -/** - * gfs2_setattr_simple - - * @ip: - * @attr: - * - * Called with a reference on the vnode. - * - * Returns: errno - */ - -int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - int error; - - if (current->journal_info) - return __gfs2_setattr_simple(ip, attr); - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); - if (error) - return error; - - error = __gfs2_setattr_simple(ip, attr); - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - return error; -} void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) { diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 2607c2c..a783a7a 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -18,6 +18,7 @@ #include <linux/gfs2_ondisk.h> #include <linux/crc32.h> #include <linux/fiemap.h> +#include <linux/security.h> #include <asm/uaccess.h> #include "gfs2.h" @@ -34,8 +35,676 @@ #include "trans.h" #include "util.h" #include "super.h" +#include "glops.h" + +struct gfs2_skip_data { + u64 no_addr; + int skipped; + int non_block; +}; + +static int iget_test(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (ip->i_no_addr == data->no_addr) { + if (data->non_block && + inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { + data->skipped = 1; + return 0; + } + return 1; + } + return 0; +} + +static int iget_set(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (data->skipped) + return -ENOENT; + inode->i_ino = (unsigned long)(data->no_addr); + ip->i_no_addr = data->no_addr; + return 0; +} + +struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block) +{ + unsigned long hash = (unsigned long)no_addr; + struct gfs2_skip_data data; + + data.no_addr = no_addr; + data.skipped = 0; + data.non_block = non_block; + return ilookup5(sb, hash, iget_test, &data); +} + +static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr, + int non_block) +{ + struct gfs2_skip_data data; + unsigned long hash = (unsigned long)no_addr; + + data.no_addr = no_addr; + data.skipped = 0; + data.non_block = non_block; + return iget5_locked(sb, hash, iget_test, iget_set, &data); +} /** + * gfs2_set_iop - Sets inode operations + * @inode: The inode with correct i_mode filled in + * + * GFS2 lookup code fills in vfs inode contents based on info obtained + * from directory entry inside gfs2_inode_lookup(). + */ + +static void gfs2_set_iop(struct inode *inode) +{ + struct gfs2_sbd *sdp = GFS2_SB(inode); + umode_t mode = inode->i_mode; + + if (S_ISREG(mode)) { + inode->i_op = &gfs2_file_iops; + if (gfs2_localflocks(sdp)) + inode->i_fop = &gfs2_file_fops_nolock; + else + inode->i_fop = &gfs2_file_fops; + } else if (S_ISDIR(mode)) { + inode->i_op = &gfs2_dir_iops; + if (gfs2_localflocks(sdp)) + inode->i_fop = &gfs2_dir_fops_nolock; + else + inode->i_fop = &gfs2_dir_fops; + } else if (S_ISLNK(mode)) { + inode->i_op = &gfs2_symlink_iops; + } else { + inode->i_op = &gfs2_file_iops; + init_special_inode(inode, inode->i_mode, inode->i_rdev); + } +} + +/** + * gfs2_inode_lookup - Lookup an inode + * @sb: The super block + * @no_addr: The inode number + * @type: The type of the inode + * non_block: Can we block on inodes that are being freed? + * + * Returns: A VFS inode, or an error + */ + +struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, + u64 no_addr, u64 no_formal_ino, int non_block) +{ + struct inode *inode; + struct gfs2_inode *ip; + struct gfs2_glock *io_gl = NULL; + int error; + + inode = gfs2_iget(sb, no_addr, non_block); + ip = GFS2_I(inode); + + if (!inode) + return ERR_PTR(-ENOBUFS); + + if (inode->i_state & I_NEW) { + struct gfs2_sbd *sdp = GFS2_SB(inode); + ip->i_no_formal_ino = no_formal_ino; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); + if (unlikely(error)) + goto fail; + ip->i_gl->gl_object = ip; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); + if (unlikely(error)) + goto fail_put; + + set_bit(GIF_INVALID, &ip->i_flags); + error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); + if (unlikely(error)) + goto fail_iopen; + + ip->i_iopen_gh.gh_gl->gl_object = ip; + gfs2_glock_put(io_gl); + io_gl = NULL; + + if (type == DT_UNKNOWN) { + /* Inode glock must be locked already */ + error = gfs2_inode_refresh(GFS2_I(inode)); + if (error) + goto fail_refresh; + } else { + inode->i_mode = DT2IF(type); + } + + gfs2_set_iop(inode); + unlock_new_inode(inode); + } + + return inode; + +fail_refresh: + ip->i_iopen_gh.gh_gl->gl_object = NULL; + gfs2_glock_dq_uninit(&ip->i_iopen_gh); +fail_iopen: + if (io_gl) + gfs2_glock_put(io_gl); +fail_put: + ip->i_gl->gl_object = NULL; + gfs2_glock_put(ip->i_gl); +fail: + iget_failed(inode); + return ERR_PTR(error); +} + +struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, + u64 *no_formal_ino, unsigned int blktype) +{ + struct super_block *sb = sdp->sd_vfs; + struct gfs2_holder i_gh; + struct inode *inode = NULL; + int error; + + /* Must not read in block until block type is verified */ + error = gfs2_glock_nq_num(sdp, no_addr, &gfs2_inode_glops, + LM_ST_EXCLUSIVE, GL_SKIP, &i_gh); + if (error) + return ERR_PTR(error); + + error = gfs2_check_blk_type(sdp, no_addr, blktype); + if (error) + goto fail; + + inode = gfs2_inode_lookup(sb, DT_UNKNOWN, no_addr, 0, 1); + if (IS_ERR(inode)) + goto fail; + + /* Two extra checks for NFS only */ + if (no_formal_ino) { + error = -ESTALE; + if (GFS2_I(inode)->i_no_formal_ino != *no_formal_ino) + goto fail_iput; + + error = -EIO; + if (GFS2_I(inode)->i_diskflags & GFS2_DIF_SYSTEM) + goto fail_iput; + + error = 0; + } + +fail: + gfs2_glock_dq_uninit(&i_gh); + return error ? ERR_PTR(error) : inode; +fail_iput: + iput(inode); + goto fail; +} + + +struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) +{ + struct qstr qstr; + struct inode *inode; + gfs2_str2qstr(&qstr, name); + inode = gfs2_lookupi(dip, &qstr, 1); + /* gfs2_lookupi has inconsistent callers: vfs + * related routines expect NULL for no entry found, + * gfs2_lookup_simple callers expect ENOENT + * and do not check for NULL. + */ + if (inode == NULL) + return ERR_PTR(-ENOENT); + else + return inode; +} + + +/** + * gfs2_lookupi - Look up a filename in a directory and return its inode + * @d_gh: An initialized holder for the directory glock + * @name: The name of the inode to look for + * @is_root: If 1, ignore the caller's permissions + * @i_gh: An uninitialized holder for the new inode glock + * + * This can be called via the VFS filldir function when NFS is doing + * a readdirplus and the inode which its intending to stat isn't + * already in cache. In this case we must not take the directory glock + * again, since the readdir call will have already taken that lock. + * + * Returns: errno + */ + +struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, + int is_root) +{ + struct super_block *sb = dir->i_sb; + struct gfs2_inode *dip = GFS2_I(dir); + struct gfs2_holder d_gh; + int error = 0; + struct inode *inode = NULL; + int unlock = 0; + + if (!name->len || name->len > GFS2_FNAMESIZE) + return ERR_PTR(-ENAMETOOLONG); + + if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) || + (name->len == 2 && memcmp(name->name, "..", 2) == 0 && + dir == sb->s_root->d_inode)) { + igrab(dir); + return dir; + } + + if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) { + error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); + if (error) + return ERR_PTR(error); + unlock = 1; + } + + if (!is_root) { + error = gfs2_permission(dir, MAY_EXEC, 0); + if (error) + goto out; + } + + inode = gfs2_dir_search(dir, name); + if (IS_ERR(inode)) + error = PTR_ERR(inode); +out: + if (unlock) + gfs2_glock_dq_uninit(&d_gh); + if (error == -ENOENT) + return NULL; + return inode ? inode : ERR_PTR(error); +} + +/** + * create_ok - OK to create a new on-disk inode here? + * @dip: Directory in which dinode is to be created + * @name: Name of new dinode + * @mode: + * + * Returns: errno + */ + +static int create_ok(struct gfs2_inode *dip, const struct qstr *name, + unsigned int mode) +{ + int error; + + error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0); + if (error) + return error; + + /* Don't create entries in an unlinked directory */ + if (!dip->i_inode.i_nlink) + return -ENOENT; + + error = gfs2_dir_check(&dip->i_inode, name, NULL); + switch (error) { + case -ENOENT: + error = 0; + break; + case 0: + return -EEXIST; + default: + return error; + } + + if (dip->i_entries == (u32)-1) + return -EFBIG; + if (S_ISDIR(mode) && dip->i_inode.i_nlink == (u32)-1) + return -EMLINK; + + return 0; +} + +static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, + unsigned int *uid, unsigned int *gid) +{ + if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && + (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { + if (S_ISDIR(*mode)) + *mode |= S_ISUID; + else if (dip->i_inode.i_uid != current_fsuid()) + *mode &= ~07111; + *uid = dip->i_inode.i_uid; + } else + *uid = current_fsuid(); + + if (dip->i_inode.i_mode & S_ISGID) { + if (S_ISDIR(*mode)) + *mode |= S_ISGID; + *gid = dip->i_inode.i_gid; + } else + *gid = current_fsgid(); +} + +static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + int error; + + if (gfs2_alloc_get(dip) == NULL) + return -ENOMEM; + + dip->i_alloc->al_requested = RES_DINODE; + error = gfs2_inplace_reserve(dip); + if (error) + goto out; + + error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS, 0); + if (error) + goto out_ipreserv; + + error = gfs2_alloc_di(dip, no_addr, generation); + + gfs2_trans_end(sdp); + +out_ipreserv: + gfs2_inplace_release(dip); +out: + gfs2_alloc_put(dip); + return error; +} + +/** + * init_dinode - Fill in a new dinode structure + * @dip: the directory this inode is being created in + * @gl: The glock covering the new inode + * @inum: the inode number + * @mode: the file permissions + * @uid: + * @gid: + * + */ + +static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, + const struct gfs2_inum_host *inum, unsigned int mode, + unsigned int uid, unsigned int gid, + const u64 *generation, dev_t dev, struct buffer_head **bhp) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + struct gfs2_dinode *di; + struct buffer_head *dibh; + struct timespec tv = CURRENT_TIME; + + dibh = gfs2_meta_new(gl, inum->no_addr); + gfs2_trans_add_bh(gl, dibh, 1); + gfs2_metatype_set(dibh, GFS2_METATYPE_DI, GFS2_FORMAT_DI); + gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); + di = (struct gfs2_dinode *)dibh->b_data; + + di->di_num.no_formal_ino = cpu_to_be64(inum->no_formal_ino); + di->di_num.no_addr = cpu_to_be64(inum->no_addr); + di->di_mode = cpu_to_be32(mode); + di->di_uid = cpu_to_be32(uid); + di->di_gid = cpu_to_be32(gid); + di->di_nlink = 0; + di->di_size = 0; + di->di_blocks = cpu_to_be64(1); + di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); + di->di_major = cpu_to_be32(MAJOR(dev)); + di->di_minor = cpu_to_be32(MINOR(dev)); + di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); + di->di_generation = cpu_to_be64(*generation); + di->di_flags = 0; + + if (S_ISREG(mode)) { + if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) || + gfs2_tune_get(sdp, gt_new_files_jdata)) + di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA); + } else if (S_ISDIR(mode)) { + di->di_flags |= cpu_to_be32(dip->i_diskflags & + GFS2_DIF_INHERIT_JDATA); + } + + di->__pad1 = 0; + di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0); + di->di_height = 0; + di->__pad2 = 0; + di->__pad3 = 0; + di->di_depth = 0; + di->di_entries = 0; + memset(&di->__pad4, 0, sizeof(di->__pad4)); + di->di_eattr = 0; + di->di_atime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); + di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); + memset(&di->di_reserved, 0, sizeof(di->di_reserved)); + + set_buffer_uptodate(dibh); + + *bhp = dibh; +} + +static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, + unsigned int mode, const struct gfs2_inum_host *inum, + const u64 *generation, dev_t dev, struct buffer_head **bhp) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + unsigned int uid, gid; + int error; + + munge_mode_uid_gid(dip, &mode, &uid, &gid); + if (!gfs2_alloc_get(dip)) + return -ENOMEM; + + error = gfs2_quota_lock(dip, uid, gid); + if (error) + goto out; + + error = gfs2_quota_check(dip, uid, gid); + if (error) + goto out_quota; + + error = gfs2_trans_begin(sdp, RES_DINODE + RES_QUOTA, 0); + if (error) + goto out_quota; + + init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); + gfs2_quota_change(dip, +1, uid, gid); + gfs2_trans_end(sdp); + +out_quota: + gfs2_quota_unlock(dip); +out: + gfs2_alloc_put(dip); + return error; +} + +static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, + struct gfs2_inode *ip) +{ + struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); + struct gfs2_alloc *al; + int alloc_required; + struct buffer_head *dibh; + int error; + + al = gfs2_alloc_get(dip); + if (!al) + return -ENOMEM; + + error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + if (error) + goto fail; + + error = alloc_required = gfs2_diradd_alloc_required(&dip->i_inode, name); + if (alloc_required < 0) + goto fail_quota_locks; + if (alloc_required) { + error = gfs2_quota_check(dip, dip->i_inode.i_uid, dip->i_inode.i_gid); + if (error) + goto fail_quota_locks; + + al->al_requested = sdp->sd_max_dirres; + + error = gfs2_inplace_reserve(dip); + if (error) + goto fail_quota_locks; + + error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + + al->al_rgd->rd_length + + 2 * RES_DINODE + + RES_STATFS + RES_QUOTA, 0); + if (error) + goto fail_ipreserv; + } else { + error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0); + if (error) + goto fail_quota_locks; + } + + error = gfs2_dir_add(&dip->i_inode, name, ip); + if (error) + goto fail_end_trans; + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + goto fail_end_trans; + ip->i_inode.i_nlink = 1; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; + +fail_end_trans: + gfs2_trans_end(sdp); + +fail_ipreserv: + if (dip->i_alloc->al_rgd) + gfs2_inplace_release(dip); + +fail_quota_locks: + gfs2_quota_unlock(dip); + +fail: + gfs2_alloc_put(dip); + return error; +} + +static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, + const struct qstr *qstr) +{ + int err; + size_t len; + void *value; + char *name; + + err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr, + &name, &value, &len); + + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0, + GFS2_EATYPE_SECURITY); + kfree(value); + kfree(name); + + return err; +} + +/** + * gfs2_createi - Create a new inode + * @ghs: An array of two holders + * @name: The name of the new file + * @mode: the permissions on the new inode + * + * @ghs[0] is an initialized holder for the directory + * @ghs[1] is the holder for the inode lock + * + * If the return value is not NULL, the glocks on both the directory and the new + * file are held. A transaction has been started and an inplace reservation + * is held, as well. + * + * Returns: An inode + */ + +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 = 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, &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); + return inode; + +fail_gunlock2: + gfs2_glock_dq_uninit(ghs + 1); + if (inode && !IS_ERR(inode)) + iput(inode); +fail_gunlock: + gfs2_glock_dq(ghs); +fail: + if (bh) + brelse(bh); + return ERR_PTR(error); +} +/** * gfs2_create - Create a file * @dir: The directory in which to create the file * @dentry: The dentry of the new file @@ -1000,6 +1669,48 @@ int gfs2_permission(struct inode *inode, int mask, unsigned int flags) return error; } +static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) +{ + struct inode *inode = &ip->i_inode; + struct buffer_head *dibh; + int error; + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + return error; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; +} + +/** + * gfs2_setattr_simple - + * @ip: + * @attr: + * + * Returns: errno + */ + +int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) +{ + int error; + + if (current->journal_info) + return __gfs2_setattr_simple(ip, attr); + + error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); + if (error) + return error; + + error = __gfs2_setattr_simple(ip, attr); + gfs2_trans_end(GFS2_SB(&ip->i_inode)); + return error; +} + static int setattr_chown(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); |