--- /dev/null
+#!/usr/bin/perl -w\r
+#\r
+# Copyright (c) 2002-2003\r
+# The President and Fellows of Harvard College.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions\r
+# are met:\r
+# 1. Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# 2. Redistributions in binary form must reproduce the above copyright\r
+# notice, this list of conditions and the following disclaimer in the\r
+# documentation and/or other materials provided with the distribution.\r
+# 3. Neither the name of the University nor the names of its contributors\r
+# may be used to endorse or promote products derived from this software\r
+# without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND\r
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE\r
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
+# SUCH DAMAGE.\r
+#\r
+# $Id: nfsdump.pl,v 1.5 2003/07/26 20:52:04 ellard Exp $\r
+#\r
+# Utility for dealing with raw nfsdump records.\r
+\r
+package nfsd;\r
+\r
+# If $AllowRisky is set, then allow some optimizations that might be\r
+# "risky" in bizarre situations (but have never been observed to\r
+# actually break anything). By default, no riskiness is permitted.\r
+\r
+$AllowRisky = 0;\r
+\r
+# nfsDumpParseLine -- initializes the global associative array\r
+# %nfsd'nfsDumpLine with information about a record from nfsdump. \r
+# Returns an empty list if anything goes wrong. Otherwise, returns a\r
+# list of the protocol (eg R2, C2, R3, C3), the name of the operation,\r
+# and the xid, the client host ID, the time, and for responses, the\r
+# status (via nfsDumpParseLineHeader). The reason for this particular\r
+# return list is that these are very frequently-accessed values, so it\r
+# can save time to avoid going through the associative array to access\r
+# them.\r
+#\r
+# All records begin with several fixed fields, and then are followed\r
+# by some number of name/value pairs, and finally some diagnostic\r
+# fields (which are mostly ignored by this routine-- the only\r
+# diagnostic this routine cares about is whether the packet as part of\r
+# a jumbo packet or not. If so, then 'truncated' is set.)\r
+\r
+sub nfsDumpParseLine {\r
+ my ($line, $total) = @_;\r
+\r
+ my (@rl) = &nfsDumpParseLineHeader ($line);\r
+\r
+ if (@rl && $total) {\r
+ &nfsDumpParseLineBody ($line);\r
+ }\r
+\r
+ return @rl;\r
+}\r
+\r
+sub nfsDumpParseLineBody {\r
+ my ($line) = @_;\r
+ my $i;\r
+ my $client_id;\r
+ my $reseen;\r
+\r
+ undef %nfsDumpLine;\r
+\r
+ # If the line doesn't start with a digit, then it's certainly\r
+ # not properly formed, so bail out immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return undef;\r
+ }\r
+\r
+ my @l = split (' ', $line);\r
+ my $lineLen = @l;\r
+ if ($l[$lineLen - 1] eq 'LONGPKT') {\r
+ splice (@l, $lineLen - 1);\r
+ $nfsDumpLine{'truncated'} = 1;\r
+ $lineLen--;\r
+ }\r
+\r
+ $nfsDumpLine{'time'} = $l[0];\r
+ $nfsDumpLine{'srchost'} = $l[1];\r
+ $nfsDumpLine{'deshost'} = $l[2];\r
+ $nfsDumpLine{'proto'} = $l[4];\r
+ $nfsDumpLine{'xid'} = $l[5];\r
+ $nfsDumpLine{'opcode'} = $l[6];\r
+ $nfsDumpLine{'opname'} = $l[7];\r
+\r
+ if (($l[4] eq 'R3') || ($l[4] eq 'R2')) {\r
+ $nfsDumpLine{'status'} = $l[8];\r
+\r
+ $client_id = $l[2];\r
+ $reseen = 0;\r
+ for ($i = 9; $i < $lineLen - 10; $i += 2) {\r
+ if (defined $nfsDumpLine{$l[$i]}) {\r
+ $reseen = 1;\r
+ $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1];\r
+ }\r
+ else {\r
+ $nfsDumpLine{$l[$i]} = $l[$i + 1];\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ $client_id = $l[1];\r
+ $reseen = 0;\r
+ for ($i = 8; $i < $lineLen - 6; $i += 2) {\r
+ if (defined $nfsDumpLine{$l[$i]}) {\r
+ $nfsDumpLine{"$l[$i]-2"} = $l[$i + 1];\r
+ $reseen = 1;\r
+ }\r
+ else {\r
+ $nfsDumpLine{$l[$i]} = $l[$i + 1];\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+# Returns an empty list if anything goes wrong. Otherwise, returns a\r
+# list of the protocol (eg R2, C2, R3, C3), the name of the operation,\r
+# and the xid, the client host ID, and time, and the response status. \r
+# (For call messages, the status is 'na'.)\r
+\r
+sub nfsDumpParseLineHeader {\r
+ my ($line) = @_;\r
+\r
+ # If the line doesn't start with a digit, then it's certainly\r
+ # not properly formed, so bail out immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return ();\r
+ }\r
+ else {\r
+ my $client_id;\r
+ my $status;\r
+\r
+ my @l = split (' ', $line, 10);\r
+\r
+\r
+ if (($l[4] eq 'R3') || ($l[4] eq 'R2')) {\r
+ $client_id = $l[2];\r
+ $status = $l[8];\r
+ }\r
+ else {\r
+ $client_id = $l[1];\r
+ $status = 'na';\r
+ }\r
+\r
+ return ($l[4], $l[7], $l[5], $client_id, $l[0], $status);\r
+ }\r
+}\r
+\r
+# nfsDumpParseLineFields -- Just return a subset of the fields,\r
+# without parsing the entire line.\r
+\r
+sub nfsDumpParseLineFields {\r
+ my ($line, @fields) = @_;\r
+ my $i;\r
+\r
+ # If the line doesn't start with a digit, then\r
+ # it's certainly not properly formed, so bail out\r
+ # immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return ();\r
+ }\r
+\r
+ my $rest;\r
+ if ($AllowRisky) {\r
+ $rest = $line;\r
+ }\r
+ else {\r
+ my @foo = split (' ', $line, 9);\r
+ $rest = ' ' . $foo[8];\r
+ }\r
+\r
+ my $fl = @fields;\r
+ my @l = ();\r
+ for ($i = 0; $i < $fl; $i++) {\r
+ my $field = $fields[$i];\r
+\r
+ $rest =~ /\ $field\ +([^\ ]+)/;\r
+ $l[$i] = $1;\r
+ }\r
+\r
+ return (@l);\r
+}\r
+\r
+# nfsDumpParseLineField -- Just return ONE of the fields,\r
+# without parsing the entire line.\r
+\r
+sub nfsDumpParseLineField {\r
+ my ($line, $field) = @_;\r
+\r
+ # If the line doesn't start with a digit, then\r
+ # it's certainly not properly formed, so bail out\r
+ # immediately.\r
+\r
+ if (! ($line =~ /^[0-9]/)) {\r
+ return undef;\r
+ }\r
+\r
+ my $rest;\r
+ if ($AllowRisky) {\r
+ $rest = $line;\r
+ }\r
+ else {\r
+ my @foo = split (' ', $line, 9);\r
+ $rest = ' ' . $foo[8];\r
+ }\r
+\r
+ $rest =~ /\ $field\ +([^\ ]+)/;\r
+ return $1;\r
+}\r
+\r
+# Returns a new file handle that has all the "useful" information as\r
+# the original, but requires less storage space. File handles\r
+# typically contain quite a bit of redundancy or unused bytes.\r
+#\r
+# This routine only knows about the advfs and netapp formats. If\r
+# you're using anything else, just use anything else as the mode, and\r
+# the original file handle will be returned.\r
+#\r
+# If you extend this to handle more file handles, please send the new\r
+# code to me (ellard@eecs.harvard.edu) so I can add it to the\r
+# distribution.\r
+\r
+sub nfsDumpCompressFH {\r
+ my ($mode, $fh) = @_;\r
+\r
+ if ($mode eq 'advfs') {\r
+\r
+ # The fh is a long hex string:\r
+ # 8 chars: file system ID\r
+ # 8 chars: apparently unused.\r
+ # 8 chars: unused.\r
+ # 8 chars: inode\r
+ # 8 chars: generation\r
+ # rest of string: mount point (not interesting).\r
+ # So all we do is pluck out the fsid, inode,\r
+ # and generation number, and throw the rest away.\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$1-$4-$5");\r
+ }\r
+ elsif ($mode eq 'netapp') {\r
+\r
+ # Here's the netapp format (from Shane Owara):\r
+ #\r
+ # 4 bytes mount point file inode number\r
+ # 4 bytes mount point file generation number\r
+ # \r
+ # 2 bytes flags\r
+ # 1 byte snapshot id\r
+ # 1 byte unused\r
+ #\r
+ # 4 bytes file inode number\r
+ # 4 bytes file generation number\r
+ # 4 bytes volume identifier\r
+ #\r
+ # 4 bytes export point fileid\r
+ # 1 byte export point snapshot id\r
+ # 3 bytes export point snapshot generation number\r
+ #\r
+ # The only parts of this that are interesting are\r
+ # inode, generation, and volume identifier (and probably\r
+ # a lot of the bits of the volume identifier could be\r
+ # tossed, since we don't have many volumes...).\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$4-$5-$6-$1");\r
+ }\r
+ elsif ($mode eq 'RFSNN') {\r
+\r
+ # Here's the netapp format (from Shane Owara):\r
+ #\r
+ # 4 bytes mount point file inode number\r
+ # 4 bytes mount point file generation number\r
+ # \r
+ # 2 bytes flags\r
+ # 1 byte snapshot id\r
+ # 1 byte unused\r
+ #\r
+ # 4 bytes file inode number\r
+ # 4 bytes file generation number\r
+ # 4 bytes volume identifier\r
+ #\r
+ # 4 bytes export point fileid\r
+ # 1 byte export point snapshot id\r
+ # 3 bytes export point snapshot generation number\r
+ #\r
+ # The only parts of this that are interesting are\r
+ # inode, generation, and volume identifier (and probably\r
+ # a lot of the bits of the volume identifier could be\r
+ # tossed, since we don't have many volumes...).\r
+ \r
+ # 61890100575701002000000 0009ac710e9ea381 0d24400006189010057570100\r
+ # 61890100575701002000000 0009ac70ed2ea381 0d24400006189010057570100\r
+ # 61890100575701002000000 000479a1e008d782 0d24400006189010057570100\r
+ # Ningning need only 24-39 (or 12-19 bytes)\r
+\r
+ $fh =~ /^(........)(........)(........)(........)(........)(........)/;\r
+\r
+ return ("$4$5");\r
+ }else {\r
+\r
+ return ($fh);\r
+ }\r
+}\r
+\r
+sub testMain {\r
+ $lineNum = 0;\r
+\r
+ while (<STDIN>) {\r
+ $line = $_;\r
+ $lineNum++;\r
+\r
+ &nfsDumpParseLine ($line);\r
+ }\r
+}\r
+\r
+1;\r
+\r
+# end of nfsdump.pl\r