/** * Write a jindex file given a list of journal inums. * master: Inode of the master directory * jnls: List of inum structures relating to previously created journals. * nmemb: The number of entries in the list (number of journals). * Returns 0 on success or non-zero on error with errno set. */ int lgfs2_build_jindex(struct gfs2_inode *master, struct gfs2_inum *jnls, size_t nmemb) { char fname[GFS2_FNAMESIZE + 1]; struct gfs2_inode *jindex; unsigned j; int ret; if (nmemb == 0 || jnls == NULL) { errno = EINVAL; return 1; } jindex = createi(master, "jindex", S_IFDIR | 0700, GFS2_DIF_SYSTEM); if (jindex == NULL) return 1; fname[GFS2_FNAMESIZE] = '\0'; for (j = 0; j < nmemb; j++) { snprintf(fname, GFS2_FNAMESIZE, "journal%u", j); ret = dir_add(jindex, fname, strlen(fname), &jnls[j], IF2DT(S_IFREG | 0600)); if (ret) { inode_put(&jindex); return 1; } } if (cfg_debug) { printf("\nJindex:\n"); gfs2_dinode_print(&jindex->i_di); } inode_put(&jindex); return 0; }
static int inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl) { const struct gfs2_inode *ip = gl->gl_object; if (ip == NULL) return 0; gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu/%llu\n", (unsigned long long)ip->i_no_formal_ino, (unsigned long long)ip->i_no_addr, IF2DT(ip->i_inode.i_mode), ip->i_flags, (unsigned int)ip->i_diskflags, (unsigned long long)ip->i_inode.i_size, (unsigned long long)ip->i_disksize); return 0; }
static void inode_go_dump(struct seq_file *seq, struct gfs2_glock *gl) { struct gfs2_inode *ip = gl->gl_object; struct inode *inode = &ip->i_inode; unsigned long nrpages; if (ip == NULL) return; xa_lock_irq(&inode->i_data.i_pages); nrpages = inode->i_data.nrpages; xa_unlock_irq(&inode->i_data.i_pages); gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu p:%lu\n", (unsigned long long)ip->i_no_formal_ino, (unsigned long long)ip->i_no_addr, IF2DT(ip->i_inode.i_mode), ip->i_flags, (unsigned int)ip->i_diskflags, (unsigned long long)i_size_read(inode), nrpages); }
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]; 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); 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_qadata *qa = gfs2_qadata_get(dip); if (!qa) { error = -ENOMEM; goto out_gunlock; } error = gfs2_quota_lock_check(dip); if (error) goto out_alloc; error = gfs2_inplace_reserve(dip, sdp->sd_max_dirres); if (error) goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + gfs2_rg_blocks(dip) + 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_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode)); if (error) goto out_end_trans; error = gfs2_change_nlink(ip, +1); 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_qadata_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) { atomic_inc(&inode->i_count); d_instantiate(dentry, inode); mark_inode_dirty(inode); } return error; }
static int gfs2_rename(struct inode *odir, struct dentry *odentry, struct inode *ndir, struct dentry *ndentry) { struct gfs2_inode *odip = GFS2_I(odir); struct gfs2_inode *ndip = GFS2_I(ndir); struct gfs2_inode *ip = GFS2_I(odentry->d_inode); struct gfs2_inode *nip = NULL; struct gfs2_sbd *sdp = GFS2_SB(odir); struct gfs2_holder ghs[5], r_gh; struct gfs2_rgrpd *nrgd; unsigned int num_gh; int dir_rename = 0; int alloc_required; unsigned int x; int error; if (ndentry->d_inode) { nip = GFS2_I(ndentry->d_inode); if (ip == nip) return 0; } /* Make sure we aren't trying to move a dirctory into it's subdir */ if (S_ISDIR(ip->i_inode.i_mode) && odip != ndip) { dir_rename = 1; error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, 0, &r_gh); if (error) goto out; error = gfs2_ok_to_move(ip, ndip); if (error) goto out_gunlock_r; } num_gh = 1; gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); if (odip != ndip) { gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); num_gh++; } gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); num_gh++; if (nip) { gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); num_gh++; /* grab the resource lock for unlink flag twiddling * this is the case of the target file already existing * so we unlink before doing the rename */ nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr); if (nrgd) gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++); } error = gfs2_glock_nq_m(num_gh, ghs); if (error) goto out_uninit; /* Check out the old directory */ error = gfs2_unlink_ok(odip, &odentry->d_name, ip); if (error) goto out_gunlock; /* Check out the new directory */ if (nip) { error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); if (error) goto out_gunlock; if (S_ISDIR(nip->i_inode.i_mode)) { if (nip->i_di.di_entries < 2) { if (gfs2_consist_inode(nip)) gfs2_dinode_print(nip); error = -EIO; goto out_gunlock; } if (nip->i_di.di_entries > 2) { error = -ENOTEMPTY; goto out_gunlock; } } } else { error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC); if (error) goto out_gunlock; error = gfs2_dir_check(ndir, &ndentry->d_name, NULL); switch (error) { case -ENOENT: error = 0; break; case 0: error = -EEXIST; default: goto out_gunlock; }; if (odip != ndip) { if (!ndip->i_inode.i_nlink) { error = -EINVAL; goto out_gunlock; } if (ndip->i_di.di_entries == (u32)-1) { error = -EFBIG; goto out_gunlock; } if (S_ISDIR(ip->i_inode.i_mode) && ndip->i_inode.i_nlink == (u32)-1) { error = -EMLINK; goto out_gunlock; } } } /* Check out the dir to be renamed */ if (dir_rename) { error = gfs2_permission(odentry->d_inode, MAY_WRITE); if (error) goto out_gunlock; } alloc_required = error = gfs2_diradd_alloc_required(ndir, &ndentry->d_name); if (error < 0) goto out_gunlock; error = 0; if (alloc_required) { struct gfs2_alloc *al = gfs2_alloc_get(ndip); if (!al) { error = -ENOMEM; goto out_gunlock; } error = gfs2_quota_lock_check(ndip); if (error) goto out_alloc; al->al_requested = sdp->sd_max_dirres; error = gfs2_inplace_reserve(ndip); if (error) goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + al->al_rgd->rd_length + 4 * RES_DINODE + 4 * RES_LEAF + RES_STATFS + RES_QUOTA + 4, 0); if (error) goto out_ipreserv; } else { error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 5 * RES_LEAF + 4, 0); if (error) goto out_gunlock; } /* Remove the target file, if it exists */ if (nip) { if (S_ISDIR(nip->i_inode.i_mode)) error = gfs2_rmdiri(ndip, &ndentry->d_name, nip); else { error = gfs2_dir_del(ndip, &ndentry->d_name); if (error) goto out_end_trans; error = gfs2_change_nlink(nip, -1); } if (error) goto out_end_trans; } if (dir_rename) { struct qstr name; gfs2_str2qstr(&name, ".."); error = gfs2_change_nlink(ndip, +1); if (error) goto out_end_trans; error = gfs2_change_nlink(odip, -1); if (error) goto out_end_trans; error = gfs2_dir_mvino(ip, &name, ndip, DT_DIR); if (error) goto out_end_trans; } else { struct buffer_head *dibh; error = gfs2_meta_inode_buffer(ip, &dibh); if (error) goto out_end_trans; ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); } error = gfs2_dir_del(odip, &odentry->d_name); if (error) goto out_end_trans; error = gfs2_dir_add(ndir, &ndentry->d_name, ip, IF2DT(ip->i_inode.i_mode)); if (error) goto out_end_trans; out_end_trans: gfs2_trans_end(sdp); out_ipreserv: if (alloc_required) gfs2_inplace_release(ndip); out_gunlock_q: if (alloc_required) gfs2_quota_unlock(ndip); out_alloc: if (alloc_required) gfs2_alloc_put(ndip); out_gunlock: gfs2_glock_dq_m(num_gh, ghs); out_uninit: for (x = 0; x < num_gh; x++) gfs2_holder_uninit(ghs + x); out_gunlock_r: if (dir_rename) gfs2_glock_dq_uninit(&r_gh); out: return error; }
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); 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)); 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; if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size != i_size_read(inode)) { error = vmtruncate(inode, attr->ia_size); if (error) return error; } setattr_copy(inode, attr); mark_inode_dirty(inode); gfs2_assert_warn(GFS2_SB(inode), !error); 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; }
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, IF2DT(ip->i_inode.i_mode)); 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_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); 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 == -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; 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); /* Check if we reserved space in the rgrp. Function link_dinode may not, depending on whether alloc is required. */ if (dip->i_res) gfs2_inplace_release(dip); gfs2_quota_unlock(dip); gfs2_qadata_put(dip); mark_inode_dirty(inode); gfs2_glock_dq_uninit_m(2, ghs); d_instantiate(dentry, inode); return 0; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); fail_gunlock: gfs2_glock_dq_uninit(ghs); if (inode && !IS_ERR(inode)) { set_bit(GIF_ALLOC_FAILED, &GFS2_I(inode)->i_flags); iput(inode); } 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, umode_t mode, struct nameidata *nd) { int excl = 0; if (nd && (nd->flags & LOOKUP_EXCL)) excl = 1; return gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0, excl); }
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *parent = dget_parent(dentry); struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); struct gfs2_inode *dip = GFS2_I(parent->d_inode); struct inode *inode = dentry->d_inode; struct gfs2_holder d_gh; struct gfs2_inode *ip; struct gfs2_inum_host inum; unsigned int type; int error; int had_lock=0; if (inode && is_bad_inode(inode)) goto invalid; if (sdp->sd_args.ar_localcaching) goto valid; had_lock = gfs2_glock_is_locked_by_me(dip->i_gl); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) goto fail; } error = gfs2_dir_search(parent->d_inode, &dentry->d_name, &inum, &type); switch (error) { case 0: if (!inode) goto invalid_gunlock; break; case -ENOENT: if (!inode) goto valid_gunlock; goto invalid_gunlock; default: goto fail_gunlock; } ip = GFS2_I(inode); if (!gfs2_inum_equal(&ip->i_num, &inum)) goto invalid_gunlock; if (IF2DT(ip->i_inode.i_mode) != type) { gfs2_consist_inode(dip); goto fail_gunlock; } valid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); valid: dput(parent); return 1; invalid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); invalid: if (inode && S_ISDIR(inode->i_mode)) { if (have_submounts(dentry)) goto valid; shrink_dcache_parent(dentry); } d_drop(dentry); dput(parent); return 0; fail_gunlock: gfs2_glock_dq_uninit(&d_gh); fail: dput(parent); return 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; 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); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, inum.no_formal_ino); 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 (!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: 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; }
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 = gfs2_lookupi(dir, &dentry->d_name, 0); if (inode && !IS_ERR(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); } /** * 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)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif 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); 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)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif 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)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if (IS_APPEND(&dip->i_inode)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC); if (error) return error; error = gfs2_dir_check(&dip->i_inode, name, ip); if (error) return error; return 0; }
static int nova_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct nova_inode *pidir; struct nova_inode_info *si = NOVA_I(inode); struct nova_inode_info_header *sih = &si->header; struct nova_inode *child_pi; struct nova_inode *prev_child_pi = NULL; struct nova_dentry *entry = NULL; struct nova_dentry *prev_entry = NULL; unsigned short de_len; u64 pi_addr; unsigned long pos = 0; ino_t ino; void *addr; u64 curr_p; u8 type; int ret; timing_t readdir_time; NOVA_START_TIMING(readdir_t, readdir_time); pidir = nova_get_inode(sb, inode); nova_dbgv("%s: ino %llu, size %llu, pos 0x%llx\n", __func__, (u64)inode->i_ino, pidir->i_size, ctx->pos); if (pidir->log_head == 0) { nova_err(sb, "Dir %lu log is NULL!\n", inode->i_ino); BUG(); return -EINVAL; } pos = ctx->pos; if (pos == 0) { curr_p = pidir->log_head; } else if (pos == READDIR_END) { goto out; } else { curr_p = nova_find_next_dentry_addr(sb, sih, pos); if (curr_p == 0) goto out; } while (curr_p != pidir->log_tail) { if (goto_next_page(sb, curr_p)) { curr_p = next_log_page(sb, curr_p); } if (curr_p == 0) { nova_err(sb, "Dir %lu log is NULL!\n", inode->i_ino); BUG(); return -EINVAL; } addr = (void *)nova_get_block(sb, curr_p); type = nova_get_entry_type(addr); switch (type) { case SET_ATTR: curr_p += sizeof(struct nova_setattr_logentry); continue; case LINK_CHANGE: curr_p += sizeof(struct nova_link_change_entry); continue; case DIR_LOG: break; default: nova_dbg("%s: unknown type %d, 0x%llx\n", __func__, type, curr_p); BUG(); return -EINVAL; } entry = (struct nova_dentry *)nova_get_block(sb, curr_p); nova_dbgv("curr_p: 0x%llx, type %d, ino %llu, " "name %s, namelen %u, rec len %u\n", curr_p, entry->entry_type, le64_to_cpu(entry->ino), entry->name, entry->name_len, le16_to_cpu(entry->de_len)); de_len = le16_to_cpu(entry->de_len); if (entry->ino > 0 && entry->invalid == 0) { ino = __le64_to_cpu(entry->ino); pos = BKDRHash(entry->name, entry->name_len); ret = nova_get_inode_address(sb, ino, &pi_addr, 0); if (ret) { nova_dbg("%s: get child inode %lu address " "failed %d\n", __func__, ino, ret); ctx->pos = READDIR_END; return ret; } child_pi = nova_get_block(sb, pi_addr); nova_dbgv("ctx: ino %llu, name %s, " "name_len %u, de_len %u\n", (u64)ino, entry->name, entry->name_len, entry->de_len); if (prev_entry && !dir_emit(ctx, prev_entry->name, prev_entry->name_len, ino, IF2DT(le16_to_cpu(prev_child_pi->i_mode)))) { nova_dbgv("Here: pos %llu\n", ctx->pos); return 0; } prev_entry = entry; prev_child_pi = child_pi; } ctx->pos = pos; curr_p += de_len; } if (prev_entry && !dir_emit(ctx, prev_entry->name, prev_entry->name_len, ino, IF2DT(le16_to_cpu(prev_child_pi->i_mode)))) return 0; ctx->pos = READDIR_END; out: NOVA_END_TIMING(readdir_t, readdir_time); nova_dbgv("%s return\n", __func__); return 0; }
static int nova_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct nova_inode *pidir; struct nova_inode_info *si = NOVA_I(inode); struct nova_inode_info_header *sih = &si->header; struct nova_inode *child_pi; struct nova_dentry *entry; struct nova_dentry *entries[FREE_BATCH]; int nr_entries; u64 pi_addr; unsigned long pos = 0; ino_t ino; int i; int ret; timing_t readdir_time; NOVA_START_TIMING(readdir_t, readdir_time); pidir = nova_get_inode(sb, inode); nova_dbgv("%s: ino %llu, size %llu, pos %llu\n", __func__, (u64)inode->i_ino, pidir->i_size, ctx->pos); if (!sih) { nova_dbg("%s: inode %lu sih does not exist!\n", __func__, inode->i_ino); ctx->pos = READDIR_END; return 0; } pos = ctx->pos; if (pos == READDIR_END) goto out; do { nr_entries = radix_tree_gang_lookup(&sih->tree, (void **)entries, pos, FREE_BATCH); for (i = 0; i < nr_entries; i++) { entry = entries[i]; pos = BKDRHash(entry->name, entry->name_len); ino = __le64_to_cpu(entry->ino); if (ino == 0) continue; ret = nova_get_inode_address(sb, ino, &pi_addr, 0); if (ret) { nova_dbg("%s: get child inode %lu address " "failed %d\n", __func__, ino, ret); ctx->pos = READDIR_END; return ret; } child_pi = nova_get_block(sb, pi_addr); nova_dbgv("ctx: ino %llu, name %s, " "name_len %u, de_len %u\n", (u64)ino, entry->name, entry->name_len, entry->de_len); if (!dir_emit(ctx, entry->name, entry->name_len, ino, IF2DT(le16_to_cpu(child_pi->i_mode)))) { nova_dbgv("Here: pos %llu\n", ctx->pos); return 0; } ctx->pos = pos + 1; } pos++; } while (nr_entries == FREE_BATCH); out: NOVA_END_TIMING(readdir_t, readdir_time); return 0; }