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

Reply via email to