#include <sys/mdb_modapi.h>
#include <nfs/export.h>
#include <sys/pkp_hash.h>
#include <limits.h>
#include "nfssrv.h"
#include "common.h"
static const mdb_bitmask_t sec_flag_bits[] = {
{"M_RO", M_RO, M_RO},
{"M_ROL", M_ROL, M_ROL},
{"M_RW", M_RW, M_RW},
{"M_RWL", M_RWL, M_RWL},
{"M_ROOT", M_ROOT, M_ROOT},
{"M_EXP", M_4SEC_EXPORTED, M_4SEC_EXPORTED},
{NULL, 0, 0}
};
static int
print_sec(int cnt, uintptr_t addr)
{
int i;
if (cnt == 0)
return (DCMD_OK);
mdb_printf("Security Flavors :\n");
mdb_inc_indent(4);
for (i = 0; i < cnt; i++) {
struct secinfo si;
const char *s;
if (mdb_vread(&si, sizeof (si), addr) == -1) {
mdb_warn("can't read struct secinfo");
return (DCMD_ERR);
}
switch (si.s_secinfo.sc_nfsnum) {
case AUTH_NONE:
s = "none";
break;
case AUTH_SYS:
s = "sys";
break;
case AUTH_DH:
s = "dh";
break;
case 390003:
s = "krb5";
break;
case 390004:
s = "krb5i";
break;
case 390005:
s = "krb5p";
break;
default:
s = "???";
break;
}
mdb_printf("%-8s ref: %-8i flag: %#x (%b)\n", s, si.s_refcnt,
si.s_flags, si.s_flags, sec_flag_bits);
addr = (uintptr_t)((struct secinfo *)addr + 1);
}
mdb_dec_indent(4);
return (DCMD_OK);
}
int
nfs_expvis_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
exp_visible_t expvis;
uintptr_t vp;
char *s;
int status;
if (argc > 0)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) == 0) {
mdb_printf("requires address of exp_visible_t\n");
return (DCMD_USAGE);
}
if (mdb_vread(&expvis, sizeof (expvis), addr) == -1) {
mdb_warn("can't read exp_visible_t");
return (DCMD_ERR);
}
if (mdb_vread(&vp, sizeof (vp), (uintptr_t)expvis.vis_vp
+ OFFSETOF(vnode_t, v_path)) == -1) {
mdb_warn("can't read vnode_t");
return (DCMD_ERR);
}
s = mdb_alloc(PATH_MAX, UM_SLEEP | UM_GC);
if (mdb_readstr(s, PATH_MAX, vp) == -1) {
mdb_warn("can't read v_path");
return (DCMD_ERR);
}
mdb_printf("%s\n", s);
mdb_inc_indent(4);
mdb_printf("addr: %?p exp : %i ref: %i\n", addr,
expvis.vis_exported, expvis.vis_count);
mdb_printf("vp : %?p ino : %llu (%#llx)\n", expvis.vis_vp,
expvis.vis_ino, expvis.vis_ino);
mdb_printf("seci: %?p nsec: %i\n", expvis.vis_secinfo,
expvis.vis_seccnt);
status = print_sec(expvis.vis_seccnt, (uintptr_t)expvis.vis_secinfo);
mdb_dec_indent(4);
return (status);
}
static const mdb_bitmask_t exp_flag_bits[] = {
{"EX_NOSUID", EX_NOSUID, EX_NOSUID},
{"EX_ACLOK", EX_ACLOK, EX_ACLOK},
{"EX_PUBLIC", EX_PUBLIC, EX_PUBLIC},
{"EX_NOSUB", EX_NOSUB, EX_NOSUB},
{"EX_INDEX", EX_INDEX, EX_INDEX},
{"EX_LOG", EX_LOG, EX_LOG},
{"EX_LOG_ALLOPS", EX_LOG_ALLOPS, EX_LOG_ALLOPS},
{"EX_PSEUDO", EX_PSEUDO, EX_PSEUDO},
{NULL, 0, 0}
};
int
nfs_expinfo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
struct exportinfo exi;
char *path;
int status;
uint_t v_flag;
if (argc > 0)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) == 0) {
mdb_printf("requires address of struct exporinfo\n");
return (DCMD_USAGE);
}
if (mdb_vread(&exi, sizeof (exi), addr) == -1) {
mdb_warn("can't read struct exportinfo");
return (DCMD_ERR);
}
if (mdb_vread(&v_flag, sizeof (v_flag), (uintptr_t)exi.exi_vp
+ OFFSETOF(vnode_t, v_flag)) == -1) {
mdb_warn("can't read v_flag");
return (DCMD_ERR);
}
path = mdb_alloc(exi.exi_export.ex_pathlen + 1, UM_SLEEP | UM_GC);
if (mdb_readstr(path, exi.exi_export.ex_pathlen + 1,
(uintptr_t)exi.exi_export.ex_path) == -1) {
mdb_warn("can't read ex_path");
return (DCMD_ERR);
}
mdb_printf("\n%s %p\n", path, addr);
mdb_inc_indent(4);
mdb_printf("rtvp: %?p ref : %-8u flag: %#x (%b)%s\n", exi.exi_vp,
exi.exi_count, exi.exi_export.ex_flags, exi.exi_export.ex_flags,
exp_flag_bits, v_flag & VROOT ? " VROOT" : "");
mdb_printf("dvp : %?p anon: %-8u logb: %p\n", exi.exi_dvp,
exi.exi_export.ex_anon, exi.exi_logbuffer);
mdb_printf("seci: %?p nsec: %-8i fsid: (%#x %#x)\n",
exi.exi_export.ex_secinfo, exi.exi_export.ex_seccnt,
exi.exi_fsid.val[0], exi.exi_fsid.val[1]);
status = print_sec(exi.exi_export.ex_seccnt,
(uintptr_t)exi.exi_export.ex_secinfo);
if (status != DCMD_OK)
return (status);
if (exi.exi_visible) {
mdb_printf("PseudoFS Nodes:\n");
mdb_inc_indent(4);
if (mdb_pwalk_dcmd("nfs_expvis", "nfs_expvis", 0, NULL,
(uintptr_t)exi.exi_visible) == -1) {
mdb_warn("walk through exi_visible failed");
return (DCMD_ERR);
}
mdb_dec_indent(4);
}
mdb_dec_indent(4);
return (DCMD_OK);
}
int
nfs_exptable_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintptr_t glbls;
uintptr_t zonep;
nfs_globals_t nfsglbls;
if (argc > 0)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) != 0) {
zonep = addr;
} else {
if (mdb_readsym(
&zonep, sizeof (uintptr_t), "global_zone") == -1) {
mdb_warn("Failed to find global_zone");
return (DCMD_ERR);
}
}
mdb_printf("The zone is %p\n", zonep);
if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) {
mdb_warn("failed to get zoned specific NFS globals");
return (DCMD_ERR);
}
mdb_printf("The nfs zone globals are %p\n", glbls);
if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) {
mdb_warn("can't read zone globals");
return (DCMD_ERR);
}
mdb_printf("The nfs globals are %p\n", nfsglbls);
mdb_printf("The address of nfsglbls.nfs_export is %p\n",
nfsglbls.nfs_export);
mdb_printf("The exptable address is %p\n",
nfsglbls.nfs_export->exptable);
if (mdb_pwalk_dcmd("nfs_expinfo", "nfs_expinfo", 0, NULL,
(uintptr_t)nfsglbls.nfs_export->exptable) == -1) {
mdb_warn("exptable walk failed");
return (DCMD_ERR);
}
return (DCMD_OK);
}
int
nfs_exptable_path_dcmd(uintptr_t addr, uint_t flags, int argc,
const mdb_arg_t *argv)
{
uintptr_t glbls;
uintptr_t zonep;
nfs_globals_t nfsglbls;
if (argc > 0)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) != 0) {
zonep = addr;
} else {
if (mdb_readsym(&zonep, sizeof (uintptr_t),
"global_zone") == -1) {
mdb_warn("Failed to find global_zone");
return (DCMD_ERR);
}
}
if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) {
mdb_warn("failed to get zoned specific NFS globals");
return (DCMD_ERR);
}
if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) {
mdb_warn("can't read zone globals");
return (DCMD_ERR);
}
if (mdb_pwalk_dcmd("nfs_expinfo_path", "nfs_expinfo", 0, NULL,
(uintptr_t)nfsglbls.nfs_export->exptable) == -1) {
mdb_warn("exptable walk failed");
return (DCMD_ERR);
}
return (DCMD_OK);
}
static int
print_tree(uintptr_t addr, uint_t opt_v, treenode_t *tn, char *s)
{
while (addr != 0) {
uintptr_t a;
if (mdb_vread(tn, sizeof (*tn), addr) == -1) {
mdb_warn("can't read treenode");
return (DCMD_ERR);
}
if (mdb_vread(&a, sizeof (a), (uintptr_t)tn->tree_exi
+ OFFSETOF(struct exportinfo, exi_vp)) == -1) {
mdb_warn("can't read exi_vp");
return (DCMD_ERR);
}
if (mdb_vread(&a, sizeof (a),
a + OFFSETOF(vnode_t, v_path)) == -1) {
mdb_warn("can't read v_path");
return (DCMD_ERR);
}
if (mdb_readstr(s, PATH_MAX, a) == -1) {
mdb_warn("can't read v_path string");
return (DCMD_ERR);
}
mdb_printf("\n\nTREENODE:\n%s\n", s);
mdb_inc_indent(2);
if (opt_v)
mdb_printf("\nDump treenode:\n\n");
mdb_printf("addr: %p\n", addr);
mdb_printf("tree_parent: %p\n", tn->tree_parent);
mdb_printf("tree_child_first: %p\n", tn->tree_child_first);
mdb_printf("tree_sibling: %p\n", tn->tree_sibling);
mdb_printf("tree_exi: %p\n", tn->tree_exi);
mdb_printf("tree_vis: %p\n", tn->tree_vis);
if (opt_v) {
mdb_printf("\nDump exportinfo:\n");
if (mdb_call_dcmd("nfs_expinfo",
(uintptr_t)tn->tree_exi, DCMD_ADDRSPEC, 0, NULL)
== -1)
return (DCMD_ERR);
if (tn->tree_vis) {
mdb_printf("\nDump exp_visible:\n\n");
if (mdb_call_dcmd("nfs_expvis",
(uintptr_t)tn->tree_vis, DCMD_ADDRSPEC, 0,
NULL) == -1)
return (DCMD_ERR);
}
}
addr = (uintptr_t)tn->tree_sibling;
if (tn->tree_child_first != NULL) {
int status;
mdb_inc_indent(2);
status = print_tree((uintptr_t)tn->tree_child_first,
opt_v, tn, s);
if (status != DCMD_OK)
return (status);
mdb_dec_indent(2);
}
mdb_dec_indent(2);
}
return (DCMD_OK);
}
int
nfs_nstree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uintptr_t glbls;
uintptr_t zonep;
nfs_globals_t nfsglbls;
nfs_export_t exp;
uint_t opt_v = FALSE;
treenode_t tn;
char *s;
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) != 0) {
zonep = addr;
} else {
if (mdb_readsym(&zonep, sizeof (uintptr_t),
"global_zone") == -1) {
mdb_warn("Failed to find global_zone");
return (DCMD_ERR);
}
}
if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) {
mdb_warn("failed to get zoned specific NFS globals");
return (DCMD_ERR);
}
if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) {
mdb_warn("can't read zone globals");
return (DCMD_ERR);
}
if (mdb_vread(&exp, sizeof (nfs_export_t),
(uintptr_t)nfsglbls.nfs_export) == -1) {
mdb_warn("can't read nfs_export");
return (DCMD_ERR);
}
s = mdb_alloc(PATH_MAX, UM_SLEEP | UM_GC);
return (print_tree((uintptr_t)exp.ns_root, opt_v, &tn, s));
}
void
nfs_nstree_help(void)
{
mdb_printf(
"-v dump also exportinfo and exp_visible structures\n");
}
static int
calc_hashdist(struct exp_walk_arg *arg, uint_t opt_v, uintptr_t tbladdr)
{
struct exportinfo **table;
int i;
u_longlong_t min = 0;
u_longlong_t max = 0;
u_longlong_t sum = 0;
u_longlong_t sum_sqr = 0;
table = mdb_alloc(arg->size * sizeof (struct exportinfo *),
UM_SLEEP | UM_GC);
if (mdb_vread(table, arg->size * sizeof (struct exportinfo *),
tbladdr) == -1) {
mdb_warn("can't vreadsym exptable");
return (DCMD_ERR);
}
for (i = 0; i < arg->size; i++) {
u_longlong_t len;
uintptr_t addr;
for (addr = (uintptr_t)table[i], len = 0; addr; len++)
if (mdb_vread(&addr, sizeof (addr), addr + arg->offset)
== -1) {
mdb_warn("unable to read pointer to next "
"exportinfo struct");
return (DCMD_ERR);
}
if (i == 0 || len < min)
min = len;
if (len > max)
max = len;
sum += len;
sum_sqr += len * len;
if (opt_v)
mdb_printf("%u\n", len);
}
mdb_printf("TABLE: %s\n", arg->name);
mdb_printf("items/size = %u/%u\n", sum, arg->size);
mdb_printf("min/avg/max/variance = %u/%u/%u/%u\n", min, sum / arg->size,
max, (sum_sqr - (sum * sum) / arg->size) / arg->size);
return (DCMD_OK);
}
int
nfs_fid_hashdist_dcmd(uintptr_t addr, uint_t flags, int argc,
const mdb_arg_t *argv)
{
uint_t opt_v = FALSE;
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) == 0) {
mdb_printf("requires address of export table\n");
return (DCMD_USAGE);
}
return (calc_hashdist(&nfs_expinfo_arg, opt_v, addr));
}
void
nfs_hashdist_help(void)
{
mdb_printf(
"-v displays individual bucket lengths\n");
}
int
nfs_path_hashdist_dcmd(uintptr_t addr, uint_t flags, int argc,
const mdb_arg_t *argv)
{
uint_t opt_v = FALSE;
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) == 0) {
mdb_printf("requires address of export table\n");
return (DCMD_USAGE);
}
return (calc_hashdist(&nfs_expinfo_path_arg, opt_v, addr));
}
struct exp_walk_arg nfs_expinfo_arg = {
"exptable", EXPTABLESIZE,
OFFSETOF(struct exportinfo, fid_hash) + OFFSETOF(struct exp_hash, next)
};
struct exp_walk_arg nfs_expinfo_path_arg = {
"exptable_path_hash", PKP_HASH_SIZE,
OFFSETOF(struct exportinfo, path_hash) + OFFSETOF(struct exp_hash, next)
};
int
nfs_expinfo_walk_init(mdb_walk_state_t *wsp)
{
struct exp_walk_arg *exp_arg = wsp->walk_arg;
hash_table_walk_arg_t *arg;
int status;
if (wsp->walk_addr == 0) {
mdb_warn("global walk not supported");
return (WALK_ERR);
}
arg = mdb_alloc(sizeof (hash_table_walk_arg_t), UM_SLEEP);
arg->array_addr = wsp->walk_addr;
arg->array_len = exp_arg->size;
arg->head_size = sizeof (struct exportinfo *);
arg->first_name = "exportinfo pointer";
arg->first_offset = 0;
arg->member_type_name = "struct exportinfo";
arg->member_size = sizeof (struct exportinfo);
arg->next_offset = exp_arg->offset;
wsp->walk_arg = arg;
status = hash_table_walk_init(wsp);
if (status != WALK_NEXT)
mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
return (status);
}
void
nfs_expinfo_walk_fini(mdb_walk_state_t *wsp)
{
hash_table_walk_fini(wsp);
mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t));
}
int
nfs_expvis_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0) {
mdb_warn("global walk not supported");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
nfs_expvis_walk_step(mdb_walk_state_t *wsp)
{
exp_visible_t vis;
uintptr_t addr = wsp->walk_addr;
if (addr == 0)
return (WALK_DONE);
if (mdb_vread(&vis, sizeof (vis), addr) == -1) {
mdb_warn("failed to read exp_visible_t at %p", addr);
return (WALK_ERR);
}
wsp->walk_addr = (uintptr_t)vis.vis_next;
return (wsp->walk_callback(addr, &vis, wsp->walk_cbdata));
}
int
nfssrv_globals_walk_init(mdb_walk_state_t *wsp)
{
GElf_Sym sym;
if (wsp->walk_addr == 0) {
if (mdb_lookup_by_name("nfssrv_globals_list", &sym) == -1) {
mdb_warn("failed to find 'nfssrv_globals_list'");
return (WALK_ERR);
}
wsp->walk_addr = (uintptr_t)sym.st_value;
} else {
mdb_printf("nfssrv_globals walk only supports global walks\n");
return (WALK_ERR);
}
if (mdb_layered_walk("list", wsp) == -1) {
mdb_warn("couldn't walk 'list'");
return (WALK_ERR);
}
wsp->walk_data = (void *)wsp->walk_addr;
return (WALK_NEXT);
}
int
nfssrv_globals_walk_step(mdb_walk_state_t *wsp)
{
return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
wsp->walk_cbdata));
}