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

#include "def.h"

#define __KERNEL__
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mount.h>	/* for vfsmount */

#include "printdev.h"
#include "flags_fs.h"

void
print_kdev_t(kdev)
	kdev_t kdev;
{
	mprintf("%3d,%-3d", major(kdev), minor(kdev));
}

void
print_dev_t(dev)
	dev_t dev;
{
	mprintf("%3d,%-3d", MAJOR(dev), MINOR(dev));
}

void
prhead_dentry()
{
	mprintf(SPTR" CNT "SPTR" "SPTR" HLCSA "SPTR" "SPTR" F NAME\n",
		"ADDR", "INODE", "PARENT", "D_OP", "SB");
}

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

	switch (sw) {
	case 1:	/* hash */
		addr -= OFFSET(struct dentry, d_hash);
		break;
	case 2:	/* lru */
		addr -= OFFSET(struct dentry, d_lru);
		break;
	}
	memread(addr, sizeof(de), &de, "dentry");

	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 - OFFSET(struct dentry, d_child);
			memread(child, sizeof(de), &de, "dentry");
			(void)print_dentry(child, 0, 0, 0);
		}
		return 0;
	}

	mprintf(FPTR, addr);
	mprintf(" %3x " FPTR " " FPTR, ATOMIC_READ(de.d_count), de.d_inode, de.d_parent);
	mprintf(" %c%c%c%c%c ",
		(de.d_hash.next   !=&((struct dentry *)addr)->d_hash   )? 'H': '-',
		(de.d_lru.next    !=&((struct dentry *)addr)->d_lru    )? 'L': '-',
		(de.d_child.next  !=&((struct dentry *)addr)->d_child  )? 'C': '-',
		(de.d_subdirs.next!=&((struct dentry *)addr)->d_subdirs)? 'S': '-',
		(de.d_alias.next  !=&((struct dentry *)addr)->d_alias  )? 'A': '-');
	mprintf(FPTR " ", de.d_op);
	mprintf(FPTR " ", de.d_sb);
#if 1
	mprintf("%lx ", de.d_vfs_flags);
#endif
	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);
		}
		return 0;
	}

	if (sw == 2)
		return (addr_t)de.d_lru.next;
	else
		return (addr_t)de.d_hash.next;
}

void
prhead_file()
{
	mprintf(SPTR" "SPTR" "SPTR" MOD      POS CNT   UID   GID ERR "SPTR" FLAGS\n",
		"ADDR", "DENTRY", "F_OP", "PRIVATE");
}

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 %3x %5d %5d %3d", f.f_pos, ATOMIC_READ(f.f_count), f.f_uid, f.f_gid, f.f_error);
	mprintf(" " FPTR, f.private_data);
	mprintbit(fflags, f.f_flags);
	mprintf("\n");
	return (addr_t)f.f_list.next;
}

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

addr_t
print_file_system_type(addr)
	addr_t addr;
{
	struct file_system_type fs;
	char buf[16];

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

	mprintf(" %11s " FPTR " " FPTR " " FPTR, buf, fs.get_sb, fs.kill_sb, fs.owner);
	mprintbit(fsflags, fs.fs_flags);
	mprintf("\n");
	return (addr_t)fs.next;
}

void
prhead_inode()
{
	mprintf(SPTR"      INO CNT   DEV  LINK MODE   UID       SIZE "SPTR" HLDD\n",
		"ADDR", "MAPPING");
}

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 %3x ", inode.i_ino, ATOMIC_READ(inode.i_count));
	if (inode.i_sb) {
		struct super_block sb;
		memread((addr_t)inode.i_sb, sizeof(struct super_block), &sb, "super_block");
		print_dev_t(sb.s_dev);
	} else {
		print_dev_t(-1);
	}
	mprintf(" %3x ", inode.i_nlink);
	pmode(inode.i_mode);
	mprintf(" %5d %10Lx", inode.i_uid, inode.i_size);
	mprintf(" " FPTR, inode.i_mapping);
	mprintf(" %c%c%c\n",
		(inode.i_hash.pprev        !=NULL)? 'H': '-',
		(inode.i_list.next         !=&((struct inode *)addr)->i_list         )? 'L': '-',
		(inode.i_dentry.next       !=&((struct inode *)addr)->i_dentry       )? 'D': '-');

	if (full) {
		mprintf("\tstate:%lx  flags:%x\n", inode.i_state, inode.i_flags);
		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("\tSEM (count:%d  sleepers:%d)\n", ATOMIC_READ(inode.i_sem.count), inode.i_sem.sleepers);
		mprintf("\top:" FPTR "  fop:" FPTR "  sb:" FPTR "  flock:" FPTR "\n", inode.i_op, inode.i_fop, inode.i_sb, inode.i_flock);
		mprintf("\tbdev:%lx  gen:%u  generic_ip:" FPTR "\n", inode.i_bdev, inode.i_generation, inode.u.generic_ip);
		mprintf("\tlist: " FPTR " " FPTR "\n",
			inode.i_list.next == 0? 0:
			(addr_t)inode.i_list.next - OFFSET(struct inode,i_list),
			inode.i_list.prev == 0? 0:
			(addr_t)inode.i_list.prev - OFFSET(struct inode,i_list));
		mprintf("\n");
	}
	return (addr_t)inode.i_hash.next;
}

void
prhead_superblock()
{
	mprintf(SPTR"   DEV   BSIZE        TYPE "SPTR"    MAGIC "SPTR" "SPTR" FLAGS\n",
		"ADDR", "S_OP", "ROOT", "BDEV");
}

addr_t
print_superblock(addr, full)
	addr_t addr;
	int full;
{
	struct super_block sb;
	struct file_system_type fstype;
	char buf[16];

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

	print_dev_t(sb.s_dev);
	mprintf(" %5lx", sb.s_blocksize);

	if (sb.s_type) {
		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(" %11s", buf);
	} else {
		mprintf(" %11s", "-");
	}

	mprintf(" " FPTR, sb.s_op);
	mprintf(" %8lx", sb.s_magic);
	mprintf(" " FPTR " " FPTR, sb.s_root, sb.s_bdev);
#if 0
	if (sb.s_dirty.next == &((struct super_block *)addr)->s_dirty) {
		mprintf(SPTR " ", "-");
	} else {
		mprintf(" " FPTR, sb.s_dirty.next);
	}
	if (sb.s_dirty.prev == &((struct super_block *)addr)->s_dirty) {
		mprintf(SPTR " ", "-");
	} else {
		mprintf(" " FPTR, sb.s_dirty.prev);
	}
#endif

	mprintbit(sflags, sb.s_flags);
	mprintf("\n");

	if (full) {
		mprintf("\ts_blocksize_bits: %x\n", sb.s_blocksize_bits);
		mprintf("\tdirt: %x\n", sb.s_dirt);
		mprintf("\tdquot_operations: " FPTR "\n", sb.dq_op);
	}

	return (addr_t)sb.s_list.next;
}

addr_t
print_superblock_union(addr, fstype, func)
	addr_t addr;
	addr_t fstype;
	addr_t (*func)();
{
	struct super_block sb;

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

	if ((addr_t)sb.s_type == fstype)
		func(sb.s_fs_info);
	return (addr_t)sb.s_list.next;
}

void
prhead_vfsmount()
{
	mprintf(SPTR" "SPTR" "SPTR" "SPTR" "SPTR"  CNT  F  DEVICE\n",
		"ADDR", "MOUNT-D", "ROOT-D", "PARENT", "SB");
}

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

	if (chain) {
		addr -= OFFSET(struct vfsmount, mnt_hash);
	}

	memread(addr, sizeof(struct vfsmount), &m, "vfsmount");
	mprintf(FPTR " ", addr);
	mprintf(FPTR " " FPTR " " FPTR " " FPTR " ",
		m.mnt_mountpoint, m.mnt_root, m.mnt_parent, m.mnt_sb);
	mprintf("%4x %2x  ", ATOMIC_READ(m.mnt_count), m.mnt_flags);

	if (m.mnt_devname) {
		memread((addr_t)m.mnt_devname, sizeof(path), path, "mnt_devname");
		mprintf("%s", path);
	} else {
		mprintf("-");
	}
	mprintf("\n");

	if (chain) {
		return (addr_t)m.mnt_hash.next;
	}
	return (addr_t)m.mnt_list.next - OFFSET(struct vfsmount, mnt_list);
}

void
print_files_stat(addr)
	addr_t addr;
{
	struct files_stat_struct filst;

	memread(addr, sizeof(filst), &filst, "files_stat");
	mprintf("nr_files:      %5x\n", filst.nr_files);
	mprintf("nr_free_files: %5x\n", filst.nr_free_files);
	mprintf("max_files:     %5x\n", filst.max_files);
}

void
print_address_space(addr, plist)
	addr_t addr;
	int plist;
{
	struct address_space as;
	extern void print_gfp();
	addr_t paddr, top;
	addr_t print_page();
	void prhead_page();

	mprintf("ADDR:         " FPTR "\n", addr);
	memread(addr, sizeof(as), &as, "address_space");

	mprintf("clean_pages:  " FPTR "  " FPTR "\n",
		as.clean_pages.next, as.clean_pages.prev);
	mprintf("dirty_pages:  " FPTR "  " FPTR "\n",
		as.dirty_pages.next, as.dirty_pages.prev);
	mprintf("locked_pages: " FPTR "  " FPTR "\n",
		as.locked_pages.next, as.locked_pages.prev);

	mprintf("nrpages:      %8lx\n", as.nrpages);
	mprintf("ops:          " FPTR "\n", as.a_ops);
	mprintf("host:         " FPTR "\n", as.host);
	mprintf("mmap:         " FPTR "\n", as.i_mmap.next);		/*XXX*/
	mprintf("mmap_shared:  " FPTR "\n", as.i_mmap_shared.next);	/*XXX*/
	mprintf("gfp_mask:    ");
	print_gfp(as.gfp_mask);

	if (plist & 1) {
		prhead_page();
		top = addr + OFFSET(struct address_space, clean_pages) - OFFSET(struct page, list);
		paddr = (addr_t)as.clean_pages.next - OFFSET(struct page, list);
		while (paddr && paddr != top) {
			paddr = print_page(paddr, 1);
		}
	}
	if (plist & 2) {
		prhead_page();
		top = addr + OFFSET(struct address_space, dirty_pages) - OFFSET(struct page, list);
		paddr = (addr_t)as.dirty_pages.next - OFFSET(struct page, list);
		while (paddr && paddr != top) {
			paddr = print_page(paddr, 1);
		}
	}
	if (plist & 4) {
		prhead_page();
		top = addr + OFFSET(struct address_space, locked_pages) - OFFSET(struct page, list);
		paddr = (addr_t)as.locked_pages.next - OFFSET(struct page, list);
		while (paddr && paddr != top) {
			paddr = print_page(paddr, 1);
		}
	}
}

void
prhead_block_device()
{
	mprintf(SPTR" COUNT   DEV   OPEN BLKSZ "SPTR" "SPTR" SEM C SLP "SPTR"  INODES..\n", "ADDR", "HOLDER", "DISK", "INODE");
}

addr_t
print_block_device(addr, eflag)
	addr_t addr;
	int eflag;
{
	struct block_device bd;
	addr_t iaddr;
	struct inode inode;

	memread(addr, sizeof(bd), &bd, "block_device");
	if (eflag == 0 && bd.bd_openers == 0)
		return (addr_t)bd.bd_hash.next;

	mprintf(FPTR " ", addr);
	mprintf("%5x ", ATOMIC_READ(bd.bd_count));
	print_dev_t(bd.bd_dev);
	mprintf(" %4x %5x", bd.bd_openers, bd.bd_block_size);
	mprintf(" " FPTR " " FPTR, bd.bd_holder, bd.bd_disk);
	mprintf(" %5d %3d", ATOMIC_READ(bd.bd_sem.count), bd.bd_sem.sleepers);

	mprintf(" " FPTR " ", bd.bd_inode);
	iaddr = (addr_t)bd.bd_inodes.next;
	while (iaddr && iaddr != addr + OFFSET(struct block_device, bd_inodes)) {
		iaddr -= OFFSET(struct inode, i_devices);
		mprintf(" " FPTR, iaddr);
		memread(iaddr, sizeof(inode), &inode, "inode");
		iaddr = (addr_t)inode.i_devices.next;
	}
	mprintf("\n");
	return (addr_t)bd.bd_hash.next;
}
