📄 dir.c
字号:
str.hash = be32_to_cpu(dent->de_hash); new = gfs2_dirent_alloc(inode, nbh, &str); if (IS_ERR(new)) { error = PTR_ERR(new); break; } new->de_inum = dent->de_inum; /* No endian worries */ new->de_type = dent->de_type; /* No endian worries */ nleaf->lf_entries = cpu_to_be16(be16_to_cpu(nleaf->lf_entries)+1); dirent_del(dip, obh, prev, dent); if (!oleaf->lf_entries) gfs2_consist_inode(dip); oleaf->lf_entries = cpu_to_be16(be16_to_cpu(oleaf->lf_entries)-1); if (!prev) prev = dent; moved = 1; } else { prev = dent; } dent = next; } while (dent); oleaf->lf_depth = nleaf->lf_depth; error = gfs2_meta_inode_buffer(dip, &dibh); if (!gfs2_assert_withdraw(GFS2_SB(&dip->i_inode), !error)) { gfs2_trans_add_bh(dip->i_gl, dibh, 1); dip->i_di.di_blocks++; gfs2_set_inode_blocks(&dip->i_inode); gfs2_dinode_out(dip, dibh->b_data); brelse(dibh); } brelse(obh); brelse(nbh); return error;fail_lpfree: kfree(lp);fail_brelse: brelse(obh); brelse(nbh); return error;}/** * dir_double_exhash - Double size of ExHash table * @dip: The GFS2 dinode * * Returns: 0 on success, error code on failure */static int dir_double_exhash(struct gfs2_inode *dip){ struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct buffer_head *dibh; u32 hsize; u64 *buf; u64 *from, *to; u64 block; int x; 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; } /* Allocate both the "from" and "to" buffers in one big chunk */ buf = kcalloc(3, sdp->sd_hash_bsize, GFP_KERNEL | __GFP_NOFAIL); for (block = dip->i_di.di_size >> sdp->sd_hash_bsize_shift; block--;) { error = gfs2_dir_read_data(dip, (char *)buf, block * sdp->sd_hash_bsize, sdp->sd_hash_bsize, 1); if (error != sdp->sd_hash_bsize) { if (error >= 0) error = -EIO; goto fail; } from = buf; to = (u64 *)((char *)buf + sdp->sd_hash_bsize); for (x = sdp->sd_hash_ptrs; x--; from++) { *to++ = *from; /* No endianess worries */ *to++ = *from; } error = gfs2_dir_write_data(dip, (char *)buf + sdp->sd_hash_bsize, block * sdp->sd_sb.sb_bsize, sdp->sd_sb.sb_bsize); if (error != sdp->sd_sb.sb_bsize) { if (error >= 0) error = -EIO; goto fail; } } kfree(buf); error = gfs2_meta_inode_buffer(dip, &dibh); if (!gfs2_assert_withdraw(sdp, !error)) { dip->i_di.di_depth++; gfs2_dinode_out(dip, dibh->b_data); brelse(dibh); } return error;fail: kfree(buf); return error;}/** * compare_dents - compare directory entries by hash value * @a: first dent * @b: second dent * * When comparing the hash entries of @a to @b: * gt: returns 1 * lt: returns -1 * eq: returns 0 */static int compare_dents(const void *a, const void *b){ const struct gfs2_dirent *dent_a, *dent_b; u32 hash_a, hash_b; int ret = 0; dent_a = *(const struct gfs2_dirent **)a; hash_a = be32_to_cpu(dent_a->de_hash); dent_b = *(const struct gfs2_dirent **)b; hash_b = be32_to_cpu(dent_b->de_hash); if (hash_a > hash_b) ret = 1; else if (hash_a < hash_b) ret = -1; else { unsigned int len_a = be16_to_cpu(dent_a->de_name_len); unsigned int len_b = be16_to_cpu(dent_b->de_name_len); if (len_a > len_b) ret = 1; else if (len_a < len_b) ret = -1; else ret = memcmp(dent_a + 1, dent_b + 1, len_a); } return ret;}/** * do_filldir_main - read out directory entries * @dip: The GFS2 inode * @offset: The offset in the file to read from * @opaque: opaque data to pass to filldir * @filldir: The function to pass entries to * @darr: an array of struct gfs2_dirent pointers to read * @entries: the number of entries in darr * @copied: pointer to int that's non-zero if a entry has been copied out * * Jump through some hoops to make sure that if there are hash collsions, * they are read out at the beginning of a buffer. We want to minimize * the possibility that they will fall into different readdir buffers or * that someone will want to seek to that location. * * Returns: errno, >0 on exception from filldir */static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, void *opaque, filldir_t filldir, const struct gfs2_dirent **darr, u32 entries, int *copied){ const struct gfs2_dirent *dent, *dent_next; u64 off, off_next; unsigned int x, y; int run = 0; int error = 0; sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL); dent_next = darr[0]; off_next = be32_to_cpu(dent_next->de_hash); off_next = gfs2_disk_hash2offset(off_next); for (x = 0, y = 1; x < entries; x++, y++) { dent = dent_next; off = off_next; if (y < entries) { dent_next = darr[y]; off_next = be32_to_cpu(dent_next->de_hash); off_next = gfs2_disk_hash2offset(off_next); if (off < *offset) continue; *offset = off; if (off_next == off) { if (*copied && !run) return 1; run = 1; } else run = 0; } else { if (off < *offset) continue; *offset = off; } error = filldir(opaque, (const char *)(dent + 1), be16_to_cpu(dent->de_name_len), off, be64_to_cpu(dent->de_inum.no_addr), be16_to_cpu(dent->de_type)); if (error) return 1; *copied = 1; } /* Increment the *offset by one, so the next time we come into the do_filldir fxn, we get the next entry instead of the last one in the current leaf */ (*offset)++; return 0;}static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir, int *copied, unsigned *depth, u64 leaf_no){ struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct buffer_head *bh; struct gfs2_leaf *lf; unsigned entries = 0, entries2 = 0; unsigned leaves = 0; const struct gfs2_dirent **darr, *dent; struct dirent_gather g; struct buffer_head **larr; int leaf = 0; int error, i; u64 lfn = leaf_no; do { error = get_leaf(ip, lfn, &bh); if (error) goto out; lf = (struct gfs2_leaf *)bh->b_data; if (leaves == 0) *depth = be16_to_cpu(lf->lf_depth); entries += be16_to_cpu(lf->lf_entries); leaves++; lfn = be64_to_cpu(lf->lf_next); brelse(bh); } while(lfn); if (!entries) return 0; error = -ENOMEM; /* * The extra 99 entries are not normally used, but are a buffer * zone in case the number of entries in the leaf is corrupt. * 99 is the maximum number of entries that can fit in a single * leaf block. */ larr = vmalloc((leaves + entries + 99) * sizeof(void *)); if (!larr) goto out; darr = (const struct gfs2_dirent **)(larr + leaves); g.pdent = darr; g.offset = 0; lfn = leaf_no; do { error = get_leaf(ip, lfn, &bh); if (error) goto out_kfree; lf = (struct gfs2_leaf *)bh->b_data; lfn = be64_to_cpu(lf->lf_next); if (lf->lf_entries) { entries2 += be16_to_cpu(lf->lf_entries); dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, gfs2_dirent_gather, NULL, &g); error = PTR_ERR(dent); if (IS_ERR(dent)) goto out_kfree; if (entries2 != g.offset) { fs_warn(sdp, "Number of entries corrupt in dir " "leaf %llu, entries2 (%u) != " "g.offset (%u)\n", (unsigned long long)bh->b_blocknr, entries2, g.offset); error = -EIO; goto out_kfree; } error = 0; larr[leaf++] = bh; } else { brelse(bh); } } while(lfn); BUG_ON(entries2 != entries); error = do_filldir_main(ip, offset, opaque, filldir, darr, entries, copied);out_kfree: for(i = 0; i < leaf; i++) brelse(larr[i]); vfree(larr);out: return error;}/** * dir_e_read - Reads the entries from a directory into a filldir buffer * @dip: dinode pointer * @offset: the hash of the last entry read shifted to the right once * @opaque: buffer for the filldir function to fill * @filldir: points to the filldir function to use * * Returns: errno */static int dir_e_read(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir){ struct gfs2_inode *dip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); u32 hsize, len = 0; u32 ht_offset, lp_offset, ht_offset_cur = -1; u32 hash, index; __be64 *lp; int copied = 0; int error = 0; unsigned depth = 0; hsize = 1 << dip->i_di.di_depth; if (hsize * sizeof(u64) != dip->i_di.di_size) { gfs2_consist_inode(dip); return -EIO; } hash = gfs2_dir_offset2hash(*offset); index = hash >> (32 - dip->i_di.di_depth); 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; } error = gfs2_dir_read_leaf(inode, offset, opaque, filldir, &copied, &depth, be64_to_cpu(lp[lp_offset])); if (error) break; len = 1 << (dip->i_di.di_depth - depth); index = (index & ~(len - 1)) + len; }out: kfree(lp); if (error > 0) error = 0; return error;}int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir){ struct gfs2_inode *dip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); struct dirent_gather g; const struct gfs2_dirent **darr, *dent; struct buffer_head *dibh; int copied = 0; int error; if (!dip->i_di.di_entries) return 0; if (dip->i_di.di_flags & GFS2_DIF_EXHASH) return dir_e_read(inode, offset, opaque, filldir); if (!gfs2_is_stuffed(dip)) { gfs2_consist_inode(dip); return -EIO; } error = gfs2_meta_inode_buffer(dip, &dibh); if (error) return error; error = -ENOMEM; /* 96 is max number of dirents which can be stuffed into an inode */ darr = kmalloc(96 * sizeof(struct gfs2_dirent *), GFP_KERNEL); if (darr) { g.pdent = darr; g.offset = 0; dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size, gfs2_dirent_gather, NULL, &g); if (IS_ERR(dent)) { error = PTR_ERR(dent); goto out; } if (dip->i_di.di_entries != g.offset) { fs_warn(sdp, "Number of entries corrupt in dir %llu, " "ip->i_di.di_entries (%u) != g.offset (%u)\n", (unsigned long long)dip->i_no_addr, dip->i_di.di_entries, g.offset); error = -EIO; goto out; } error = do_filldir_main(dip, offset, opaque, filldir, darr, dip->i_di.di_entries, &copied);out: kfree(darr); } if (error > 0) error = 0; brelse(dibh); return error;}/** * gfs2_dir_search - Search a directory * @dip: The GFS2 inode * @filename: * @inode: * * This routine searches a directory for a file or another directory. * Assumes a glock is held on dip. * * Returns: errno */struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name){ struct buffer_head *bh; struct gfs2_dirent *dent; struct inode *inode; dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); if (dent) { if (IS_ERR(dent)) return ERR_PTR(PTR_ERR(dent)); inode = gfs2_inode_lookup(dir->i_sb, be16_to_cpu(dent->de_type), be64_to_cpu(dent->de_inum.no_addr), be64_to_cpu(dent->de_inum.no_formal_ino), 0); brelse(bh); return inode; } return ERR_PTR(-ENOENT);}int gfs2_dir_check(struct inode *dir, const struct qstr *name, const struct gfs2_inode *ip){ struct buffer_head *bh; struct gfs2_dirent *dent; int ret = -ENOENT; dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); if (dent) { if (IS_ERR(dent))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -