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

#include <unistd.h>
#include "crash.h"

#include "flags_fs.h"

PRIVATE addr_t operations();
const commandtable_t command_operations =
	{"operations", operations, "{-a|-b|-d|-f|-n|-i|-s|-q|-v} address", "print address_space, block_device, dentry, file, inode, nfs_rpc, super, dquot or vm operations"};

PRIVATE const char *address_space_op[] = {
	"ADDRESS SPACE OPERATIONS",
	"writepage", "readpage", "sync_page", "prepare_write", "commit_write",
	"bmap",
#if SUBLEVEL>=15
	"flushpage", "releasepage", "direct_IO",
#endif
#ifdef _address_space_operations_has_removepage
	"removepage",
#endif
	NULL
};

PRIVATE const char *block_device_op[] = {
	"BLOCK DEVICE OPERATIONS",
	"open", "release", "ioctl", "check_media_change", "revalidate",
#ifdef _block_device_operations_has_owner
	"owner",
#endif
	NULL
};

PRIVATE const char *dentry_op[] = {
	"DENTRY OPERATIONS",
	"revalidate", "hash", "compare", "delete", "release", "iput",
	NULL
};

PRIVATE const char *file_op[] = {
	"FILE OPERATIONS",
	"owner",
	"llseek", "read", "write", "readdir", "poll", "ioctl", "mmap",
	"open", "flush", "release", "fsync", "fasync", "lock",
	"readv", "writev",
#if SUBLEVEL>=15
	"sendpage", "get_unmapped_area",
#endif
	NULL
};

PRIVATE const char *inode_op[] = {
	"INODE OPERATIONS",
	"create", "lookup", "link", "unlink", "symlink",
	"mkdir", "rmdir", "mknod", "rename", "readlink", "follow_link",
	"truncate", "permission", "revalidate", "setattr", "getattr",
#ifdef _inode_operations_has_setxattr
	"setxattr",
#endif
#ifdef _inode_operations_has_getxattr
	"getxattr",
#endif
#ifdef _inode_operations_has_listxattr
	"listxattr",
#endif
#ifdef _inode_operations_has_removexattr
	"removexattr",
#endif
	NULL
};

PRIVATE const char *nfs_rpc_op[] = {
	"NFS_RPC OPERATIONS",
	"version", "getroot", "getattr", "setattr", "lookup", "access",
	"readlink", "read", "write", "commit", "create", "remove",
	"unlink_setup", "unlink_done", "rename", "link", "symlink",
	"mkdir", "rmdir", "readdir", "mknod", "statfs", "decode_dirent",
	NULL
};

PRIVATE const char *super_op[] = {
	"SUPER OPERATIONS",
#ifdef _super_operations_has_alloc_inode
	"alloc_inode",
#endif
#ifdef _super_operations_has_destroy_inode
	"destroy_inode",
#endif
	"read_inode", "read_inode2", "dirty_inode", "write_inode", "put_inode",
	"delete_inode", "put_super", "write_super",
#ifdef _super_operations_has_sync_fs
	"sync_fs",
#endif
	"write_super_lockfs", "unlockfs", "statfs", "remount_fs",
	"clear_inode", "umount_begin",
#if SUBLEVEL>=15
	"fh_to_dentry", "dentry_to_fh",
#endif
#ifdef _super_operations_has_show_options
	"show_options",
#endif
	NULL
};

PRIVATE const char *dquot_op[] = {
	"DQUOT OPERATIONS",
	"initialize", "drop", "alloc_block", "alloc_inode", "free_block",
	"free_inode", "transfer",
	NULL
};

PRIVATE const char *vm_op[] = {
	"VM OPERATIONS STRUCT",
	"open", "close", "nopage",
	NULL
};

const char head_operations[] = "%24s:   ADDRESS  SYMBOL\n";

void
print_operations(addr, op)
	addr_t addr;
	const char **op;
{
	addr_t buf[32];
	const char *p;
	int i;

	memread(addr, sizeof(buf), buf, "operations");
	mprintf(head_operations, op[0]);
	for (i = 1; op[i]; i++) {
		p = getsymstr_func(buf[i - 1]);
		mprintf("%24s:  " FPTR "  %s\n", op[i], buf[i - 1], p? p: "-");
	}
}

PRIVATE addr_t
operations()
{
	int i, c;
	const char **op = NULL;
	addr_t addr;

	while ((c = getopt(argcnt, args, "abdfinqsv")) != EOF) {
		switch (c) {
		case 'a':
			op = address_space_op;
			break;
		case 'b':
			op = block_device_op;
			break;
		case 'f':
			op = file_op;
			break;
		case 'i':
			op = inode_op;
			break;
		case 'n':
			op = nfs_rpc_op;
			break;
		case 's':
			op = super_op;
			break;
		case 'q':
			op = dquot_op;
			break;
		case 'd':
			op = dentry_op;
			break;
		case 'v':
			op = vm_op;
			break;
		default:
			THROW(usage);
		}
	}
	if (op == NULL || optind == argcnt) {
		THROW(usage);
	}

	for (i = optind; i < argcnt; i++) {
		addr = getvalue(args[i]);
		print_operations(addr, op);
	}
	return 0;
}
