📄 dir.c
字号:
return PTR_ERR(dent); if (ip) { if (be64_to_cpu(dent->de_inum.no_addr) != ip->i_no_addr) goto out; if (be64_to_cpu(dent->de_inum.no_formal_ino) != ip->i_no_formal_ino) goto out; if (unlikely(IF2DT(ip->i_inode.i_mode) != be16_to_cpu(dent->de_type))) { gfs2_consist_inode(GFS2_I(dir)); ret = -EIO; goto out; } } ret = 0;out: brelse(bh); } return ret;}static int dir_new_leaf(struct inode *inode, const struct qstr *name){ struct buffer_head *bh, *obh; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_leaf *leaf, *oleaf; int error; u32 index; u64 bn; index = name->hash >> (32 - ip->i_di.di_depth); error = get_first_leaf(ip, index, &obh); if (error) return error; do { oleaf = (struct gfs2_leaf *)obh->b_data; bn = be64_to_cpu(oleaf->lf_next); if (!bn) break; brelse(obh); error = get_leaf(ip, bn, &obh); if (error) return error; } while(1); gfs2_trans_add_bh(ip->i_gl, obh, 1); leaf = new_leaf(inode, &bh, be16_to_cpu(oleaf->lf_depth)); if (!leaf) { brelse(obh); return -ENOSPC; } oleaf->lf_next = cpu_to_be64(bh->b_blocknr); brelse(bh); brelse(obh); error = gfs2_meta_inode_buffer(ip, &bh); if (error) return error; gfs2_trans_add_bh(ip->i_gl, bh, 1); ip->i_di.di_blocks++; gfs2_set_inode_blocks(&ip->i_inode); gfs2_dinode_out(ip, bh->b_data); brelse(bh); return 0;}/** * gfs2_dir_add - Add new filename into directory * @dip: The GFS2 inode * @filename: The new name * @inode: The inode number of the entry * @type: The type of the entry * * Returns: 0 on success, error code on failure */int gfs2_dir_add(struct inode *inode, const struct qstr *name, const struct gfs2_inode *nip, unsigned type){ struct gfs2_inode *ip = GFS2_I(inode); struct buffer_head *bh; struct gfs2_dirent *dent; struct gfs2_leaf *leaf; int error; while(1) { dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, &bh); if (dent) { if (IS_ERR(dent)) return PTR_ERR(dent); dent = gfs2_init_dirent(inode, dent, name, bh); gfs2_inum_out(nip, dent); dent->de_type = cpu_to_be16(type); if (ip->i_di.di_flags & GFS2_DIF_EXHASH) { leaf = (struct gfs2_leaf *)bh->b_data; leaf->lf_entries = cpu_to_be16(be16_to_cpu(leaf->lf_entries) + 1); } brelse(bh); error = gfs2_meta_inode_buffer(ip, &bh); if (error) break; gfs2_trans_add_bh(ip->i_gl, bh, 1); ip->i_di.di_entries++; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, bh->b_data); brelse(bh); error = 0; break; } if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH)) { error = dir_make_exhash(inode); if (error) break; continue; } error = dir_split_leaf(inode, name); if (error == 0) continue; if (error < 0) break; if (ip->i_di.di_depth < GFS2_DIR_MAX_DEPTH) { error = dir_double_exhash(ip); if (error) break; error = dir_split_leaf(inode, name); if (error < 0) break; if (error == 0) continue; } error = dir_new_leaf(inode, name); if (!error) continue; error = -ENOSPC; break; } return error;}/** * gfs2_dir_del - Delete a directory entry * @dip: The GFS2 inode * @filename: The filename * * Returns: 0 on success, error code on failure */int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name){ struct gfs2_dirent *dent, *prev = NULL; struct buffer_head *bh; int error; /* Returns _either_ the entry (if its first in block) or the previous entry otherwise */ dent = gfs2_dirent_search(&dip->i_inode, name, gfs2_dirent_prev, &bh); if (!dent) { gfs2_consist_inode(dip); return -EIO; } if (IS_ERR(dent)) { gfs2_consist_inode(dip); return PTR_ERR(dent); } /* If not first in block, adjust pointers accordingly */ if (gfs2_dirent_find(dent, name, NULL) == 0) { prev = dent; dent = (struct gfs2_dirent *)((char *)dent + be16_to_cpu(prev->de_rec_len)); } dirent_del(dip, bh, prev, dent); if (dip->i_di.di_flags & GFS2_DIF_EXHASH) { struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data; u16 entries = be16_to_cpu(leaf->lf_entries); if (!entries) gfs2_consist_inode(dip); leaf->lf_entries = cpu_to_be16(--entries); } brelse(bh); error = gfs2_meta_inode_buffer(dip, &bh); if (error) return error; if (!dip->i_di.di_entries) gfs2_consist_inode(dip); gfs2_trans_add_bh(dip->i_gl, bh, 1); dip->i_di.di_entries--; dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(dip, bh->b_data); brelse(bh); mark_inode_dirty(&dip->i_inode); return error;}/** * gfs2_dir_mvino - Change inode number of directory entry * @dip: The GFS2 inode * @filename: * @new_inode: * * This routine changes the inode number of a directory entry. It's used * by rename to change ".." when a directory is moved. * Assumes a glock is held on dvp. * * Returns: errno */int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, const struct gfs2_inode *nip, unsigned int new_type){ struct buffer_head *bh; struct gfs2_dirent *dent; int error; dent = gfs2_dirent_search(&dip->i_inode, filename, gfs2_dirent_find, &bh); if (!dent) { gfs2_consist_inode(dip); return -EIO; } if (IS_ERR(dent)) return PTR_ERR(dent); gfs2_trans_add_bh(dip->i_gl, bh, 1); gfs2_inum_out(nip, dent); dent->de_type = cpu_to_be16(new_type); if (dip->i_di.di_flags & GFS2_DIF_EXHASH) { brelse(bh); error = gfs2_meta_inode_buffer(dip, &bh); if (error) return error; gfs2_trans_add_bh(dip->i_gl, bh, 1); } dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(dip, bh->b_data); brelse(bh); return 0;}/** * foreach_leaf - call a function for each leaf in a directory * @dip: the directory * @lc: the function to call for each each * @data: private data to pass to it * * Returns: errno */static int foreach_leaf(struct gfs2_inode *dip, leaf_call_t lc, void *data){ struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct buffer_head *bh; struct gfs2_leaf *leaf; u32 hsize, len; u32 ht_offset, lp_offset, ht_offset_cur = -1; u32 index = 0; __be64 *lp; u64 leaf_no; int error = 0; hsize = 1 << dip->i_di.di_depth; if (hsize * sizeof(u64) != dip->i_di.di_size) { gfs2_consist_inode(dip); return -EIO; } lp = kmalloc(sdp->sd_hash_bsize, GFP_KERNEL); if (!lp) return -ENOMEM; while (index < hsize) { lp_offset = index & (sdp->sd_hash_ptrs - 1); ht_offset = index - lp_offset; if (ht_offset_cur != ht_offset) { error = gfs2_dir_read_data(dip, (char *)lp, ht_offset * sizeof(__be64), sdp->sd_hash_bsize, 1); if (error != sdp->sd_hash_bsize) { if (error >= 0) error = -EIO; goto out; } ht_offset_cur = ht_offset; } leaf_no = be64_to_cpu(lp[lp_offset]); if (leaf_no) { error = get_leaf(dip, leaf_no, &bh); if (error) goto out; leaf = (struct gfs2_leaf *)bh->b_data; len = 1 << (dip->i_di.di_depth - be16_to_cpu(leaf->lf_depth)); brelse(bh); error = lc(dip, index, len, leaf_no, data); if (error) goto out; index = (index & ~(len - 1)) + len; } else index++; } if (index != hsize) { gfs2_consist_inode(dip); error = -EIO; }out: kfree(lp); return error;}/** * leaf_dealloc - Deallocate a directory leaf * @dip: the directory * @index: the hash table offset in the directory * @len: the number of pointers to this leaf * @leaf_no: the leaf number * @data: not used * * Returns: errno */static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, u64 leaf_no, void *data){ struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_leaf *tmp_leaf; struct gfs2_rgrp_list rlist; struct buffer_head *bh, *dibh; u64 blk, nblk; unsigned int rg_blocks = 0, l_blocks = 0; char *ht; unsigned int x, size = len * sizeof(u64); int error; memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); ht = kzalloc(size, GFP_KERNEL); if (!ht) return -ENOMEM; gfs2_alloc_get(dip); error = gfs2_quota_hold(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); if (error) goto out; error = gfs2_rindex_hold(sdp, &dip->i_alloc.al_ri_gh); if (error) goto out_qs; /* Count the number of leaves */ for (blk = leaf_no; blk; blk = nblk) { error = get_leaf(dip, blk, &bh); if (error) goto out_rlist; tmp_leaf = (struct gfs2_leaf *)bh->b_data; nblk = be64_to_cpu(tmp_leaf->lf_next); brelse(bh); gfs2_rlist_add(sdp, &rlist, blk); l_blocks++; } gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0); for (x = 0; x < rlist.rl_rgrps; x++) { struct gfs2_rgrpd *rgd; rgd = rlist.rl_ghs[x].gh_gl->gl_object; rg_blocks += rgd->rd_length; } error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); if (error) goto out_rlist; error = gfs2_trans_begin(sdp, rg_blocks + (DIV_ROUND_UP(size, sdp->sd_jbsize) + 1) + RES_DINODE + RES_STATFS + RES_QUOTA, l_blocks); if (error) goto out_rg_gunlock; for (blk = leaf_no; blk; blk = nblk) { error = get_leaf(dip, blk, &bh); if (error) goto out_end_trans; tmp_leaf = (struct gfs2_leaf *)bh->b_data; nblk = be64_to_cpu(tmp_leaf->lf_next); brelse(bh); gfs2_free_meta(dip, blk, 1); if (!dip->i_di.di_blocks) gfs2_consist_inode(dip); dip->i_di.di_blocks--; gfs2_set_inode_blocks(&dip->i_inode); } error = gfs2_dir_write_data(dip, ht, index * sizeof(u64), size); if (error != size) { if (error >= 0) error = -EIO; goto out_end_trans; } error = gfs2_meta_inode_buffer(dip, &dibh); if (error) goto out_end_trans; gfs2_trans_add_bh(dip->i_gl, dibh, 1); gfs2_dinode_out(dip, dibh->b_data); brelse(dibh);out_end_trans: gfs2_trans_end(sdp);out_rg_gunlock: gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs);out_rlist: gfs2_rlist_free(&rlist); gfs2_glock_dq_uninit(&dip->i_alloc.al_ri_gh);out_qs: gfs2_quota_unhold(dip);out: gfs2_alloc_put(dip); kfree(ht); return error;}/** * gfs2_dir_exhash_dealloc - free all the leaf blocks in a directory * @dip: the directory * * Dealloc all on-disk directory leaves to FREEMETA state * Change on-disk inode type to "regular file" * * Returns: errno */int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip){ struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct buffer_head *bh; int error; /* Dealloc on-disk leaves to FREEMETA state */ error = foreach_leaf(dip, leaf_dealloc, NULL); if (error) return error; /* Make this a regular file in case we crash. (We don't want to free these blocks a second time.) */ error = gfs2_trans_begin(sdp, RES_DINODE, 0); if (error) return error; error = gfs2_meta_inode_buffer(dip, &bh); if (!error) { gfs2_trans_add_bh(dip->i_gl, bh, 1); ((struct gfs2_dinode *)bh->b_data)->di_mode = cpu_to_be32(S_IFREG); brelse(bh); } gfs2_trans_end(sdp); return error;}/** * gfs2_diradd_alloc_required - find if adding entry will require an allocation * @ip: the file being written to * @filname: the filename that's going to be added * * Returns: 1 if alloc required, 0 if not, -ve on error */int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name){ struct gfs2_dirent *dent; struct buffer_head *bh; dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, &bh); if (!dent) { return 1; } if (IS_ERR(dent)) return PTR_ERR(dent); brelse(bh); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -