/*
* Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <cache.h>
#include <core.h>
#include <fs.h>
#include "xfs_types.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "misc.h"
#include "xfs.h"
#include "xfs_dinode.h"
#include "xfs_dir2.h"
#define XFS_DIR2_DIRBLKS_CACHE_SIZE 128
struct xfs_dir2_dirblks_cache {
block_t dc_startblock;
xfs_filblks_t dc_blkscount;
void *dc_area;
};
static struct xfs_dir2_dirblks_cache dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE];
static unsigned char dirblks_cached_count = 0;
uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen)
{
uint32_t hash;
/*
* Do four characters at a time as long as we can.
*/
for (hash = 0; namelen >= 4; namelen -=4, name += 4)
hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
(name[3] << 0) ^ rol32(hash, 7 * 4);
/*
* Now do the rest of the characters.
*/
switch (namelen) {
case 3:
return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
rol32(hash, 7 * 3);
case 2:
return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
case 1:
return (name[0] << 0) ^ rol32(hash, 7 * 1);
default: /* case 0: */
return hash;
}
}
static void *get_dirblks(struct fs_info *fs, block_t startblock,
xfs_filblks_t c)
{
int count = c << XFS_INFO(fs)->dirblklog;
uint8_t *p;
uint8_t *buf;
off_t offset = 0;
buf = malloc(c * XFS_INFO(fs)->dirblksize);
if (!buf)
malloc_error("buffer memory");
memset(buf, 0, XFS_INFO(fs)->dirblksize);
while (count--) {
p = (uint8_t *)get_cache(fs->fs_dev, startblock++);
memcpy(buf + offset, p, BLOCK_SIZE(fs));
offset += BLOCK_SIZE(fs);
}
return buf;
}
const void *xfs_dir2_dirblks_get_cached(struct fs_info *fs, block_t startblock,
xfs_filblks_t c)
{
unsigned char i;
void *buf;
xfs_debug("fs %p startblock %llu (0x%llx) blkscount %lu", fs, startblock,
startblock, c);
if (!dirblks_cached_count) {
buf = get_dirblks(fs, startblock, c);
dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
dirblks_cache[dirblks_cached_count].dc_blkscount = c;
dirblks_cache[dirblks_cached_count].dc_area = buf;
return dirblks_cache[dirblks_cached_count++].dc_area;
} else if (dirblks_cached_count == XFS_DIR2_DIRBLKS_CACHE_SIZE) {
for (i = 0; i < XFS_DIR2_DIRBLKS_CACHE_SIZE / 2; i++) {
unsigned char k = XFS_DIR2_DIRBLKS_CACHE_SIZE - (i + 1);
free(dirblks_cache[i].dc_area);
dirblks_cache[i] = dirblks_cache[k];
memset(&dirblks_cache[k], 0, sizeof(dirblks_cache[k]));
}
buf = get_dirblks(fs, startblock, c);
dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_startblock =
startblock;
dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_blkscount = c;
dirblks_cache[XFS_DIR2_DIRBLKS_CACHE_SIZE / 2].dc_area = buf;
dirblks_cached_count = XFS_DIR2_DIRBLKS_CACHE_SIZE / 2;
return dirblks_cache[dirblks_cached_count++].dc_area;
} else {
block_t block;
xfs_filblks_t count;
block = dirblks_cache[dirblks_cached_count - 1].dc_startblock;
count = dirblks_cache[dirblks_cached_count - 1].dc_blkscount;
if (block == startblock && count == c) {
return dirblks_cache[dirblks_cached_count - 1].dc_area;
} else {
for (i = 0; i < dirblks_cached_count; i++) {
block = dirblks_cache[i].dc_startblock;
count = dirblks_cache[i].dc_blkscount;
if (block == startblock && count == c)
return dirblks_cache[i].dc_area;
}
buf = get_dirblks(fs, startblock, c);
dirblks_cache[dirblks_cached_count].dc_startblock = startblock;
dirblks_cache[dirblks_cached_count].dc_blkscount = c;
dirblks_cache[dirblks_cached_count].dc_area = buf;
return dirblks_cache[dirblks_cached_count++].dc_area;
}
}
return NULL;
}
void xfs_dir2_dirblks_flush_cache(void)
{
unsigned char i;
for (i = 0; i < dirblks_cached_count; i++) {
free(dirblks_cache[i].dc_area);
memset(&dirblks_cache[i], 0, sizeof(dirblks_cache[i]));
}
dirblks_cached_count = 0;
}
struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent,
xfs_dinode_t *core)
{
xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
xfs_dir2_sf_entry_t *sf_entry;
uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
struct fs_info *fs = parent->fs;
struct inode *inode;
xfs_intino_t ino;
xfs_dinode_t *ncore = NULL;
xfs_debug("dname %s parent %p core %p", dname, parent, core);
xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
(!sf->hdr.i8count ? 4 : 0));
while (count--) {
uint8_t *start_name = &sf_entry->name[0];
uint8_t *end_name = start_name + sf_entry->namelen;
if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
xfs_debug("Found entry %s", dname);
goto found;
}
sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry +
offsetof(struct xfs_dir2_sf_entry,
name[0]) +
sf_entry->namelen +
(sf->hdr.i8count ? 8 : 4));
}
return NULL;
found:
inode = xfs_new_inode(fs);
ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
(uint8_t *)sf_entry +
offsetof(struct xfs_dir2_sf_entry,
name[0]) +
sf_entry->namelen));
xfs_debug("entry inode's number %lu", ino);
ncore = xfs_dinode_get_core(fs, ino);
if (!ncore) {
xfs_error("Failed to get dinode!");
goto out;
}
fill_xfs_inode_pvt(fs, inode, ino);
inode->ino = ino;
inode->size = be64_to_cpu(ncore->di_size);
if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
inode->mode = DT_DIR;
xfs_debug("Found a directory inode!");
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
inode->mode = DT_REG;
xfs_debug("Found a file inode!");
xfs_debug("inode size %llu", inode->size);
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
inode->mode = DT_LNK;
xfs_debug("Found a symbolic link inode!");
}
return inode;
out:
free(inode);
return NULL;
}
struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent,
xfs_dinode_t *core)
{
xfs_bmbt_irec_t r;
block_t dir_blk;
struct fs_info *fs = parent->fs;
const uint8_t *dirblk_buf;
uint8_t *p, *endp;
xfs_dir2_data_hdr_t *hdr;
struct inode *inode = NULL;
xfs_dir2_block_tail_t *btp;
xfs_dir2_data_unused_t *dup;
xfs_dir2_data_entry_t *dep;
xfs_intino_t ino;
xfs_dinode_t *ncore;
xfs_debug("dname %s parent %p core %p", dname, parent, core);
bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
xfs_error("Block directory header's magic number does not match!");
xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
goto out;
}
p = (uint8_t *)(hdr + 1);
btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count));
while (p < endp) {
uint8_t *start_name;
uint8_t *end_name;
dup = (xfs_dir2_data_unused_t *)p;
if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
p += be16_to_cpu(dup->length);
continue;
}
dep = (xfs_dir2_data_entry_t *)p;
start_name = &dep->name[0];
end_name = start_name + dep->namelen;
if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
xfs_debug("Found entry %s", dname);
goto found;
}
p += xfs_dir2_data_entsize(dep->namelen);
}
out:
return NULL;
found:
inode = xfs_new_inode(fs);
ino = be64_to_cpu(dep->inumber);
xfs_debug("entry inode's number %lu", ino);
ncore = xfs_dinode_get_core(fs, ino);
if (!ncore) {
xfs_error("Failed to get dinode!");
goto failed;
}
fill_xfs_inode_pvt(fs, inode, ino);
inode->ino = ino;
XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs);
inode->size = be64_to_cpu(ncore->di_size);
if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
inode->mode = DT_DIR;
xfs_debug("Found a directory inode!");
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
inode->mode = DT_REG;
xfs_debug("Found a file inode!");
xfs_debug("inode size %llu", inode->size);
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
inode->mode = DT_LNK;
xfs_debug("Found a symbolic link inode!");
}
xfs_debug("entry inode's number %lu", ino);
return inode;
failed:
free(inode);
return NULL;
}
struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent,
xfs_dinode_t *core)
{
xfs_dir2_leaf_t *leaf;
xfs_bmbt_irec_t irec;
block_t leaf_blk, dir_blk;
xfs_dir2_leaf_entry_t *lep;
int low;
int high;
int mid = 0;
uint32_t hash = 0;
uint32_t hashwant;
uint32_t newdb, curdb = -1;
xfs_dir2_data_entry_t *dep;
struct inode *ip;
xfs_dir2_data_hdr_t *data_hdr;
uint8_t *start_name;
uint8_t *end_name;
xfs_intino_t ino;
xfs_dinode_t *ncore;
const uint8_t *buf = NULL;
xfs_debug("dname %s parent %p core %p", dname, parent, core);
bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
be32_to_cpu(core->di_nextents) - 1);
leaf_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
BLOCK_SHIFT(parent->fs);
leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(parent->fs, leaf_blk,
irec.br_blockcount);
if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
xfs_error("Single leaf block header's magic number does not match!");
goto out;
}
if (!leaf->hdr.count)
goto out;
hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
/* Binary search */
for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
low <= high; ) {
mid = (low + high) >> 1;
if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
break;
if (hash < hashwant)
low = mid + 1;
else
high = mid - 1;
}
/* If hash is not the one we want, then the directory does not contain the
* entry we're looking for and there is nothing to do anymore.
*/
if (hash != hashwant)
goto out;
while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
mid--;
for (lep = &leaf->ents[mid];
mid < be16_to_cpu(leaf->hdr.count) &&
be32_to_cpu(lep->hashval) == hashwant;
lep++, mid++) {
/* Skip over stale leaf entries. */
if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
continue;
newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
if (newdb != curdb) {
bmbt_irec_get(&irec,
((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb);
dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >>
BLOCK_SHIFT(parent->fs);
buf = xfs_dir2_dirblks_get_cached(parent->fs, dir_blk, irec.br_blockcount);
data_hdr = (xfs_dir2_data_hdr_t *)buf;
if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
xfs_error("Leaf directory's data magic No. does not match!");
goto out;
}
curdb = newdb;
}
dep = (xfs_dir2_data_entry_t *)((char *)buf +
xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
start_name = &dep->name[0];
end_name = start_name + dep->namelen;
if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
xfs_debug("Found entry %s", dname);
goto found;
}
}
out:
return NULL;
found:
ip = xfs_new_inode(parent->fs);
ino = be64_to_cpu(dep->inumber);
xfs_debug("entry inode's number %lu", ino);
ncore = xfs_dinode_get_core(parent->fs, ino);
if (!ncore) {
xfs_error("Failed to get dinode!");
goto failed;
}
fill_xfs_inode_pvt(parent->fs, ip, ino);
ip->ino = ino;
XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
BLOCK_SHIFT(parent->fs);
ip->size = be64_to_cpu(ncore->di_size);
if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
ip->mode = DT_DIR;
xfs_debug("Found a directory inode!");
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
ip->mode = DT_REG;
xfs_debug("Found a file inode!");
xfs_debug("inode size %llu", ip->size);
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
ip->mode = DT_LNK;
xfs_debug("Found a symbolic link inode!");
}
xfs_debug("entry inode's number %lu", ino);
return ip;
failed:
free(ip);
return ip;
}
static xfs_fsblock_t
select_child(xfs_dfiloff_t off,
xfs_bmbt_key_t *kp,
xfs_bmbt_ptr_t *pp,
int nrecs)
{
int i;
for (i = 0; i < nrecs; i++) {
if (be64_to_cpu(kp[i].br_startoff) == off)
return be64_to_cpu(pp[i]);
if (be64_to_cpu(kp[i].br_startoff) > off) {
if (i == 0)
return be64_to_cpu(pp[i]);
else
return be64_to_cpu(pp[i-1]);
}
}
return be64_to_cpu(pp[nrecs - 1]);
}
block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core,
block_t fsblkno, int *error)
{
uint32_t idx;
xfs_bmbt_irec_t irec;
block_t bno;
block_t nextbno;
xfs_bmdr_block_t *rblock;
int fsize;
int nextents;
xfs_bmbt_ptr_t *pp;
xfs_bmbt_key_t *kp;
xfs_btree_block_t *blk;
xfs_bmbt_rec_t *xp;
*error = 0;
if (core->di_format == XFS_DINODE_FMT_EXTENTS) {
xfs_debug("XFS_DINODE_FMT_EXTENTS");
for (idx = 0; idx < be32_to_cpu(core->di_nextents); idx++) {
bmbt_irec_get(&irec,
((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx);
if (fsblkno >= irec.br_startoff &&
fsblkno < irec.br_startoff + irec.br_blockcount)
break;
}
} else if (core->di_format == XFS_DINODE_FMT_BTREE) {
xfs_debug("XFS_DINODE_FMT_BTREE");
bno = NULLFSBLOCK;
rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0];
fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK);
pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0));
kp = XFS_BMDR_KEY_ADDR(rblock, 1);
bno = fsblock_to_bytes(fs,
select_child(fsblkno, kp, pp,
be16_to_cpu(rblock->bb_numrecs))) >> BLOCK_SHIFT(fs);
/* Find the leaf */
for (;;) {
blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
if (be16_to_cpu(blk->bb_level) == 0)
break;
pp = XFS_BMBT_PTR_ADDR(fs, blk, 1,
xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0));
kp = XFS_BMBT_KEY_ADDR(fs, blk, 1);
bno = fsblock_to_bytes(fs,
select_child(fsblkno, kp, pp,
be16_to_cpu(blk->bb_numrecs))) >> BLOCK_SHIFT(fs);
}
/* Find the records among leaves */
for (;;) {
nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib);
nextents = be16_to_cpu(blk->bb_numrecs);
xp = (xfs_bmbt_rec_t *)XFS_BMBT_REC_ADDR(fs, blk, 1);
for (idx = 0; idx < nextents; idx++) {
bmbt_irec_get(&irec, xp + idx);
if (fsblkno >= irec.br_startoff &&
fsblkno < irec.br_startoff + irec.br_blockcount) {
nextbno = NULLFSBLOCK;
break;
}
}
if (nextbno == NULLFSBLOCK)
break;
bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs);
blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno);
}
}
if (fsblkno < irec.br_startoff ||
fsblkno >= irec.br_startoff + irec.br_blockcount)
*error = 1;
return fsblock_to_bytes(fs,
fsblkno - irec.br_startoff + irec.br_startblock) >>
BLOCK_SHIFT(fs);
}
struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent,
xfs_dinode_t *core)
{
block_t fsblkno;
xfs_da_intnode_t *node = NULL;
uint32_t hashwant;
uint32_t hash = 0;
xfs_da_node_entry_t *btree;
uint16_t max;
uint16_t span;
uint16_t probe;
int error;
xfs_dir2_data_hdr_t *data_hdr;
xfs_dir2_leaf_t *leaf;
xfs_dir2_leaf_entry_t *lep;
xfs_dir2_data_entry_t *dep;
struct inode *ip;
uint8_t *start_name;
uint8_t *end_name;
int low;
int high;
int mid = 0;
uint32_t newdb, curdb = -1;
xfs_intino_t ino;
xfs_dinode_t *ncore;
const uint8_t *buf = NULL;
xfs_debug("dname %s parent %p core %p", dname, parent, core);
hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname));
fsblkno = xfs_dir2_get_right_blk(parent->fs, core,
xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET),
&error);
if (error) {
xfs_error("Cannot find right rec!");
return NULL;
}
node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs, fsblkno,
1);
if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
xfs_error("Node's magic number does not match!");
goto out;
}
do {
if (!node->hdr.count)
goto out;
/* Given a hash to lookup, you read the node's btree array and first
* "hashval" in the array that exceeds the given hash and it can then
* be found in the block pointed by the "before" value.
*/
max = be16_to_cpu(node->hdr.count);
probe = span = max/2;
for (btree = &node->btree[probe];
span > 4; btree = &node->btree[probe]) {
span /= 2;
hash = be32_to_cpu(btree->hashval);
if (hash < hashwant)
probe += span;
else if (hash > hashwant)
probe -= span;
else
break;
}
while ((probe > 0) && (be32_to_cpu(btree->hashval) >= hashwant)) {
btree--;
probe--;
}
while ((probe < max) && (be32_to_cpu(btree->hashval) < hashwant)) {
btree++;
probe++;
}
if (probe == max)
fsblkno = be32_to_cpu(node->btree[max-1].before);
else
fsblkno = be32_to_cpu(node->btree[probe].before);
fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error);
if (error) {
xfs_error("Cannot find right rec!");
goto out;
}
node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(parent->fs,
fsblkno, 1);
} while(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC);
leaf = (xfs_dir2_leaf_t*)node;
if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
xfs_error("Leaf's magic number does not match!");
goto out;
}
if (!leaf->hdr.count)
goto out;
for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1;
low <= high; ) {
mid = (low + high) >> 1;
if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant)
break;
if (hash < hashwant)
low = mid + 1;
else
high = mid - 1;
}
/* If hash is not the one we want, then the directory does not contain the
* entry we're looking for and there is nothing to do anymore.
*/
if (hash != hashwant)
goto out;
while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant)
mid--;
for (lep = &leaf->ents[mid];
mid < be16_to_cpu(leaf->hdr.count) &&
be32_to_cpu(lep->hashval) == hashwant;
lep++, mid++) {
/* Skip over stale leaf entries. */
if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
continue;
newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address));
if (newdb != curdb) {
fsblkno = xfs_dir2_get_right_blk(parent->fs, core, newdb, &error);
if (error) {
xfs_error("Cannot find data block!");
goto out;
}
buf = xfs_dir2_dirblks_get_cached(parent->fs, fsblkno, 1);
data_hdr = (xfs_dir2_data_hdr_t *)buf;
if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
xfs_error("Leaf directory's data magic No. does not match!");
goto out;
}
curdb = newdb;
}
dep = (xfs_dir2_data_entry_t *)((char *)buf +
xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address)));
start_name = &dep->name[0];
end_name = start_name + dep->namelen;
if (!xfs_dir2_entry_name_cmp(start_name, end_name, dname)) {
xfs_debug("Found entry %s", dname);
goto found;
}
}
out:
return NULL;
found:
ip = xfs_new_inode(parent->fs);
ino = be64_to_cpu(dep->inumber);
ncore = xfs_dinode_get_core(parent->fs, ino);
if (!ncore) {
xfs_error("Failed to get dinode!");
goto failed;
}
fill_xfs_inode_pvt(parent->fs, ip, ino);
ip->ino = ino;
XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >>
BLOCK_SHIFT(parent->fs);
ip->size = be64_to_cpu(ncore->di_size);
if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) {
ip->mode = DT_DIR;
xfs_debug("Found a directory inode!");
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) {
ip->mode = DT_REG;
xfs_debug("Found a file inode!");
xfs_debug("inode size %llu", ip->size);
} else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) {
ip->mode = DT_LNK;
xfs_debug("Found a symbolic link inode!");
}
xfs_debug("entry inode's number %lu", ino);
return ip;
failed:
free(ip);
return NULL;
}