1 /* 2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that: (1) source code distributions 7 * retain the above copyright notice and this paragraph in its entirety, (2) 8 * distributions including binary code include the above copyright notice and 9 * this paragraph in its entirety in the documentation or other materials 10 * provided with the distribution, and (3) all advertising materials mentioning 11 * features or use of this software display the following acknowledgement: 12 * ``This product includes software developed by the University of California, 13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 14 * the University nor the names of its contributors may be used to endorse 15 * or promote products derived from this software without specific prior 16 * written permission. 17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20 */ 21 22 #ifndef lint 23 static const char rcsid[] = 24 "@(#) $Header: /home/cvs/src/usr.sbin/tcpdump/print-nfs.c,v 1.5 1996/12/12 16:22:32 bitblt Exp $ (LBL)"; 25 #endif 26 27 #include <sys/param.h> 28 #include <sys/time.h> 29 #include <sys/socket.h> 30 31 #if __STDC__ 32 struct mbuf; 33 struct rtentry; 34 #endif 35 #include <net/if.h> 36 37 #include <netinet/in.h> 38 #include <netinet/if_ether.h> 39 #include <netinet/in_systm.h> 40 #include <netinet/ip.h> 41 #include <netinet/ip_var.h> 42 43 #include <rpc/rpc.h> 44 45 #include <ctype.h> 46 #include <pcap.h> 47 #include <stdio.h> 48 #include <string.h> 49 50 #include "interface.h" 51 #include "addrtoname.h" 52 53 #include "nfsv2.h" 54 #include "nfsfh.h" 55 56 static void nfs_printfh(const u_int32_t *); 57 static void xid_map_enter(const struct rpc_msg *, const struct ip *); 58 static int32_t xid_map_find(const struct rpc_msg *, const struct ip *); 59 static void interp_reply(const struct rpc_msg *, u_int32_t, u_int); 60 61 static int nfserr; /* true if we error rather than trunc */ 62 63 void 64 nfsreply_print(register const u_char *bp, u_int length, 65 register const u_char *bp2) 66 { 67 register const struct rpc_msg *rp; 68 register const struct ip *ip; 69 int32_t proc; 70 71 nfserr = 0; /* assume no error */ 72 rp = (const struct rpc_msg *)bp; 73 ip = (const struct ip *)bp2; 74 75 if (!nflag) 76 (void)printf("%s.nfs > %s.%x: reply %s %d", 77 ipaddr_string(&ip->ip_src), 78 ipaddr_string(&ip->ip_dst), 79 (u_int32_t)ntohl(rp->rm_xid), 80 ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED? 81 "ok":"ERR", 82 length); 83 else 84 (void)printf("%s.%x > %s.%x: reply %s %d", 85 ipaddr_string(&ip->ip_src), 86 NFS_PORT, 87 ipaddr_string(&ip->ip_dst), 88 (u_int32_t)ntohl(rp->rm_xid), 89 ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED? 90 "ok":"ERR", 91 length); 92 93 proc = xid_map_find(rp, ip); 94 if (proc >= 0) 95 interp_reply(rp, (u_int32_t)proc, length); 96 } 97 98 /* 99 * Return a pointer to the first file handle in the packet. 100 * If the packet was truncated, return 0. 101 */ 102 static const u_int32_t * 103 parsereq(register const struct rpc_msg *rp, register u_int length) 104 { 105 register const u_int32_t *dp; 106 register u_int len; 107 108 /* 109 * find the start of the req data (if we captured it) 110 */ 111 dp = (u_int32_t *)&rp->rm_call.cb_cred; 112 TCHECK(dp[1]); 113 len = ntohl(dp[1]); 114 if (len < length) { 115 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp); 116 TCHECK(dp[1]); 117 len = ntohl(dp[1]); 118 if (len < length) { 119 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp); 120 TCHECK2(dp[0], 0); 121 return (dp); 122 } 123 } 124 trunc: 125 return (NULL); 126 } 127 128 /* 129 * Print out an NFS file handle and return a pointer to following word. 130 * If packet was truncated, return 0. 131 */ 132 static const u_int32_t * 133 parsefh(register const u_int32_t *dp) 134 { 135 if (dp + 8 <= (u_int32_t *)snapend) { 136 nfs_printfh(dp); 137 return (dp + 8); 138 } 139 return (NULL); 140 } 141 142 /* 143 * Print out a file name and return pointer to 32-bit word past it. 144 * If packet was truncated, return 0. 145 */ 146 static const u_int32_t * 147 parsefn(register const u_int32_t *dp) 148 { 149 register u_int32_t len; 150 register const u_char *cp; 151 152 /* Bail if we don't have the string length */ 153 if ((u_char *)dp > snapend - sizeof(*dp)) 154 return (NULL); 155 156 /* Fetch string length; convert to host order */ 157 len = *dp++; 158 NTOHL(len); 159 160 cp = (u_char *)dp; 161 /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */ 162 dp += ((len + 3) & ~3) / sizeof(*dp); 163 if ((u_char *)dp > snapend) 164 return (NULL); 165 /* XXX seems like we should be checking the length */ 166 putchar('"'); 167 (void) fn_printn(cp, len, NULL); 168 putchar('"'); 169 170 return (dp); 171 } 172 173 /* 174 * Print out file handle and file name. 175 * Return pointer to 32-bit word past file name. 176 * If packet was truncated (or there was some other error), return 0. 177 */ 178 static const u_int32_t * 179 parsefhn(register const u_int32_t *dp) 180 { 181 dp = parsefh(dp); 182 if (dp == NULL) 183 return (NULL); 184 putchar(' '); 185 return (parsefn(dp)); 186 } 187 188 void 189 nfsreq_print(register const u_char *bp, u_int length, 190 register const u_char *bp2) 191 { 192 register const struct rpc_msg *rp; 193 register const struct ip *ip; 194 register const u_int32_t *dp; 195 196 nfserr = 0; /* assume no error */ 197 rp = (const struct rpc_msg *)bp; 198 ip = (const struct ip *)bp2; 199 if (!nflag) 200 (void)printf("%s.%x > %s.nfs: %d", 201 ipaddr_string(&ip->ip_src), 202 (u_int32_t)ntohl(rp->rm_xid), 203 ipaddr_string(&ip->ip_dst), 204 length); 205 else 206 (void)printf("%s.%x > %s.%x: %d", 207 ipaddr_string(&ip->ip_src), 208 (u_int32_t)ntohl(rp->rm_xid), 209 ipaddr_string(&ip->ip_dst), 210 NFS_PORT, 211 length); 212 213 xid_map_enter(rp, ip); /* record proc number for later on */ 214 215 switch (ntohl(rp->rm_call.cb_proc)) { 216 #ifdef NFSPROC_NOOP 217 case NFSPROC_NOOP: 218 printf(" nop"); 219 return; 220 #else 221 #define NFSPROC_NOOP -1 222 #endif 223 case NFSPROC_NULL: 224 printf(" null"); 225 return; 226 227 case NFSPROC_GETATTR: 228 printf(" getattr"); 229 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL) 230 return; 231 break; 232 233 case NFSPROC_SETATTR: 234 printf(" setattr"); 235 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL) 236 return; 237 break; 238 239 #if NFSPROC_ROOT != NFSPROC_NOOP 240 case NFSPROC_ROOT: 241 printf(" root"); 242 break; 243 #endif 244 case NFSPROC_LOOKUP: 245 printf(" lookup"); 246 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL) 247 return; 248 break; 249 250 case NFSPROC_READLINK: 251 printf(" readlink"); 252 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL) 253 return; 254 break; 255 256 case NFSPROC_READ: 257 printf(" read"); 258 if ((dp = parsereq(rp, length)) != NULL && 259 (dp = parsefh(dp)) != NULL) { 260 TCHECK2(dp[0], 3 * sizeof(*dp)); 261 printf(" %u bytes @ %u", 262 (u_int32_t)ntohl(dp[1]), 263 (u_int32_t)ntohl(dp[0])); 264 return; 265 } 266 break; 267 268 #if NFSPROC_WRITECACHE != NFSPROC_NOOP 269 case NFSPROC_WRITECACHE: 270 printf(" writecache"); 271 if ((dp = parsereq(rp, length)) != NULL && 272 (dp = parsefh(dp)) != NULL) { 273 TCHECK2(dp[0], 4 * sizeof(*dp)); 274 printf(" %u (%u) bytes @ %u (%u)", 275 (u_int32_t)ntohl(dp[3]), 276 (u_int32_t)ntohl(dp[2]), 277 (u_int32_t)ntohl(dp[1]), 278 (u_int32_t)ntohl(dp[0])); 279 return; 280 } 281 break; 282 #endif 283 case NFSPROC_WRITE: 284 printf(" write"); 285 if ((dp = parsereq(rp, length)) != NULL && 286 (dp = parsefh(dp)) != NULL) { 287 TCHECK2(dp[0], 4 * sizeof(*dp)); 288 printf(" %u (%u) bytes @ %u (%u)", 289 (u_int32_t)ntohl(dp[3]), 290 (u_int32_t)ntohl(dp[2]), 291 (u_int32_t)ntohl(dp[1]), 292 (u_int32_t)ntohl(dp[0])); 293 return; 294 } 295 break; 296 297 case NFSPROC_CREATE: 298 printf(" create"); 299 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL) 300 return; 301 break; 302 303 case NFSPROC_REMOVE: 304 printf(" remove"); 305 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL) 306 return; 307 break; 308 309 case NFSPROC_RENAME: 310 printf(" rename"); 311 if ((dp = parsereq(rp, length)) != NULL && 312 (dp = parsefhn(dp)) != NULL) { 313 fputs(" ->", stdout); 314 if (parsefhn(dp) != NULL) 315 return; 316 } 317 break; 318 319 case NFSPROC_LINK: 320 printf(" link"); 321 if ((dp = parsereq(rp, length)) != NULL && 322 (dp = parsefh(dp)) != NULL) { 323 fputs(" ->", stdout); 324 if (parsefhn(dp) != NULL) 325 return; 326 } 327 break; 328 329 case NFSPROC_SYMLINK: 330 printf(" symlink"); 331 if ((dp = parsereq(rp, length)) != NULL && 332 (dp = parsefhn(dp)) != NULL) { 333 fputs(" -> ", stdout); 334 if (parsefn(dp) != NULL) 335 return; 336 } 337 break; 338 339 case NFSPROC_MKDIR: 340 printf(" mkdir"); 341 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL) 342 return; 343 break; 344 345 case NFSPROC_RMDIR: 346 printf(" rmdir"); 347 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp) != NULL) 348 return; 349 break; 350 351 case NFSPROC_READDIR: 352 printf(" readdir"); 353 if ((dp = parsereq(rp, length)) != NULL && 354 (dp = parsefh(dp)) != NULL) { 355 TCHECK2(dp[0], 2 * sizeof(*dp)); 356 /* 357 * Print the offset as signed, since -1 is common, 358 * but offsets > 2^31 aren't. 359 */ 360 printf(" %u bytes @ %d", 361 (u_int32_t)ntohl(dp[1]), 362 (u_int32_t)ntohl(dp[0])); 363 return; 364 } 365 break; 366 367 case NFSPROC_STATFS: 368 printf(" statfs"); 369 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp) != NULL) 370 return; 371 break; 372 373 default: 374 printf(" proc-%u", (u_int32_t)ntohl(rp->rm_call.cb_proc)); 375 return; 376 } 377 trunc: 378 if (!nfserr) 379 fputs(" [|nfs]", stdout); 380 } 381 382 /* 383 * Print out an NFS file handle. 384 * We assume packet was not truncated before the end of the 385 * file handle pointed to by dp. 386 * 387 * Note: new version (using portable file-handle parser) doesn't produce 388 * generation number. It probably could be made to do that, with some 389 * additional hacking on the parser code. 390 */ 391 static void 392 nfs_printfh(register const u_int32_t *dp) 393 { 394 my_fsid fsid; 395 ino_t ino; 396 char *sfsname = NULL; 397 398 Parse_fh((caddr_t *)dp, &fsid, &ino, NULL, &sfsname, 0); 399 400 if (sfsname) { 401 /* file system ID is ASCII, not numeric, for this server OS */ 402 static char temp[NFS_FHSIZE+1]; 403 404 /* Make sure string is null-terminated */ 405 strncpy(temp, sfsname, NFS_FHSIZE); 406 /* Remove trailing spaces */ 407 sfsname = strchr(temp, ' '); 408 if (sfsname) 409 *sfsname = 0; 410 411 (void)printf(" fh %s/%u", temp, (u_int32_t)ino); 412 } else { 413 (void)printf(" fh %u,%u/%u", 414 fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor, (u_int32_t)ino); 415 } 416 } 417 418 /* 419 * Maintain a small cache of recent client.XID.server/proc pairs, to allow 420 * us to match up replies with requests and thus to know how to parse 421 * the reply. 422 */ 423 424 struct xid_map_entry { 425 u_int32_t xid; /* transaction ID (net order) */ 426 struct in_addr client; /* client IP address (net order) */ 427 struct in_addr server; /* server IP address (net order) */ 428 u_int32_t proc; /* call proc number (host order) */ 429 }; 430 431 /* 432 * Map entries are kept in an array that we manage as a ring; 433 * new entries are always added at the tail of the ring. Initially, 434 * all the entries are zero and hence don't match anything. 435 */ 436 437 #define XIDMAPSIZE 64 438 439 struct xid_map_entry xid_map[XIDMAPSIZE]; 440 441 int xid_map_next = 0; 442 int xid_map_hint = 0; 443 444 static void 445 xid_map_enter(const struct rpc_msg *rp, const struct ip *ip) 446 { 447 struct xid_map_entry *xmep; 448 449 xmep = &xid_map[xid_map_next]; 450 451 if (++xid_map_next >= XIDMAPSIZE) 452 xid_map_next = 0; 453 454 xmep->xid = rp->rm_xid; 455 xmep->client = ip->ip_src; 456 xmep->server = ip->ip_dst; 457 xmep->proc = ntohl(rp->rm_call.cb_proc); 458 } 459 460 /* Returns NFSPROC_xxx or -1 on failure */ 461 static int32_t 462 xid_map_find(const struct rpc_msg *rp, const struct ip *ip) 463 { 464 int i; 465 struct xid_map_entry *xmep; 466 u_int32_t xid = rp->rm_xid; 467 u_int32_t clip = ip->ip_dst.s_addr; 468 u_int32_t sip = ip->ip_src.s_addr; 469 470 /* Start searching from where we last left off */ 471 i = xid_map_hint; 472 do { 473 xmep = &xid_map[i]; 474 if (xmep->xid == xid && xmep->client.s_addr == clip && 475 xmep->server.s_addr == sip) { 476 /* match */ 477 xid_map_hint = i; 478 return ((int32_t)xmep->proc); 479 } 480 if (++i >= XIDMAPSIZE) 481 i = 0; 482 } while (i != xid_map_hint); 483 484 /* search failed */ 485 return (-1); 486 } 487 488 /* 489 * Routines for parsing reply packets 490 */ 491 492 /* 493 * Return a pointer to the beginning of the actual results. 494 * If the packet was truncated, return 0. 495 */ 496 static const u_int32_t * 497 parserep(register const struct rpc_msg *rp, register u_int length) 498 { 499 register const u_int32_t *dp; 500 u_int len; 501 enum accept_stat astat; 502 503 /* 504 * Portability note: 505 * Here we find the address of the ar_verf credentials. 506 * Originally, this calculation was 507 * dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf 508 * On the wire, the rp_acpt field starts immediately after 509 * the (32 bit) rp_stat field. However, rp_acpt (which is a 510 * "struct accepted_reply") contains a "struct opaque_auth", 511 * whose internal representation contains a pointer, so on a 512 * 64-bit machine the compiler inserts 32 bits of padding 513 * before rp->rm_reply.rp_acpt.ar_verf. So, we cannot use 514 * the internal representation to parse the on-the-wire 515 * representation. Instead, we skip past the rp_stat field, 516 * which is an "enum" and so occupies one 32-bit word. 517 */ 518 dp = ((const u_int32_t *)&rp->rm_reply) + 1; 519 TCHECK2(dp[0], 1); 520 len = ntohl(dp[1]); 521 if (len >= length) 522 return (NULL); 523 /* 524 * skip past the ar_verf credentials. 525 */ 526 dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t); 527 TCHECK2(dp[0], 0); 528 529 /* 530 * now we can check the ar_stat field 531 */ 532 astat = ntohl(*(enum accept_stat *)dp); 533 switch (astat) { 534 535 case SUCCESS: 536 break; 537 538 case PROG_UNAVAIL: 539 printf(" PROG_UNAVAIL"); 540 nfserr = 1; /* suppress trunc string */ 541 return (NULL); 542 543 case PROG_MISMATCH: 544 printf(" PROG_MISMATCH"); 545 nfserr = 1; /* suppress trunc string */ 546 return (NULL); 547 548 case PROC_UNAVAIL: 549 printf(" PROC_UNAVAIL"); 550 nfserr = 1; /* suppress trunc string */ 551 return (NULL); 552 553 case GARBAGE_ARGS: 554 printf(" GARBAGE_ARGS"); 555 nfserr = 1; /* suppress trunc string */ 556 return (NULL); 557 558 case SYSTEM_ERR: 559 printf(" SYSTEM_ERR"); 560 nfserr = 1; /* suppress trunc string */ 561 return (NULL); 562 563 default: 564 printf(" ar_stat %d", astat); 565 nfserr = 1; /* suppress trunc string */ 566 return (NULL); 567 } 568 /* successful return */ 569 if ((sizeof(astat) + ((u_char *)dp)) < snapend) 570 return ((u_int32_t *) (sizeof(astat) + ((char *)dp))); 571 572 trunc: 573 return (NULL); 574 } 575 576 static const u_int32_t * 577 parsestatus(const u_int32_t *dp) 578 { 579 register int errnum; 580 581 TCHECK(dp[0]); 582 errnum = ntohl(dp[0]); 583 if (errnum != 0) { 584 if (!qflag) 585 printf(" ERROR: %s", pcap_strerror(errnum)); 586 nfserr = 1; /* suppress trunc string */ 587 return (NULL); 588 } 589 return (dp + 1); 590 trunc: 591 return (NULL); 592 } 593 594 static struct tok type2str[] = { 595 { NFNON, "NON" }, 596 { NFREG, "REG" }, 597 { NFDIR, "DIR" }, 598 { NFBLK, "BLK" }, 599 { NFCHR, "CHR" }, 600 { NFLNK, "LNK" }, 601 { 0, NULL } 602 }; 603 604 static const u_int32_t * 605 parsefattr(const u_int32_t *dp, int verbose) 606 { 607 const struct nfsv2_fattr *fap; 608 609 fap = (const struct nfsv2_fattr *)dp; 610 if (verbose) { 611 TCHECK(fap->fa_nfssize); 612 printf(" %s %o ids %u/%u sz %u ", 613 tok2str(type2str, "unk-ft %d ", 614 (u_int32_t)ntohl(fap->fa_type)), 615 (u_int32_t)ntohl(fap->fa_mode), 616 (u_int32_t)ntohl(fap->fa_uid), 617 (u_int32_t)ntohl(fap->fa_gid), 618 (u_int32_t)ntohl(fap->fa_nfssize)); 619 } 620 /* print lots more stuff */ 621 if (verbose > 1) { 622 TCHECK(fap->fa_nfsfileid); 623 printf("nlink %u rdev %x fsid %x nodeid %x a/m/ctime ", 624 (u_int32_t)ntohl(fap->fa_nlink), 625 (u_int32_t)ntohl(fap->fa_nfsrdev), 626 (u_int32_t)ntohl(fap->fa_nfsfsid), 627 (u_int32_t)ntohl(fap->fa_nfsfileid)); 628 TCHECK(fap->fa_nfsatime); 629 printf("%u.%06u ", 630 (u_int32_t)ntohl(fap->fa_nfsatime.nfs_sec), 631 (u_int32_t)ntohl(fap->fa_nfsatime.nfs_usec)); 632 TCHECK(fap->fa_nfsmtime); 633 printf("%u.%06u ", 634 (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_sec), 635 (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_usec)); 636 TCHECK(fap->fa_nfsctime); 637 printf("%u.%06u ", 638 (u_int32_t)ntohl(fap->fa_nfsctime.nfs_sec), 639 (u_int32_t)ntohl(fap->fa_nfsctime.nfs_usec)); 640 } 641 return ((const u_int32_t *)&fap[1]); 642 trunc: 643 return (NULL); 644 } 645 646 static int 647 parseattrstat(const u_int32_t *dp, int verbose) 648 { 649 650 dp = parsestatus(dp); 651 if (dp == NULL) 652 return (0); 653 654 return (parsefattr(dp, verbose) != NULL); 655 } 656 657 static int 658 parsediropres(const u_int32_t *dp) 659 { 660 661 dp = parsestatus(dp); 662 if (dp == NULL) 663 return (0); 664 665 dp = parsefh(dp); 666 if (dp == NULL) 667 return (0); 668 669 return (parsefattr(dp, vflag) != NULL); 670 } 671 672 static int 673 parselinkres(const u_int32_t *dp) 674 { 675 dp = parsestatus(dp); 676 if (dp == NULL) 677 return (0); 678 679 putchar(' '); 680 return (parsefn(dp) != NULL); 681 } 682 683 static int 684 parsestatfs(const u_int32_t *dp) 685 { 686 const struct nfsv2_statfs *sfsp; 687 688 dp = parsestatus(dp); 689 if (dp == NULL) 690 return (0); 691 692 if (!qflag) { 693 sfsp = (const struct nfsv2_statfs *)dp; 694 TCHECK(sfsp->sf_bavail); 695 printf(" tsize %u bsize %u blocks %u bfree %u bavail %u", 696 (u_int32_t)ntohl(sfsp->sf_tsize), 697 (u_int32_t)ntohl(sfsp->sf_bsize), 698 (u_int32_t)ntohl(sfsp->sf_blocks), 699 (u_int32_t)ntohl(sfsp->sf_bfree), 700 (u_int32_t)ntohl(sfsp->sf_bavail)); 701 } 702 703 return (1); 704 trunc: 705 return (0); 706 } 707 708 static int 709 parserddires(const u_int32_t *dp) 710 { 711 dp = parsestatus(dp); 712 if (dp == NULL) 713 return (0); 714 if (!qflag) { 715 TCHECK(dp[0]); 716 printf(" offset %x", (u_int32_t)ntohl(dp[0])); 717 TCHECK(dp[1]); 718 printf(" size %u", (u_int32_t)ntohl(dp[1])); 719 TCHECK(dp[2]); 720 if (dp[2] != 0) 721 printf(" eof"); 722 } 723 724 return (1); 725 trunc: 726 return (0); 727 } 728 729 static void 730 interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int length) 731 { 732 register const u_int32_t *dp; 733 734 switch (proc) { 735 736 #ifdef NFSPROC_NOOP 737 case NFSPROC_NOOP: 738 printf(" nop"); 739 return; 740 #else 741 #define NFSPROC_NOOP -1 742 #endif 743 case NFSPROC_NULL: 744 printf(" null"); 745 return; 746 747 case NFSPROC_GETATTR: 748 printf(" getattr"); 749 dp = parserep(rp, length); 750 if (dp != NULL && parseattrstat(dp, !qflag) != 0) 751 return; 752 break; 753 754 case NFSPROC_SETATTR: 755 printf(" setattr"); 756 dp = parserep(rp, length); 757 if (dp != NULL && parseattrstat(dp, !qflag) != 0) 758 return; 759 break; 760 761 #if NFSPROC_ROOT != NFSPROC_NOOP 762 case NFSPROC_ROOT: 763 printf(" root"); 764 break; 765 #endif 766 case NFSPROC_LOOKUP: 767 printf(" lookup"); 768 dp = parserep(rp, length); 769 if (dp != NULL && parsediropres(dp) != 0) 770 return; 771 break; 772 773 case NFSPROC_READLINK: 774 printf(" readlink"); 775 dp = parserep(rp, length); 776 if (dp != NULL && parselinkres(dp) != 0) 777 return; 778 break; 779 780 case NFSPROC_READ: 781 printf(" read"); 782 dp = parserep(rp, length); 783 if (dp != NULL && parseattrstat(dp, vflag) != 0) 784 return; 785 break; 786 787 #if NFSPROC_WRITECACHE != NFSPROC_NOOP 788 case NFSPROC_WRITECACHE: 789 printf(" writecache"); 790 break; 791 #endif 792 case NFSPROC_WRITE: 793 printf(" write"); 794 dp = parserep(rp, length); 795 if (dp != NULL && parseattrstat(dp, vflag) != 0) 796 return; 797 break; 798 799 case NFSPROC_CREATE: 800 printf(" create"); 801 dp = parserep(rp, length); 802 if (dp != NULL && parsediropres(dp) != 0) 803 return; 804 break; 805 806 case NFSPROC_REMOVE: 807 printf(" remove"); 808 dp = parserep(rp, length); 809 if (dp != NULL && parsestatus(dp) != 0) 810 return; 811 break; 812 813 case NFSPROC_RENAME: 814 printf(" rename"); 815 dp = parserep(rp, length); 816 if (dp != NULL && parsestatus(dp) != 0) 817 return; 818 break; 819 820 case NFSPROC_LINK: 821 printf(" link"); 822 dp = parserep(rp, length); 823 if (dp != NULL && parsestatus(dp) != 0) 824 return; 825 break; 826 827 case NFSPROC_SYMLINK: 828 printf(" symlink"); 829 dp = parserep(rp, length); 830 if (dp != NULL && parsestatus(dp) != 0) 831 return; 832 break; 833 834 case NFSPROC_MKDIR: 835 printf(" mkdir"); 836 dp = parserep(rp, length); 837 if (dp != NULL && parsediropres(dp) != 0) 838 return; 839 break; 840 841 case NFSPROC_RMDIR: 842 printf(" rmdir"); 843 dp = parserep(rp, length); 844 if (dp != NULL && parsestatus(dp) != 0) 845 return; 846 break; 847 848 case NFSPROC_READDIR: 849 printf(" readdir"); 850 dp = parserep(rp, length); 851 if (dp != NULL && parserddires(dp) != 0) 852 return; 853 break; 854 855 case NFSPROC_STATFS: 856 printf(" statfs"); 857 dp = parserep(rp, length); 858 if (dp != NULL && parsestatfs(dp) != 0) 859 return; 860 break; 861 862 default: 863 printf(" proc-%u", proc); 864 return; 865 } 866 if (!nfserr) 867 fputs(" [|nfs]", stdout); 868 } 869