Hi All,
You can find a webrev for changes to mdb to allow ctf to be loaded with
raw targets at:
http://www.bruningsystems.com/webrev.rawctf.
I'll say more about how this works in a later email.
Usage looks like this:
# mdb /dev/rdsk/c0d0s0 <-- should work with any file
> ::loadctf <-- currently no options, only
loads ctf for running kernel
>
> 2000::print struct fs <-- prints the superblock
You can read my blog at http://mbruning.blogspot.com for some ideas
on how to use it. I have also written a dmod which has one dcmd and one
walker.
The dcmd takes an inumber and returns the disk location of the inode.
The walker takes an inode for a directory and walks the entries in the
directory.
The dmod source is attached. To compile and use, see /usr/demo/mdb/README.
have fun.
max
/*
* Copyright 2007 Max Bruning
*/
#include <sys/mdb_modapi.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/stat.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_acl.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_fsdir.h>
typedef struct ufsdir_walk_data {
int uw_dsize;
int uw_off;
longlong_t uw_dinoaddr;
struct fs *uw_fs;
} ufsdir_walk_data_t;
/*
* Find highest one bit set. from lib/libc/port/gen/mktemp.c (?)
* Returns bit number of highest bit that is set.
* Low order bit is number 0, high order bit is number 31.
*/
static int
uhighbit(uint_t i)
{
int h = 0;
if (i & 0xffff0000)
h += 16, i >>= 16;
if (i & 0xff00)
h += 8, i >>= 8;
if (i & 0xf0)
h += 4, i >>= 4;
if (i & 0xc)
h += 2, i >>= 2;
if (i & 0x2)
h += 1;
return (h);
}
static int
ufsdir_walk_init(mdb_walk_state_t *wsp)
{
ufsdir_walk_data_t *uw;
struct dinode di;
struct fs *fs = mdb_alloc(sizeof(struct fs), UM_SLEEP);
if (wsp->walk_addr == NULL) {
mdb_warn("ufsdir does not support global walks\n");
mdb_warn(" You must specify disk inode location\n");
return (WALK_ERR);
}
/*
* first, get the superblock for some size info...
* this assumes the first superblock is ok. Later,
* I'll add a "setsb" dcmd that allows one to specify
* an alternate superblock.
*/
if (mdb_vread(fs, sizeof(struct fs), 0x2000) == -1) {
mdb_warn("failed to read superblock\n");
return (WALK_ERR);
}
/* read and check the inode */
if (mdb_vread(&di, sizeof(struct dinode), wsp->walk_addr) == -1) {
mdb_warn("failed to read inode at %x\n", wsp->walk_addr);
return (WALK_DONE);
}
if (!(di.di_un.di_icom.ic_smode & S_IFDIR)) {
mdb_warn("inode is not for a directory");
return (WALK_ERR);
}
uw = mdb_alloc(sizeof(ufsdir_walk_data_t), UM_SLEEP);
uw->uw_fs = fs;
uw->uw_dinoaddr = wsp->walk_addr;
uw->uw_off = 0;
/* assume fragment size is 1024 (blkno<<10) */
wsp->walk_addr = (uintptr_t)(di.di_un.di_icom.ic_db[0]<<10);
mdb_printf("%x\n", wsp->walk_addr);
wsp->walk_data = uw;
return (WALK_NEXT);
}
/*
* the following is largely hacked from bmap_read(), os/common/fs/ufs/ufs_bmap.c
*/
static int
ufsdir_walk_step(mdb_walk_state_t *wsp)
{
int status;
struct direct dirp;
struct dinode di;
ufsdir_walk_data_t *uw;
int iblks[2048]; /* assumes blk size is 8192 bytes */
/* the following between lbn and nindiroffset is from bmap_read() */
daddr_t lbn;
struct fs *fs;
int i, j, boff;
int shft; /* we maintain sh = 1 << shft */
daddr_t ob, nb, tbn;
daddr32_t bap[8192]; /* XXX should be dynamically allocated based on fs.bsize */
int nindirshift, nindiroffset;
uw = (ufsdir_walk_data_t *)wsp->walk_data;
fs = uw->uw_fs;
if (mdb_vread(&dirp, sizeof(struct direct), wsp->walk_addr) == -1) {
mdb_warn("cannot read directory entry at %p\n", wsp->walk_addr);
return (WALK_ERR);
}
/* read the inode every time in the step function (in case of changes) */
if (mdb_vread(&di, sizeof(struct dinode), uw->uw_dinoaddr) == -1) {
mdb_warn("cannot read disk inode\n");
return (WALK_ERR);
}
/*
* Now, find the next address. Normally this is the old address
* plus the reclen. But, we need to handle crossing block
* boundaries and very large directories that use indirect blocks.
*/
uw->uw_off += dirp.d_reclen;
if (uw->uw_off >= di.di_un.di_icom.ic_lsize)
return (WALK_DONE);
/* most of the rest of this code comes from bmap_read() */
lbn = (daddr_t)lblkno(fs, uw->uw_off);
boff = (int)blkoff(fs, uw->uw_off);
if (lbn < 0) {
mdb_warn("directory is too big!\n");
return (WALK_ERR);
}
/*
* The first NDADDR blocks are direct blocks.
*/
if (lbn < NDADDR) {
wsp->walk_addr = (di.di_un.di_icom.ic_db[lbn]*fs->fs_fsize) + (uw->uw_off % fs->fs_bsize);
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
return (status);
}
nindiroffset = fs->fs_nindir - 1;
nindirshift = uhighbit(nindiroffset);
/*
* Determine how many levels of indirection.
*/
shft = 0; /* sh = 1 */
tbn = lbn - NDADDR;
for (j = NIADDR; j > 0; j--) {
longlong_t sh;
shft += nindirshift; /* sh *= nindir */
sh = 1LL << shft;
if (tbn < sh)
break;
tbn -= sh;
}
if (j == 0)
return (WALK_ERR);
/*
* Fetch the first indirect block.
*/
nb = di.di_un.di_icom.ic_ib[NIADDR - j];
if (nb == 0) { /* directories can have holes??? */
return (WALK_ERR);
}
/*
* Fetch through the indirect blocks.
*/
for (; j <= NIADDR; j++) {
ob = nb;
if (mdb_vread(bap, fs->fs_bsize, (fsbtodb(fs, ob)*fs->fs_fsize)) == -1) {
mdb_warn("error reading indirect block\n");
return (WALK_ERR);
}
shft -= nindirshift; /* sh / nindir */
i = (tbn >> shft) & nindiroffset; /* (tbn / sh) % nindir */
nb = bap[i];
if (nb == 0) { /* directories can have holes??? */
mdb_warn("hole in directory file\n");
return (WALK_ERR);
}
}
wsp->walk_addr = (bap[i] << 10) + (uw->uw_off % fs->fs_bsize);
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
return (status);
}
static void
ufsdir_walk_fini(mdb_walk_state_t *wsp)
{
ufsdir_walk_data_t *uw = (ufsdir_walk_data_t *)wsp->walk_data;
mdb_free(uw->uw_fs, sizeof(struct fs));
mdb_free(wsp->walk_data, sizeof (ufsdir_walk_data_t));
}
static int
inum2inode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
struct fs fs;
/* XXX - assume using primary superblock */
if (mdb_vread(&fs, sizeof (struct fs), 0x2000) == -1) {
mdb_printf("cannot read superblock");
return (DCMD_ERR);
}
mdb_printf("%x\n", ((addr/fs.fs_ipg)*(fs.fs_fpg*fs.fs_fsize))
+ ((addr/fs.fs_ipg)*(fs.fs_cgoffset*fs.fs_fsize))
+ (fs.fs_iblkno*fs.fs_fsize) + ((addr%fs.fs_ipg) * sizeof(struct dinode)));
}
/*
* MDB module linkage information:
*
* We declare a list of structures describing our dcmds, a list of structures
* describing our walkers, and a function named _mdb_init to return a pointer
* to our module information.
*/
static const mdb_dcmd_t dcmds[] = {
{ "inum2inode", NULL, "given inumber, return address of inode on disk", inum2inode },
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ "ufsdir", "given a disk inode, walk list of ufs directory entries on disk", ufsdir_walk_init, ufsdir_walk_step, ufsdir_walk_fini },
{ NULL }
};
static const mdb_modinfo_t modinfo = {
MDB_API_VERSION, dcmds, walkers
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}
_______________________________________________
opensolaris-discuss mailing list
opensolaris-discuss@opensolaris.org