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