/*
 * Copyright (C) 2000-2002 ASANO Masahiro
 */

#include "def.h"

#define __KERNEL__
#include <linux/fs.h>

#include "printdev.h"

void
prhead_buffer_head()
{
	mprintf(SPTR"    BLOCK  SIZE COUNT  DEV   RDEV   "SPTR"  FLAG\n",
		"ADDR");
}

void
print_kdev_t(kdev)
	kdev_t kdev;
{
	mprintf("%2d,%-3d", MAJOR(kdev), MINOR(kdev));
}

addr_t
print_buffer_head(addr, full)
	addr_t addr;
	int full;
{
	struct buffer_head buf;
	static const struct bitname bstate[] = {
		{ 1<<BH_Uptodate,	"uptodate" },
		{ 1<<BH_Dirty,		"dirty" },
		{ 1<<BH_Lock,		"lock" },
		{ 1<<BH_Req,		"req" },
		{ 1<<BH_Protected,	"protected" },
#ifdef BH_LowPrio
		{ 1<<BH_LowPrio,	"lowprio" },
#endif
#ifdef BH_Wait_IO
		{ 1<<BH_Wait_IO,	"waitio" },
#endif
		{ 0,			NULL }
	};

	memread(addr, sizeof(struct buffer_head), &buf, "buffer_head");
	mprintf(FPTR " ", addr);

	mprintf("%8lx %5ld %5d ", buf.b_blocknr, buf.b_size, buf.b_count);
	print_kdev_t(buf.b_dev);
	mprintf(" ");
	print_kdev_t(buf.b_rdev);
	mprintf(" " FPTR, buf.b_data);
	mprintbit(bstate, (long)buf.b_state);
	mprintf("\n");
	return (addr_t)buf.b_next;
}

void
prhead_dentry()
{
	mprintf(SPTR" CNT "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" NAME\n",
		"ADDR", "INODE", "PARENT", "MOUNTS", "COVERS", "OP", "FSDATA");
}

addr_t
print_dentry(addr, dhash, subdir, parent)
addr_t addr;
int dhash, subdir, parent;
{
	struct dentry de;
	addr_t child, next;

	if (dhash) {
		addr -= (addr_t)&((struct dentry *)NULL)->d_hash;
	}

	memread(addr, sizeof(de), &de, "dentry");
	mprintf(FPTR " ", addr);
	mprintf("%3x %8lx %8lx %8lx %8lx", de.d_count, de.d_inode, de.d_parent, de.d_mounts, de.d_covers);
	mprintf(" %8lx %8lx ", de.d_op, de.d_fsdata);
#if 0
	mprintf(" %8lx %8lx ", de.d_time, de.d_reftime);
#endif
	/*mprint_str(de.d_iname, sizeof(de.d_iname));*/
	if (de.d_name.name == ((struct dentry *)addr)->d_iname)
		mprint_str(de.d_iname, sizeof(de.d_iname));
	else if (de.d_name.name == NULL)
		mprintf("[null]");
	else {
		char qbuf[256];
		int len = de.d_name.len < sizeof(qbuf)? de.d_name.len: sizeof(qbuf);
		memread((addr_t)de.d_name.name, len, qbuf, "dentry.d_name");
		mprint_str(qbuf, len);
	}
	mprintf("\n");

	if (parent) {
		if (de.d_parent && (addr_t)de.d_parent != addr) {
			return print_dentry(de.d_parent, 0, 0, 1);
		}
		if (de.d_covers && (addr_t)de.d_covers != addr) {
			return print_dentry(de.d_covers, 0, 0, 1);
		}
		return 0;
	}

	if (subdir) {
		for (next = (addr_t)de.d_subdirs.next; next != (addr_t)&((struct dentry *)addr)->d_subdirs; next = (addr_t)de.d_child.next) {
			child = next - (addr_t)&((struct dentry *)NULL)->d_child;
			memread(child, sizeof(de), &de, "dentry");
			(void)print_dentry(child, 0, 0, 0);
		}
		return 0;
	}
	return (addr_t)de.d_hash.next;
}

const char head_file[] = "    ADDR   DENTRY       OP MOD      POS CNT   UID   GID ERR  PRIVATE FLAGS\n";

addr_t
print_file(addr, full)
addr_t addr;
int full;
{
	struct file f;
	static const struct bitname fflags[] = {
		{ O_APPEND,	"append" },
		{ O_NONBLOCK,	"nonblock" },
		{ O_SYNC,	"sync" },
		{ FASYNC,	"fasync" },
		{ O_DIRECT,	"direct" },
		{ O_LARGEFILE,	"largefile" },
		{ 0,		NULL }
	};

	memread(addr, sizeof(f), &f, "file");
	mprintf(FPTR " ", addr);
	mprintf(FPTR " " FPTR "  ", f.f_dentry, f.f_op);
	switch (f.f_mode) {
	case 0:	mprintf(" -");	break;
	case 1: mprintf("r ");	break;
	case 2: mprintf(" w");	break;
	case 3: mprintf("rw");	break;
	default:mprintf(" ?");	break;
	}
	mprintf(" %8lx %3d %5d %5d %3d", (long)f.f_pos, f.f_count, f.f_uid, f.f_gid, f.f_error);
	mprintf(" %8lx", f.private_data);
	mprintbit(fflags, f.f_flags);
	mprintf("\n");
	return (addr_t)f.f_next;
}

void
prhead_filesystem()
{
	mprintf(SPTR"       NAME "SPTR" FLAG\n", "ADDR", "READ");
}

addr_t
print_file_system_type(addr)
	addr_t addr;
{
	struct file_system_type fs;
	char buf[16];
	static const struct bitname fsflags[] = {
		{ FS_REQUIRES_DEV,	"requires_dev" },
		{ FS_NO_DCACHE,		"no_dcache" },
		{ FS_NO_PRELIM,		"no_prelim" },
		{ FS_IBASKET,		"ibasket" },
		{ 0,			NULL }
	};

	memread(addr, sizeof(fs), &fs, "file_system_type");
	memread((addr_t)fs.name, sizeof(buf), buf, "file system type name");
	mprintf(FPTR, addr);

	mprintf(" %10s %8lx", buf, fs.read_super);
	mprintbit(fsflags, fs.fs_flags);
	mprintf("\n");
	return (addr_t)fs.next;
}

const char head_inode[] = "    ADDR      INO CNT  DEV  LINK MODE   UID     SIZE S FLAG\n";

addr_t
print_inode(addr, full)
	addr_t addr;
	int full;
{
	struct inode inode;

	memread(addr, sizeof(struct inode), &inode, "inode");
	mprintf(FPTR, addr);
	mprintf(" %8ld %3d ", inode.i_ino, inode.i_count);
	print_kdev_t(inode.i_dev);
	mprintf(" %3d ", inode.i_nlink);
	pmode(inode.i_mode);
	mprintf(" %5d %8ld", inode.i_uid, inode.i_size);
	mprintf(" %lx", inode.i_state);
	mprintf(" %x\n", inode.i_flags);

	if (full) {
		mprintf("\trdev:");
		print_kdev_t(inode.i_rdev);
		mprintf("  blksize:%ld  blocks:%ld  version:%ld\n", inode.i_blksize, inode.i_blocks, inode.i_version);
		mprintf("\tatime:%08lx  mtime:%08lx  ctime:%08lx\n",
		       inode.i_atime, inode.i_mtime, inode.i_ctime);
		mprintf("\tnrpages:%ld  mmap:%lx  pages:%lx\n", inode.i_nrpages, inode.i_mmap, inode.i_pages);
		mprintf("\top:%8lx  sb:%8lx  gen:%u\n", inode.i_op, inode.i_sb, inode.i_generation);
		mprintf("\tu:%8lx\n", (addr_t)&((struct inode *)addr)->u);
		mprintf("\n");
	}
	return (addr_t)inode.i_hash.next;
}

void
prhead_superblock()
{
	mprintf(SPTR"  DEV   BSIZE       TYPE "SPTR" "SPTR" "SPTR" "SPTR" FLAG\n",
		"ADDR", "ROOT", "S_OP", "DIRTY.N", "DIRTY.P");
}

addr_t
print_superblock(addr, full)
	addr_t addr;
	int full;
{
	struct super_block sb;
	struct file_system_type fstype;
	char buf[16];
	static const struct bitname sflags[] = {
		{ MS_RDONLY,	"rdonly" },
		{ MS_NOSUID,	"nosuid" },
		{ MS_NODEV,	"nodev" },
		{ MS_NOEXEC,	"noexec" },
		{ MS_SYNCHRONOUS,"synchronous" },
		{ MS_REMOUNT,	"remount" },
		{ MS_MANDLOCK,	"mandlock" },
		{ S_QUOTA,	"quota" },
		{ S_APPEND,	"append" },
		{ S_IMMUTABLE,	"immutable" },
		{ MS_NOATIME,	"noatime" },
		{ MS_NODIRATIME,"nodirtime" },
		{ MS_ODD_RENAME,"odd_rename" },
		{ 0,		NULL }
	};

	memread(addr, sizeof(struct super_block), &sb, "super_block");
	mprintf(FPTR " ", addr);

	print_kdev_t(sb.s_dev);
	mprintf(" %5ld", sb.s_blocksize);

#if 1
	memread((addr_t)sb.s_type, sizeof(fstype), &fstype, "file system type");
	memread((addr_t)fstype.name, sizeof(buf), buf, "file system type name");
	mprintf(" %10s", buf);
#else
	if ((p = searchsym_byaddr((addr_t)sb.s_type)) == NULL || p->addr != (addr_t)sb.s_type) {
		mprintf(" %10x", sb.s_type);
	} else {
		mprintf(" %10s", p->name);
	}
#endif
	mprintf(" %8lx %8lx", sb.s_root, sb.s_op);
	if (sb.s_dirty.next == &((struct super_block *)addr)->s_dirty) {
		mprintf("     -   ");
	} else {
		mprintf(" %8lx", sb.s_dirty.next);
	}
	if (sb.s_dirty.prev == &((struct super_block *)addr)->s_dirty) {
		mprintf("     -   ");
	} else {
		mprintf(" %8lx", sb.s_dirty.prev);
	}
	mprintbit(sflags, sb.s_flags);
	mprintf("\n");

	if (full) {
		mprintf("\tsuper_operations:%8lx\n\tdquot_operations:%8lx\n",
			sb.s_op, sb.dq_op);
	}

	return (addr_t)sb.s_list.next;
}

void
prhead_vfsmount()
{
	mprintf(SPTR"  DEV                 DEVICE             DIRECTRY "SPTR" FLAGS\n",
		"ADDR", "SUPERBLK");
}

addr_t
print_vfsmount(addr, full)
	addr_t addr;
	int full;
{
	struct vfsmount m;
	char path[PATH_MAX];

	memread(addr, sizeof(struct vfsmount), &m, "vfsmount");
	mprintf(FPTR " ", addr);
	print_kdev_t(m.mnt_dev);

	memread((addr_t)m.mnt_devname, sizeof(path), path, "mnt_devname");
	mprintf(" %20s", path);
	memread((addr_t)m.mnt_dirname, sizeof(path), path, "mnt_dirname");
	mprintf(" %20s", path);
	mprintf(" %8lx", m.mnt_sb);
	mprintf("%s%s%s",
		(m.mnt_flags & MNT_FORCE)? " force": "",
		(m.mnt_dquot.flags & DQUOT_USR_ENABLED)? " dqusr": "",
		(m.mnt_dquot.flags & DQUOT_USR_ENABLED)? " dqusr": "");
	mprintf("\n");
	return (addr_t) m.mnt_next;
}
