/*ARGSUSED1*/ xfs_dahash_t xfs_da_cookie_hash(xfs_mount_t *mp, xfs_off_t cookie) { return XFS_DA_COOKIE_HASH(mp, cookie); }
STATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp, dirent_t *dbp, xfs_dir_put_t put) { xfs_da_intnode_t *node; xfs_da_node_entry_t *btree; xfs_dir_leafblock_t *leaf; xfs_dablk_t bno, nextbno; xfs_dahash_t cookhash; xfs_mount_t *mp; int error, eob, i; xfs_dabuf_t *bp; xfs_daddr_t nextda; /* * Pick up our context. */ mp = dp->i_mount; bp = NULL; bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset); cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset); xfs_dir_trace_g_du("node: start", dp, uio); /* * Re-find our place, even if we're confused about what our place is. * * First we check the block number from the magic cookie, it is a * cache of where we ended last time. If we find a leaf block, and * the starting hashval in that block is less than our desired * hashval, then we run with it. */ if (bno > 0) { error = xfs_da_read_buf(trans, dp, bno, -1, &bp, XFS_DATA_FORK); if ((error != 0) && (error != EFSCORRUPTED)) return(error); if (bp) leaf = bp->data; if (bp && INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) { xfs_dir_trace_g_dub("node: block not a leaf", dp, uio, bno); xfs_da_brelse(trans, bp); bp = NULL; } if (bp && INT_GET(leaf->entries[0].hashval, ARCH_CONVERT) > cookhash) { xfs_dir_trace_g_dub("node: leaf hash too large", dp, uio, bno); xfs_da_brelse(trans, bp); bp = NULL; } if (bp && cookhash > INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)) { xfs_dir_trace_g_dub("node: leaf hash too small", dp, uio, bno); xfs_da_brelse(trans, bp); bp = NULL; } } /* * If we did not find a leaf block from the blockno in the cookie, * or we there was no blockno in the cookie (eg: first time thru), * the we start at the top of the Btree and re-find our hashval. */ if (bp == NULL) { xfs_dir_trace_g_du("node: start at root" , dp, uio); bno = 0; for (;;) { error = xfs_da_read_buf(trans, dp, bno, -1, &bp, XFS_DATA_FORK); if (error) return(error); if (bp == NULL) return(XFS_ERROR(EFSCORRUPTED)); node = bp->data; if (INT_GET(node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC) break; btree = &node->btree[0]; xfs_dir_trace_g_dun("node: node detail", dp, uio, node); for (i = 0; i < INT_GET(node->hdr.count, ARCH_CONVERT); btree++, i++) { if (INT_GET(btree->hashval, ARCH_CONVERT) >= cookhash) { bno = INT_GET(btree->before, ARCH_CONVERT); break; } } if (i == INT_GET(node->hdr.count, ARCH_CONVERT)) { xfs_da_brelse(trans, bp); xfs_dir_trace_g_du("node: hash beyond EOF", dp, uio); uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH); *eofp = 1; return(0); } xfs_dir_trace_g_dub("node: going to block", dp, uio, bno); xfs_da_brelse(trans, bp); } } ASSERT(cookhash != XFS_DA_MAXHASH); /* * We've dropped down to the (first) leaf block that contains the * hashval we are interested in. Continue rolling upward thru the * leaf blocks until we fill up our buffer. */ for (;;) { leaf = bp->data; if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) { xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf); xfs_da_brelse(trans, bp); return XFS_ERROR(EFSCORRUPTED); } xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf); if (nextbno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT)) { nextda = xfs_da_reada_buf(trans, dp, nextbno, XFS_DATA_FORK); } else nextda = -1; error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp, put, nextda); xfs_da_brelse(trans, bp); bno = nextbno; if (eob) { xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno); *eofp = 0; return(error); } if (bno == 0) break; error = xfs_da_read_buf(trans, dp, bno, nextda, &bp, XFS_DATA_FORK); if (error) return(error); if (bp == NULL) return(XFS_ERROR(EFSCORRUPTED)); } *eofp = 1; xfs_dir_trace_g_du("node: E-O-F", dp, uio); return(0); }