794 lines
23 KiB
C
794 lines
23 KiB
C
/*
|
|
* 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;
|
|
}
|