1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 /* 12 * Copyright 2021 Tintri by DDN, Inc. All rights reserved. 13 */ 14 15 #include <sys/mdb_modapi.h> 16 #include <nfs/export.h> 17 #include <sys/pkp_hash.h> 18 #include <limits.h> 19 20 #include "nfssrv.h" 21 #include "common.h" 22 23 /* 24 * nfs_expvis dcmd implementation 25 */ 26 27 static const mdb_bitmask_t sec_flag_bits[] = { 28 {"M_RO", M_RO, M_RO}, 29 {"M_ROL", M_ROL, M_ROL}, 30 {"M_RW", M_RW, M_RW}, 31 {"M_RWL", M_RWL, M_RWL}, 32 {"M_ROOT", M_ROOT, M_ROOT}, 33 {"M_EXP", M_4SEC_EXPORTED, M_4SEC_EXPORTED}, 34 {NULL, 0, 0} 35 }; 36 37 static int 38 print_sec(int cnt, uintptr_t addr) 39 { 40 int i; 41 42 if (cnt == 0) 43 return (DCMD_OK); 44 45 mdb_printf("Security Flavors :\n"); 46 mdb_inc_indent(4); 47 48 for (i = 0; i < cnt; i++) { 49 struct secinfo si; 50 const char *s; 51 52 if (mdb_vread(&si, sizeof (si), addr) == -1) { 53 mdb_warn("can't read struct secinfo"); 54 return (DCMD_ERR); 55 } 56 57 switch (si.s_secinfo.sc_nfsnum) { 58 case AUTH_NONE: 59 s = "none"; 60 break; 61 case AUTH_SYS: 62 s = "sys"; 63 break; 64 case AUTH_DH: 65 s = "dh"; 66 break; 67 case 390003: 68 s = "krb5"; 69 break; 70 case 390004: 71 s = "krb5i"; 72 break; 73 case 390005: 74 s = "krb5p"; 75 break; 76 default: 77 s = "???"; 78 break; 79 } 80 81 mdb_printf("%-8s ref: %-8i flag: %#x (%b)\n", s, si.s_refcnt, 82 si.s_flags, si.s_flags, sec_flag_bits); 83 84 addr = (uintptr_t)((struct secinfo *)addr + 1); 85 } 86 87 mdb_dec_indent(4); 88 89 return (DCMD_OK); 90 } 91 92 int 93 nfs_expvis_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 94 { 95 exp_visible_t expvis; 96 uintptr_t vp; 97 char *s; 98 int status; 99 100 if (argc > 0) 101 return (DCMD_USAGE); 102 103 if ((flags & DCMD_ADDRSPEC) == 0) { 104 mdb_printf("requires address of exp_visible_t\n"); 105 return (DCMD_USAGE); 106 } 107 108 if (mdb_vread(&expvis, sizeof (expvis), addr) == -1) { 109 mdb_warn("can't read exp_visible_t"); 110 return (DCMD_ERR); 111 } 112 113 /* vp = expvis.vis_vp->v_path */ 114 if (mdb_vread(&vp, sizeof (vp), (uintptr_t)expvis.vis_vp 115 + OFFSETOF(vnode_t, v_path)) == -1) { 116 mdb_warn("can't read vnode_t"); 117 return (DCMD_ERR); 118 } 119 120 s = mdb_alloc(PATH_MAX, UM_SLEEP | UM_GC); 121 if (mdb_readstr(s, PATH_MAX, vp) == -1) { 122 mdb_warn("can't read v_path"); 123 return (DCMD_ERR); 124 } 125 126 mdb_printf("%s\n", s); 127 128 mdb_inc_indent(4); 129 130 mdb_printf("addr: %?p exp : %i ref: %i\n", addr, 131 expvis.vis_exported, expvis.vis_count); 132 mdb_printf("vp : %?p ino : %llu (%#llx)\n", expvis.vis_vp, 133 expvis.vis_ino, expvis.vis_ino); 134 mdb_printf("seci: %?p nsec: %i\n", expvis.vis_secinfo, 135 expvis.vis_seccnt); 136 137 status = print_sec(expvis.vis_seccnt, (uintptr_t)expvis.vis_secinfo); 138 139 mdb_dec_indent(4); 140 141 return (status); 142 } 143 144 145 /* 146 * nfs_expinfo dcmd implementation 147 */ 148 149 static const mdb_bitmask_t exp_flag_bits[] = { 150 {"EX_NOSUID", EX_NOSUID, EX_NOSUID}, 151 {"EX_ACLOK", EX_ACLOK, EX_ACLOK}, 152 {"EX_PUBLIC", EX_PUBLIC, EX_PUBLIC}, 153 {"EX_NOSUB", EX_NOSUB, EX_NOSUB}, 154 {"EX_INDEX", EX_INDEX, EX_INDEX}, 155 {"EX_LOG", EX_LOG, EX_LOG}, 156 {"EX_LOG_ALLOPS", EX_LOG_ALLOPS, EX_LOG_ALLOPS}, 157 {"EX_PSEUDO", EX_PSEUDO, EX_PSEUDO}, 158 {NULL, 0, 0} 159 }; 160 161 int 162 nfs_expinfo_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 163 { 164 struct exportinfo exi; 165 char *path; 166 int status; 167 uint_t v_flag; 168 169 if (argc > 0) 170 return (DCMD_USAGE); 171 172 if ((flags & DCMD_ADDRSPEC) == 0) { 173 mdb_printf("requires address of struct exporinfo\n"); 174 return (DCMD_USAGE); 175 } 176 177 if (mdb_vread(&exi, sizeof (exi), addr) == -1) { 178 mdb_warn("can't read struct exportinfo"); 179 return (DCMD_ERR); 180 } 181 182 if (mdb_vread(&v_flag, sizeof (v_flag), (uintptr_t)exi.exi_vp 183 + OFFSETOF(vnode_t, v_flag)) == -1) { 184 mdb_warn("can't read v_flag"); 185 return (DCMD_ERR); 186 } 187 188 path = mdb_alloc(exi.exi_export.ex_pathlen + 1, UM_SLEEP | UM_GC); 189 if (mdb_readstr(path, exi.exi_export.ex_pathlen + 1, 190 (uintptr_t)exi.exi_export.ex_path) == -1) { 191 mdb_warn("can't read ex_path"); 192 return (DCMD_ERR); 193 } 194 195 mdb_printf("\n%s %p\n", path, addr); 196 197 mdb_inc_indent(4); 198 199 mdb_printf("rtvp: %?p ref : %-8u flag: %#x (%b)%s\n", exi.exi_vp, 200 exi.exi_count, exi.exi_export.ex_flags, exi.exi_export.ex_flags, 201 exp_flag_bits, v_flag & VROOT ? " VROOT" : ""); 202 203 mdb_printf("dvp : %?p anon: %-8u logb: %p\n", exi.exi_dvp, 204 exi.exi_export.ex_anon, exi.exi_logbuffer); 205 mdb_printf("seci: %?p nsec: %-8i fsid: (%#x %#x)\n", 206 exi.exi_export.ex_secinfo, exi.exi_export.ex_seccnt, 207 exi.exi_fsid.val[0], exi.exi_fsid.val[1]); 208 209 status = print_sec(exi.exi_export.ex_seccnt, 210 (uintptr_t)exi.exi_export.ex_secinfo); 211 if (status != DCMD_OK) 212 return (status); 213 214 if (exi.exi_visible) { 215 mdb_printf("PseudoFS Nodes:\n"); 216 mdb_inc_indent(4); 217 218 if (mdb_pwalk_dcmd("nfs_expvis", "nfs_expvis", 0, NULL, 219 (uintptr_t)exi.exi_visible) == -1) { 220 mdb_warn("walk through exi_visible failed"); 221 return (DCMD_ERR); 222 } 223 224 mdb_dec_indent(4); 225 } 226 227 mdb_dec_indent(4); 228 229 return (DCMD_OK); 230 } 231 232 /* 233 * nfs_exptable dcmd implementation 234 */ 235 236 int 237 nfs_exptable_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 238 { 239 uintptr_t glbls; 240 uintptr_t zonep; 241 nfs_globals_t nfsglbls; 242 243 if (argc > 0) 244 return (DCMD_USAGE); 245 246 if ((flags & DCMD_ADDRSPEC) != 0) { 247 zonep = addr; 248 } else { 249 if (mdb_readsym( 250 &zonep, sizeof (uintptr_t), "global_zone") == -1) { 251 mdb_warn("Failed to find global_zone"); 252 return (DCMD_ERR); 253 } 254 } 255 mdb_printf("The zone is %p\n", zonep); 256 257 if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) { 258 mdb_warn("failed to get zoned specific NFS globals"); 259 return (DCMD_ERR); 260 } 261 262 mdb_printf("The nfs zone globals are %p\n", glbls); 263 264 if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) { 265 mdb_warn("can't read zone globals"); 266 return (DCMD_ERR); 267 } 268 mdb_printf("The nfs globals are %p\n", nfsglbls); 269 mdb_printf("The address of nfsglbls.nfs_export is %p\n", 270 nfsglbls.nfs_export); 271 mdb_printf("The exptable address is %p\n", 272 nfsglbls.nfs_export->exptable); 273 274 if (mdb_pwalk_dcmd("nfs_expinfo", "nfs_expinfo", 0, NULL, 275 (uintptr_t)nfsglbls.nfs_export->exptable) == -1) { 276 mdb_warn("exptable walk failed"); 277 return (DCMD_ERR); 278 } 279 280 return (DCMD_OK); 281 } 282 283 /* 284 * nfs_exptable_path dcmd implementation 285 */ 286 287 int 288 nfs_exptable_path_dcmd(uintptr_t addr, uint_t flags, int argc, 289 const mdb_arg_t *argv) 290 { 291 uintptr_t glbls; 292 uintptr_t zonep; 293 nfs_globals_t nfsglbls; 294 295 if (argc > 0) 296 return (DCMD_USAGE); 297 298 if ((flags & DCMD_ADDRSPEC) != 0) { 299 zonep = addr; 300 } else { 301 if (mdb_readsym(&zonep, sizeof (uintptr_t), 302 "global_zone") == -1) { 303 mdb_warn("Failed to find global_zone"); 304 return (DCMD_ERR); 305 } 306 } 307 308 if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) { 309 mdb_warn("failed to get zoned specific NFS globals"); 310 return (DCMD_ERR); 311 } 312 313 if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) { 314 mdb_warn("can't read zone globals"); 315 return (DCMD_ERR); 316 } 317 318 if (mdb_pwalk_dcmd("nfs_expinfo_path", "nfs_expinfo", 0, NULL, 319 (uintptr_t)nfsglbls.nfs_export->exptable) == -1) { 320 mdb_warn("exptable walk failed"); 321 return (DCMD_ERR); 322 } 323 324 return (DCMD_OK); 325 } 326 327 /* 328 * nfs_nstree dcmd implementation 329 */ 330 331 static int 332 print_tree(uintptr_t addr, uint_t opt_v, treenode_t *tn, char *s) 333 { 334 while (addr != 0) { 335 uintptr_t a; 336 337 if (mdb_vread(tn, sizeof (*tn), addr) == -1) { 338 mdb_warn("can't read treenode"); 339 return (DCMD_ERR); 340 } 341 342 /* a = tn->tree_exi->exi_vp */ 343 if (mdb_vread(&a, sizeof (a), (uintptr_t)tn->tree_exi 344 + OFFSETOF(struct exportinfo, exi_vp)) == -1) { 345 mdb_warn("can't read exi_vp"); 346 return (DCMD_ERR); 347 } 348 /* a = ((vnode_t *)a)->v_path */ 349 if (mdb_vread(&a, sizeof (a), 350 a + OFFSETOF(vnode_t, v_path)) == -1) { 351 mdb_warn("can't read v_path"); 352 return (DCMD_ERR); 353 } 354 if (mdb_readstr(s, PATH_MAX, a) == -1) { 355 mdb_warn("can't read v_path string"); 356 return (DCMD_ERR); 357 } 358 359 mdb_printf("\n\nTREENODE:\n%s\n", s); 360 361 mdb_inc_indent(2); 362 363 if (opt_v) 364 mdb_printf("\nDump treenode:\n\n"); 365 366 mdb_printf("addr: %p\n", addr); 367 mdb_printf("tree_parent: %p\n", tn->tree_parent); 368 mdb_printf("tree_child_first: %p\n", tn->tree_child_first); 369 mdb_printf("tree_sibling: %p\n", tn->tree_sibling); 370 mdb_printf("tree_exi: %p\n", tn->tree_exi); 371 mdb_printf("tree_vis: %p\n", tn->tree_vis); 372 373 if (opt_v) { 374 mdb_printf("\nDump exportinfo:\n"); 375 if (mdb_call_dcmd("nfs_expinfo", 376 (uintptr_t)tn->tree_exi, DCMD_ADDRSPEC, 0, NULL) 377 == -1) 378 return (DCMD_ERR); 379 380 if (tn->tree_vis) { 381 mdb_printf("\nDump exp_visible:\n\n"); 382 if (mdb_call_dcmd("nfs_expvis", 383 (uintptr_t)tn->tree_vis, DCMD_ADDRSPEC, 0, 384 NULL) == -1) 385 return (DCMD_ERR); 386 } 387 } 388 389 addr = (uintptr_t)tn->tree_sibling; 390 391 if (tn->tree_child_first != NULL) { 392 int status; 393 394 mdb_inc_indent(2); 395 status = print_tree((uintptr_t)tn->tree_child_first, 396 opt_v, tn, s); 397 if (status != DCMD_OK) 398 return (status); 399 mdb_dec_indent(2); 400 } 401 402 mdb_dec_indent(2); 403 } 404 405 return (DCMD_OK); 406 } 407 408 int 409 nfs_nstree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 410 { 411 uintptr_t glbls; 412 uintptr_t zonep; 413 nfs_globals_t nfsglbls; 414 nfs_export_t exp; 415 416 uint_t opt_v = FALSE; 417 treenode_t tn; 418 char *s; 419 420 if (mdb_getopts(argc, argv, 421 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 422 return (DCMD_USAGE); 423 424 if ((flags & DCMD_ADDRSPEC) != 0) { 425 zonep = addr; 426 } else { 427 if (mdb_readsym(&zonep, sizeof (uintptr_t), 428 "global_zone") == -1) { 429 mdb_warn("Failed to find global_zone"); 430 return (DCMD_ERR); 431 } 432 } 433 434 if (zoned_get_nfs_globals(zonep, &glbls) != DCMD_OK) { 435 mdb_warn("failed to get zoned specific NFS globals"); 436 return (DCMD_ERR); 437 } 438 439 if (mdb_vread(&nfsglbls, sizeof (nfs_globals_t), glbls) == -1) { 440 mdb_warn("can't read zone globals"); 441 return (DCMD_ERR); 442 } 443 444 if (mdb_vread(&exp, sizeof (nfs_export_t), 445 (uintptr_t)nfsglbls.nfs_export) == -1) { 446 mdb_warn("can't read nfs_export"); 447 return (DCMD_ERR); 448 } 449 450 s = mdb_alloc(PATH_MAX, UM_SLEEP | UM_GC); 451 452 return (print_tree((uintptr_t)exp.ns_root, opt_v, &tn, s)); 453 } 454 455 void 456 nfs_nstree_help(void) 457 { 458 mdb_printf( 459 "-v dump also exportinfo and exp_visible structures\n"); 460 } 461 462 /* 463 * nfs_fid_hashdist dcmd implementation 464 */ 465 466 static int 467 calc_hashdist(struct exp_walk_arg *arg, uint_t opt_v, uintptr_t tbladdr) 468 { 469 struct exportinfo **table; 470 int i; 471 u_longlong_t min = 0; 472 u_longlong_t max = 0; 473 u_longlong_t sum = 0; 474 u_longlong_t sum_sqr = 0; 475 476 table = mdb_alloc(arg->size * sizeof (struct exportinfo *), 477 UM_SLEEP | UM_GC); 478 if (mdb_vread(table, arg->size * sizeof (struct exportinfo *), 479 tbladdr) == -1) { 480 mdb_warn("can't vreadsym exptable"); 481 return (DCMD_ERR); 482 } 483 484 485 for (i = 0; i < arg->size; i++) { 486 u_longlong_t len; 487 uintptr_t addr; 488 489 for (addr = (uintptr_t)table[i], len = 0; addr; len++) 490 if (mdb_vread(&addr, sizeof (addr), addr + arg->offset) 491 == -1) { 492 mdb_warn("unable to read pointer to next " 493 "exportinfo struct"); 494 return (DCMD_ERR); 495 } 496 497 if (i == 0 || len < min) 498 min = len; 499 if (len > max) 500 max = len; 501 sum += len; 502 sum_sqr += len * len; 503 504 if (opt_v) 505 mdb_printf("%u\n", len); 506 } 507 508 mdb_printf("TABLE: %s\n", arg->name); 509 mdb_printf("items/size = %u/%u\n", sum, arg->size); 510 mdb_printf("min/avg/max/variance = %u/%u/%u/%u\n", min, sum / arg->size, 511 max, (sum_sqr - (sum * sum) / arg->size) / arg->size); 512 513 return (DCMD_OK); 514 } 515 516 int 517 nfs_fid_hashdist_dcmd(uintptr_t addr, uint_t flags, int argc, 518 const mdb_arg_t *argv) 519 { 520 uint_t opt_v = FALSE; 521 522 if (mdb_getopts(argc, argv, 523 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 524 return (DCMD_USAGE); 525 526 if ((flags & DCMD_ADDRSPEC) == 0) { 527 mdb_printf("requires address of export table\n"); 528 return (DCMD_USAGE); 529 } 530 531 return (calc_hashdist(&nfs_expinfo_arg, opt_v, addr)); 532 } 533 534 void 535 nfs_hashdist_help(void) 536 { 537 mdb_printf( 538 "-v displays individual bucket lengths\n"); 539 } 540 541 /* 542 * nfs_path_hashdist dcmd implementation 543 */ 544 545 int 546 nfs_path_hashdist_dcmd(uintptr_t addr, uint_t flags, int argc, 547 const mdb_arg_t *argv) 548 { 549 uint_t opt_v = FALSE; 550 551 if (mdb_getopts(argc, argv, 552 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 553 return (DCMD_USAGE); 554 555 if ((flags & DCMD_ADDRSPEC) == 0) { 556 mdb_printf("requires address of export table\n"); 557 return (DCMD_USAGE); 558 } 559 560 return (calc_hashdist(&nfs_expinfo_path_arg, opt_v, addr)); 561 } 562 563 /* 564 * nfs_expinfo/nfs_expinfo_path walkers implementation 565 */ 566 567 struct exp_walk_arg nfs_expinfo_arg = { 568 "exptable", EXPTABLESIZE, 569 OFFSETOF(struct exportinfo, fid_hash) + OFFSETOF(struct exp_hash, next) 570 }; 571 572 struct exp_walk_arg nfs_expinfo_path_arg = { 573 "exptable_path_hash", PKP_HASH_SIZE, 574 OFFSETOF(struct exportinfo, path_hash) + OFFSETOF(struct exp_hash, next) 575 }; 576 577 int 578 nfs_expinfo_walk_init(mdb_walk_state_t *wsp) 579 { 580 struct exp_walk_arg *exp_arg = wsp->walk_arg; 581 hash_table_walk_arg_t *arg; 582 int status; 583 584 if (wsp->walk_addr == 0) { 585 mdb_warn("global walk not supported"); 586 return (WALK_ERR); 587 } 588 589 arg = mdb_alloc(sizeof (hash_table_walk_arg_t), UM_SLEEP); 590 arg->array_addr = wsp->walk_addr; 591 arg->array_len = exp_arg->size; 592 arg->head_size = sizeof (struct exportinfo *); 593 arg->first_name = "exportinfo pointer"; 594 arg->first_offset = 0; 595 arg->member_type_name = "struct exportinfo"; 596 arg->member_size = sizeof (struct exportinfo); 597 arg->next_offset = exp_arg->offset; 598 599 wsp->walk_arg = arg; 600 601 status = hash_table_walk_init(wsp); 602 if (status != WALK_NEXT) 603 mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t)); 604 return (status); 605 } 606 607 void 608 nfs_expinfo_walk_fini(mdb_walk_state_t *wsp) 609 { 610 hash_table_walk_fini(wsp); 611 mdb_free(wsp->walk_arg, sizeof (hash_table_walk_arg_t)); 612 } 613 614 /* 615 * nfs_expvis walker implementation 616 */ 617 618 int 619 nfs_expvis_walk_init(mdb_walk_state_t *wsp) 620 { 621 if (wsp->walk_addr == 0) { 622 mdb_warn("global walk not supported"); 623 return (WALK_ERR); 624 } 625 626 return (WALK_NEXT); 627 } 628 629 int 630 nfs_expvis_walk_step(mdb_walk_state_t *wsp) 631 { 632 exp_visible_t vis; 633 uintptr_t addr = wsp->walk_addr; 634 635 if (addr == 0) 636 return (WALK_DONE); 637 638 if (mdb_vread(&vis, sizeof (vis), addr) == -1) { 639 mdb_warn("failed to read exp_visible_t at %p", addr); 640 return (WALK_ERR); 641 } 642 643 wsp->walk_addr = (uintptr_t)vis.vis_next; 644 return (wsp->walk_callback(addr, &vis, wsp->walk_cbdata)); 645 } 646 647 /* 648 * nfssrv_globals walker, gets the nfs globals for each zone 649 * 650 * Note: Most of the NFS dcmds take a zone pointer, at some point we may 651 * want to change that to take the nfs globals address and aviod the zone 652 * key lookup. This walker could be helpful in that change. 653 */ 654 int 655 nfssrv_globals_walk_init(mdb_walk_state_t *wsp) 656 { 657 GElf_Sym sym; 658 659 if (wsp->walk_addr == 0) { 660 if (mdb_lookup_by_name("nfssrv_globals_list", &sym) == -1) { 661 mdb_warn("failed to find 'nfssrv_globals_list'"); 662 return (WALK_ERR); 663 } 664 wsp->walk_addr = (uintptr_t)sym.st_value; 665 } else { 666 mdb_printf("nfssrv_globals walk only supports global walks\n"); 667 return (WALK_ERR); 668 } 669 670 if (mdb_layered_walk("list", wsp) == -1) { 671 mdb_warn("couldn't walk 'list'"); 672 return (WALK_ERR); 673 } 674 675 wsp->walk_data = (void *)wsp->walk_addr; 676 return (WALK_NEXT); 677 } 678 679 int 680 nfssrv_globals_walk_step(mdb_walk_state_t *wsp) 681 { 682 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer, 683 wsp->walk_cbdata)); 684 } 685