Wireshark Patches

From NFSv4

Jump to: navigation, search

updated 2007-06-05, base version wireshark-0.99.5

Just copy packet-nfs.c and packet-nfs.h into wireshark's epan/dissectors/ directory


/* packet-nfs.c
 * Routines for nfs dissection
 * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
 * Copyright 2000-2004, Mike Frisch <frisch@hummingbird.com> (NFSv4 decoding)
 *
 * $Id: packet-nfs.c 19875 2006-11-10 23:36:57Z sahlberg $
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * Copied from packet-smb.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif


#include <string.h>


#include "packet-rpc.h"
#include "packet-nfs.h"
#include <epan/prefs.h>
#include <epan/packet.h>
#include <epan/emem.h>

static int proto_nfs = -1;

static int hf_nfs_procedure_v2 = -1;
static int hf_nfs_procedure_v3 = -1;
static int hf_nfs_procedure_v4 = -1;
static int hf_nfs_fh_length = -1;
static int hf_nfs_fh_hash = -1;
static int hf_nfs_fh_fhandle_data = -1;
static int hf_nfs_fh_mount_fileid = -1;
static int hf_nfs_fh_mount_generation = -1;
static int hf_nfs_fh_snapid = -1;
static int hf_nfs_fh_unused = -1;
static int hf_nfs_fh_flags = -1;
static int hf_nfs_fh_fileid = -1;
static int hf_nfs_fh_generation = -1;
static int hf_nfs_fh_fsid = -1;
static int hf_nfs_fh_export_fileid = -1;
static int hf_nfs_fh_export_generation = -1;
static int hf_nfs_fh_export_snapid = -1;
static int hf_nfs_fh_file_flag_mntpoint = -1;
static int hf_nfs_fh_file_flag_snapdir = -1;
static int hf_nfs_fh_file_flag_snapdir_ent = -1;
static int hf_nfs_fh_file_flag_empty = -1;
static int hf_nfs_fh_file_flag_vbn_access = -1;
static int hf_nfs_fh_file_flag_multivolume = -1;
static int hf_nfs_fh_file_flag_metadata = -1;
static int hf_nfs_fh_file_flag_orphan = -1;
static int hf_nfs_fh_file_flag_foster = -1;
static int hf_nfs_fh_file_flag_named_attr = -1;
static int hf_nfs_fh_file_flag_exp_snapdir = -1;
static int hf_nfs_fh_file_flag_vfiler = -1;
static int hf_nfs_fh_file_flag_aggr = -1;
static int hf_nfs_fh_file_flag_striped = -1;
static int hf_nfs_fh_file_flag_private = -1;
static int hf_nfs_fh_file_flag_next_gen = -1;
static int hf_nfs_fh_handle_type = -1;
static int hf_nfs_fh_fsid_major = -1;
static int hf_nfs_fh_fsid_minor = -1;
static int hf_nfs_fh_fsid_inode = -1;
static int hf_nfs_fh_xfsid_major = -1;
static int hf_nfs_fh_xfsid_minor = -1;
static int hf_nfs_fh_fstype = -1;
static int hf_nfs_fh_fn = -1;
static int hf_nfs_fh_fn_len = -1;
static int hf_nfs_fh_fn_inode = -1;
static int hf_nfs_fh_fn_generation = -1;
static int hf_nfs_fh_xfn = -1;
static int hf_nfs_fh_xfn_len = -1;
static int hf_nfs_fh_xfn_inode = -1;
static int hf_nfs_fh_xfn_generation = -1;
static int hf_nfs_fh_dentry = -1;
static int hf_nfs_fh_dev = -1;
static int hf_nfs_fh_xdev = -1;
static int hf_nfs_fh_dirinode = -1;
static int hf_nfs_fh_pinode = -1;
static int hf_nfs_fh_hp_len = -1;
static int hf_nfs_fh_version = -1;
static int hf_nfs_fh_auth_type = -1;
static int hf_nfs_fh_fsid_type = -1;
static int hf_nfs_fh_fileid_type = -1;
static int hf_gxfh3_utlfield = -1;
static int hf_gxfh3_utlfield_tree_r = -1;
static int hf_gxfh3_utlfield_tree_w = -1;
static int hf_gxfh3_utlfield_jun = -1;
static int hf_gxfh3_utlfield_jun_not = -1;
static int hf_gxfh3_utlfield_ver = -1;
static int hf_gxfh3_volcnt = -1; 
static int hf_gxfh3_epoch = -1; 
static int hf_gxfh3_ldsid = -1; 
static int hf_gxfh3_cid = -1; 
static int hf_gxfh3_resv = -1; 			
static int hf_gxfh3_sfhflags = -1; 			
static int hf_gxfh3_sfhflags_resv1 = -1; 			
static int hf_gxfh3_sfhflags_resv2 = -1; 			
static int hf_gxfh3_sfhflags_ontap7G = -1; 			
static int hf_gxfh3_sfhflags_ontapGX = -1; 			
static int hf_gxfh3_sfhflags_striped = -1; 			
static int hf_gxfh3_sfhflags_empty = -1; 			
static int hf_gxfh3_sfhflags_snapdirent = -1; 			
static int hf_gxfh3_sfhflags_snapdir = -1; 
static int hf_gxfh3_sfhflags_streamdir = -1;  
static int hf_gxfh3_spinfid = -1; 
static int hf_gxfh3_spinfuid = -1; 
static int hf_gxfh3_exportptid = -1; 
static int hf_gxfh3_exportptuid = -1; 

static int hf_nfs_stat = -1;
static int hf_nfs_full_name = -1;
static int hf_nfs_name = -1;
static int hf_nfs_readlink_data = -1;
static int hf_nfs_read_offset = -1;
static int hf_nfs_read_count = -1;
static int hf_nfs_read_totalcount = -1;
static int hf_nfs_data = -1;
static int hf_nfs_write_beginoffset = -1;
static int hf_nfs_write_offset = -1;
static int hf_nfs_write_totalcount = -1;
static int hf_nfs_symlink_to = -1;
static int hf_nfs_readdir_cookie = -1;
static int hf_nfs_readdir_count = -1;
static int hf_nfs_readdir_entry = -1;
static int hf_nfs_readdir_entry_fileid = -1;
static int hf_nfs_readdir_entry_name = -1;
static int hf_nfs_readdir_entry_cookie = -1;
static int hf_nfs_readdir_entry3_fileid = -1;
static int hf_nfs_readdir_entry3_name = -1;
static int hf_nfs_readdir_entry3_cookie = -1;
static int hf_nfs_readdirplus_entry_fileid = -1;
static int hf_nfs_readdirplus_entry_name = -1;
static int hf_nfs_readdirplus_entry_cookie = -1;
static int hf_nfs_readdir_eof = -1;
static int hf_nfs_statfs_tsize = -1;
static int hf_nfs_statfs_bsize = -1;
static int hf_nfs_statfs_blocks = -1;
static int hf_nfs_statfs_bfree = -1;
static int hf_nfs_statfs_bavail = -1;
static int hf_nfs_ftype3 = -1;
static int hf_nfs_nfsstat3 = -1;
static int hf_nfs_read_eof = -1;
static int hf_nfs_write_stable = -1;
static int hf_nfs_write_committed = -1;
static int hf_nfs_createmode3 = -1;
static int hf_nfs_fsstat_invarsec = -1;
static int hf_nfs_fsinfo_rtmax = -1;
static int hf_nfs_fsinfo_rtpref = -1;
static int hf_nfs_fsinfo_rtmult = -1;
static int hf_nfs_fsinfo_wtmax = -1;
static int hf_nfs_fsinfo_wtpref = -1;
static int hf_nfs_fsinfo_wtmult = -1;
static int hf_nfs_fsinfo_dtpref = -1;
static int hf_nfs_fsinfo_maxfilesize = -1;
static int hf_nfs_fsinfo_properties = -1;
static int hf_nfs_pathconf_linkmax = -1;
static int hf_nfs_pathconf_name_max = -1;
static int hf_nfs_pathconf_no_trunc = -1;
static int hf_nfs_pathconf_chown_restricted = -1;
static int hf_nfs_pathconf_case_insensitive = -1;
static int hf_nfs_pathconf_case_preserving = -1;

static int hf_nfs_atime = -1;
static int hf_nfs_atime_sec = -1;
static int hf_nfs_atime_nsec = -1;
static int hf_nfs_atime_usec = -1;
static int hf_nfs_mtime = -1;
static int hf_nfs_mtime_sec = -1;
static int hf_nfs_mtime_nsec = -1;
static int hf_nfs_mtime_usec = -1;
static int hf_nfs_ctime = -1;
static int hf_nfs_ctime_sec = -1;
static int hf_nfs_ctime_nsec = -1;
static int hf_nfs_ctime_usec = -1;
static int hf_nfs_dtime = -1;
static int hf_nfs_dtime_sec = -1;
static int hf_nfs_dtime_nsec = -1;

static int hf_nfs_fattr_type = -1;
static int hf_nfs_fattr_nlink = -1;
static int hf_nfs_fattr_uid = -1;
static int hf_nfs_fattr_gid = -1;
static int hf_nfs_fattr_size = -1;
static int hf_nfs_fattr_blocksize = -1;
static int hf_nfs_fattr_rdev = -1;
static int hf_nfs_fattr_blocks = -1;
static int hf_nfs_fattr_fsid = -1;
static int hf_nfs_fattr_fileid = -1;
static int hf_nfs_fattr3_type = -1;
static int hf_nfs_fattr3_nlink = -1;
static int hf_nfs_fattr3_uid = -1;
static int hf_nfs_fattr3_gid = -1;
static int hf_nfs_fattr3_size = -1;
static int hf_nfs_fattr3_used = -1;
static int hf_nfs_fattr3_rdev = -1;
static int hf_nfs_fattr3_fsid = -1;
static int hf_nfs_fattr3_fileid = -1;
static int hf_nfs_wcc_attr_size = -1;
static int hf_nfs_set_size3_size = -1;
static int hf_nfs_cookie3 = -1;
static int hf_nfs_fsstat3_resok_tbytes = -1;
static int hf_nfs_fsstat3_resok_fbytes = -1;
static int hf_nfs_fsstat3_resok_abytes = -1;
static int hf_nfs_fsstat3_resok_tfiles = -1;
static int hf_nfs_fsstat3_resok_ffiles = -1;
static int hf_nfs_fsstat3_resok_afiles = -1;
static int hf_nfs_uid3 = -1;
static int hf_nfs_gid3 = -1;
static int hf_nfs_offset3 = -1;
static int hf_nfs_count3 = -1;
static int hf_nfs_count3_maxcount = -1;
static int hf_nfs_count3_dircount= -1;

/* NFSv4 */
static int hf_nfs_nfsstat4 = -1;
static int hf_nfs_argop4 = -1;
static int hf_nfs_resop4 = -1;
static int hf_nfs_linktext4 = -1;
static int hf_nfs_tag4 = -1;
static int hf_nfs_component4 = -1;
static int hf_nfs_clientid4 = -1;
static int hf_nfs_ace4 = -1;
static int hf_nfs_recall = -1;
static int hf_nfs_open_claim_type4 = -1;
static int hf_nfs_opentype4 = -1;
static int hf_nfs_limit_by4 = -1;
static int hf_nfs_open_delegation_type4 = -1;
static int hf_nfs_ftype4 = -1;
static int hf_nfs_change_info4_atomic = -1;
static int hf_nfs_open4_share_access = -1;
static int hf_nfs_open4_share_deny = -1;
static int hf_nfs_seqid4 = -1;
static int hf_nfs_lock_seqid4 = -1;
static int hf_nfs_mand_attr = -1;
static int hf_nfs_recc_attr = -1;
static int hf_nfs_time_how4 = -1;
static int hf_nfs_attrlist4 = -1;
static int hf_nfs_fattr4_link_support = -1;
static int hf_nfs_fattr4_symlink_support = -1;
static int hf_nfs_fattr4_named_attr = -1;
static int hf_nfs_fattr4_unique_handles = -1;
static int hf_nfs_fattr4_archive = -1;
static int hf_nfs_fattr4_cansettime = -1;
static int hf_nfs_fattr4_case_insensitive = -1;
static int hf_nfs_fattr4_case_preserving = -1;
static int hf_nfs_fattr4_chown_restricted = -1;
static int hf_nfs_fattr4_hidden = -1;
static int hf_nfs_fattr4_homogeneous = -1;
static int hf_nfs_fattr4_mimetype = -1;
static int hf_nfs_fattr4_no_trunc = -1;
static int hf_nfs_fattr4_system = -1;
static int hf_nfs_fattr4_owner = -1;
static int hf_nfs_fattr4_owner_group = -1;
static int hf_nfs_fattr4_size = -1;
static int hf_nfs_fattr4_aclsupport = -1;
static int hf_nfs_fattr4_lease_time = -1;
static int hf_nfs_fattr4_fileid = -1;
static int hf_nfs_fattr4_files_avail = -1;
static int hf_nfs_fattr4_files_free = -1;
static int hf_nfs_fattr4_files_total = -1;
static int hf_nfs_fattr4_maxfilesize = -1;
static int hf_nfs_fattr4_maxlink = -1;
static int hf_nfs_fattr4_maxname = -1;
static int hf_nfs_fattr4_numlinks = -1;
static int hf_nfs_fattr4_maxread = -1;
static int hf_nfs_fattr4_maxwrite = -1;
static int hf_nfs_fattr4_quota_hard = -1;
static int hf_nfs_fattr4_quota_soft = -1;
static int hf_nfs_fattr4_quota_used = -1;
static int hf_nfs_fattr4_space_avail = -1;
static int hf_nfs_fattr4_space_free = -1;
static int hf_nfs_fattr4_space_total = -1;
static int hf_nfs_fattr4_space_used = -1;
static int hf_nfs_who = -1;
static int hf_nfs_server = -1;
static int hf_nfs_stable_how4 = -1;
static int hf_nfs_dirlist4_eof = -1;
static int hf_nfs_stateid4 = -1;
static int hf_nfs_offset4 = -1;
static int hf_nfs_specdata1 = -1;
static int hf_nfs_specdata2 = -1;
static int hf_nfs_lock_type4 = -1;
static int hf_nfs_reclaim4 = -1;
static int hf_nfs_length4 = -1;
static int hf_nfs_changeid4 = -1;
static int hf_nfs_changeid4_before = -1;
static int hf_nfs_changeid4_after = -1;
static int hf_nfs_nfstime4_seconds = -1;
static int hf_nfs_nfstime4_nseconds = -1;
static int hf_nfs_fsid4_major = -1;
static int hf_nfs_fsid4_minor = -1;
static int hf_nfs_acetype4 = -1;
static int hf_nfs_aceflag4 = -1;
static int hf_nfs_acemask4 = -1;
static int hf_nfs_delegate_type = -1;
static int hf_nfs_secinfo_flavor = -1;
static int hf_nfs_secinfo_arr4 = -1;
static int hf_nfs_num_blocks = -1;
static int hf_nfs_bytes_per_block = -1;
static int hf_nfs_eof = -1;
static int hf_nfs_stateid4_delegate_stateid = -1;
static int hf_nfs_verifier4 = -1;
static int hf_nfs_cookie4 = -1;
static int hf_nfs_cookieverf4 = -1;
static int hf_nfs_cb_program = -1;
static int hf_nfs_cb_location = -1;
static int hf_nfs_recall4 = -1;
static int hf_nfs_filesize = -1;
static int hf_nfs_count4 = -1;
static int hf_nfs_count4_dircount = -1;
static int hf_nfs_count4_maxcount = -1;
static int hf_nfs_minorversion = -1;
static int hf_nfs_open_owner4 = -1;
static int hf_nfs_lock_owner4 = -1;
static int hf_nfs_new_lock_owner = -1;
static int hf_nfs_sec_oid4 = -1;
static int hf_nfs_qop4 = -1;
static int hf_nfs_secinfo_rpcsec_gss_info_service = -1;
static int hf_nfs_attrdircreate = -1;
static int hf_nfs_client_id4_id = -1;
static int hf_nfs_stateid4_other = -1;
static int hf_nfs_lock4_reclaim = -1;
static int hf_nfs_acl4 = -1;
static int hf_nfs_callback_ident = -1;
static int hf_nfs_r_netid = -1;
static int hf_nfs_r_addr = -1;

/* NFSv4.1 */
static int hf_nfs_length4_minlength = -1;
static int hf_nfs_layouttype4 = -1;
static int hf_nfs_layoutlen = -1;
static int hf_nfs_layoutreturn_type4 = -1;
static int hf_nfs_iomode4 = -1;
static int hf_nfs_fldtype4 = -1;
static int hf_nfs_stripetype4 = -1;
static int hf_nfs_mdscommit4 = -1;
static int hf_nfs_stripeunit4 = -1;
static int hf_nfs_newtime4 = -1;
static int hf_nfs_newoffset4 = -1;
static int hf_nfs_layout_avail4 = -1;
static int hf_nfs_newsize4 = -1;
static int hf_nfs_layoutupdate4 = -1;
static int hf_nfs_deviceid4 = -1;
static int hf_nfs_devicenum4 = -1;
static int hf_nfs_deviceidx4 = -1;
static int hf_nfs_layout4 = -1;
static int hf_nfs_stripedevs4 = -1;
static int hf_nfs_devaddr4 = -1;
static int hf_nfs_notifydsop4 = -1;
static int hf_nfs_return_on_close4 = -1;
static int hf_nfs_slotid4 = -1;
static int hf_nfs_sr_status4 = -1;
static int hf_nfs_serverscope4 = -1;
static int hf_nfs_minorid4 = -1;
static int hf_nfs_majorid4 = -1;
static int hf_nfs_padsize4 = -1;
static int hf_nfs_cbrenforce4 = -1;
static int hf_nfs_hashalg4 = -1;
static int hf_nfs_ssvlen4 = -1;
static int hf_nfs_maxreqsize4 = -1;
static int hf_nfs_maxrespsize4 = -1;
static int hf_nfs_maxrespsizecached4 = -1;
static int hf_nfs_maxops4 = -1;
static int hf_nfs_maxreqs4 = -1;
static int hf_nfs_rdmachanattrs4 = -1;
static int hf_nfs_machinename4 = -1;
static int hf_nfs_flavor4 = -1;
static int hf_nfs_stamp4 = -1;
static int hf_nfs_uid4 = -1;
static int hf_nfs_gid4 = -1;
static int hf_nfs_service4 = -1;
static int hf_nfs_sessionid4 = -1;
static int hf_nfs_exch_id_flags4 = -1;
static int hf_nfs_nii_domain4 = -1;
static int hf_nfs_nii_name4 = -1;
static int hf_nfs_create_session_flags4 = -1;
static int hf_nfs_cachethis4 = -1;

/* Hidden field for v2, v3, and v4 status */
static int hf_nfs_nfsstat = -1;

static gint ett_nfs = -1;
static gint ett_nfs_fh_encoding = -1;
static gint ett_nfs_fh_mount = -1;
static gint ett_nfs_fh_file = -1;
static gint ett_nfs_fh_export = -1;
static gint ett_nfsv4_fh_export = -1;
static gint ett_nfsv4_fh_file   = -1;
static gint ett_nfsv4_fh_handle_type = -1;
static gint ett_nfsv4_fh_export_snapgen = -1;
static gint ett_nfsv4_fh_file_flags = -1;
static gint ett_nfs_fh_fsid = -1;
static gint ett_nfs_fh_xfsid = -1;
static gint ett_nfs_fh_fn = -1;
static gint ett_nfs_fh_xfn = -1;
static gint ett_nfs_fh_hp = -1;
static gint ett_nfs_fh_auth = -1;
static gint ett_nfs_fhandle = -1;
static gint ett_nfs_timeval = -1;
static gint ett_nfs_mode = -1;
static gint ett_nfs_fattr = -1;
static gint ett_nfs_sattr = -1;
static gint ett_nfs_diropargs = -1;
static gint ett_nfs_readdir_entry = -1;
static gint ett_nfs_mode3 = -1;
static gint ett_nfs_specdata3 = -1;
static gint ett_nfs_fh3 = -1;
static gint ett_nfs_nfstime3 = -1;
static gint ett_nfs_fattr3 = -1;
static gint ett_nfs_post_op_fh3 = -1;
static gint ett_nfs_sattr3 = -1;
static gint ett_nfs_diropargs3 = -1;
static gint ett_nfs_sattrguard3 = -1;
static gint ett_nfs_set_mode3 = -1;
static gint ett_nfs_set_uid3 = -1;
static gint ett_nfs_set_gid3 = -1;
static gint ett_nfs_set_size3 = -1;
static gint ett_nfs_set_atime = -1;
static gint ett_nfs_set_mtime = -1;
static gint ett_nfs_pre_op_attr = -1;
static gint ett_nfs_post_op_attr = -1;
static gint ett_nfs_wcc_attr = -1;
static gint ett_nfs_wcc_data = -1;
static gint ett_nfs_access = -1;
static gint ett_nfs_fsinfo_properties = -1;
static gint ett_nfs_gxfh3_utlfield = -1;
static gint ett_nfs_gxfh3_sfhfield = -1;
static gint ett_nfs_gxfh3_sfhflags = -1;

/* NFSv4 */
static gint ett_nfs_compound_call4 = -1;
static gint ett_nfs_utf8string = -1;
static gint ett_nfs_argop4 = -1;
static gint ett_nfs_resop4 = -1;
static gint ett_nfs_access4 = -1;
static gint ett_nfs_close4 = -1;
static gint ett_nfs_commit4 = -1;
static gint ett_nfs_create4 = -1;
static gint ett_nfs_delegpurge4 = -1;
static gint ett_nfs_delegreturn4 = -1;
static gint ett_nfs_getattr4 = -1;
static gint ett_nfs_getfh4 = -1;
static gint ett_nfs_link4 = -1;
static gint ett_nfs_lock4 = -1;
static gint ett_nfs_lockt4 = -1;
static gint ett_nfs_locku4 = -1;
static gint ett_nfs_lookup4 = -1;
static gint ett_nfs_lookupp4 = -1;
static gint ett_nfs_nverify4 = -1;
static gint ett_nfs_open4 = -1;
static gint ett_nfs_openattr4 = -1;
static gint ett_nfs_open_confirm4 = -1;
static gint ett_nfs_open_downgrade4 = -1;
static gint ett_nfs_putfh4 = -1;
static gint ett_nfs_putpubfh4 = -1;
static gint ett_nfs_putrootfh4 = -1;
static gint ett_nfs_read4 = -1;
static gint ett_nfs_readdir4 = -1;
static gint ett_nfs_readlink4 = -1;
static gint ett_nfs_remove4 = -1;
static gint ett_nfs_rename4 = -1;
static gint ett_nfs_renew4 = -1;
static gint ett_nfs_restorefh4 = -1;
static gint ett_nfs_savefh4 = -1;
static gint ett_nfs_secinfo4 = -1;
static gint ett_nfs_setattr4 = -1;
static gint ett_nfs_setclientid4 = -1;
static gint ett_nfs_setclientid_confirm4 = -1;
static gint ett_nfs_verify4 = -1;
static gint ett_nfs_write4 = -1;
static gint ett_nfs_release_lockowner4 = -1;
static gint ett_nfs_illegal4 = -1;
static gint ett_nfs_verifier4 = -1;
static gint ett_nfs_opaque = -1;
static gint ett_nfs_dirlist4 = -1;
static gint ett_nfs_pathname4 = -1;
static gint ett_nfs_change_info4 = -1;
static gint ett_nfs_open_delegation4 = -1;
static gint ett_nfs_open_claim4 = -1;
static gint ett_nfs_opentype4 = -1;
static gint ett_nfs_lock_owner4 = -1;
static gint ett_nfs_cb_client4 = -1;
static gint ett_nfs_client_id4 = -1;
static gint ett_nfs_bitmap4 = -1;
static gint ett_nfs_fattr4 = -1;
static gint ett_nfs_fsid4 = -1;
static gint ett_nfs_fs_locations4 = -1;
static gint ett_nfs_fs_location4 = -1;
static gint ett_nfs_open4_result_flags = -1;
static gint ett_nfs_secinfo4_flavor_info = -1;
static gint ett_nfs_stateid4 = -1;
static gint ett_nfs_fattr4_fh_expire_type = -1;
static gint ett_nfs_ace4 = -1;
static gint ett_nfs_clientaddr4 = -1;
static gint ett_nfs_aceflag4 = -1;
static gint ett_nfs_acemask4 = -1;

static gint ett_nfs_layoutget4 = -1;
static gint ett_nfs_layoutcommit4 = -1;
static gint ett_nfs_layoutreturn4 = -1;
static gint ett_nfs_getdevinfo4 = -1;
static gint ett_nfs_getdevlist4 = -1;
static gint ett_nfs_notifyds4 = -1;
static gint ett_nfs_exchange_id4 = -1;
static gint ett_nfs_create_session4 = -1;
static gint ett_nfs_destroy_session4 = -1;
static gint ett_nfs_sequence4 = -1;
static gint ett_nfs_pnfs_create4 = -1;
static gint ett_nfs_slotid4 = -1;
static gint ett_nfs_sr_status4 = -1;
static gint ett_nfs_serverscope4 = -1;
static gint ett_nfs_minorid4 = -1;
static gint ett_nfs_majorid4 = -1;
static gint ett_nfs_persist4 = -1;
static gint ett_nfs_backchan4 = -1;
static gint ett_nfs_rdmamode4 = -1;
static gint ett_nfs_padsize4 = -1;
static gint ett_nfs_cbrenforce4 = -1;
static gint ett_nfs_hashalg4 = -1;
static gint ett_nfs_ssvlen4 = -1;
static gint ett_nfs_maxreqsize4 = -1;
static gint ett_nfs_maxrespsize4 = -1;
static gint ett_nfs_maxrespsizecached4 = -1;
static gint ett_nfs_maxops4 = -1;
static gint ett_nfs_maxreqs4 = -1;
static gint ett_nfs_streamchanattrs4 = -1;
static gint ett_nfs_rdmachanattrs4 = -1;
static gint ett_nfs_machinename4 = -1;
static gint ett_nfs_flavor4 = -1;
static gint ett_nfs_stamp4 = -1;
static gint ett_nfs_uid4 = -1;
static gint ett_nfs_gid4 = -1;
static gint ett_nfs_service4 = -1;
static gint ett_nfs_sessionid4 = -1;

/* what type of fhandles shoudl we dissect as */
static dissector_table_t nfs_fhandle_table;


#define FHT_UNKNOWN		0
#define FHT_SVR4		1
#define FHT_LINUX_KNFSD_LE	2
#define FHT_LINUX_NFSD_LE	3
#define FHT_LINUX_KNFSD_NEW	4
#define FHT_NETAPP		5
#define FHT_NETAPP_V4		6
#define FHT_NETAPP_GX_V3	7


static enum_val_t nfs_fhandle_types[] = {
	{ "unknown",	"Unknown",	FHT_UNKNOWN },
	{ "svr4",	"SVR4",		FHT_SVR4 },
	{ "knfsd_le",	"KNFSD_LE",	FHT_LINUX_KNFSD_LE },
	{ "nfsd_le",	"NFSD_LE",	FHT_LINUX_NFSD_LE },
	{ "knfsd_new",	"KNFSD_NEW",	FHT_LINUX_KNFSD_NEW },
	{ "ontap_v3",	"ONTAP_V3",	FHT_NETAPP },
	{ "ontap_v4",	"ONTAP_V4",	FHT_NETAPP_V4},
	{ "ontap_gx_v3","ONTAP_GX_V3",	FHT_NETAPP_GX_V3},	
	{ NULL, NULL, 0 }
};
/* decode all nfs filehandles as this type */
gint default_nfs_fhandle_type=FHT_UNKNOWN;

/* For dissector helpers which take a "levels" argument to indicate how
 * many expansions up they should populate the expansion items with
 * text to enhance useability, this flag to "levels" specify that the
 * text should also be appended to COL_INFO
 */
#define COL_INFO_LEVEL 0x80000000


/* fhandle displayfilters to match also corresponding request/response
   packet in addition to the one containing the actual filehandle */
gboolean nfs_fhandle_reqrep_matching = FALSE;
static emem_tree_t *nfs_fhandle_frame_table = NULL;


/* file name snooping */
gboolean nfs_file_name_snooping = FALSE;
gboolean nfs_file_name_full_snooping = FALSE;
typedef struct nfs_name_snoop {
	int fh_length;
	unsigned char *fh;
	int name_len;
	unsigned char *name;
	int parent_len;
	unsigned char *parent;
	int full_name_len;
	unsigned char *full_name;
} nfs_name_snoop_t;

typedef struct nfs_name_snoop_key {
	int key;
	int fh_length;
	const unsigned char *fh;
} nfs_name_snoop_key_t;

static GHashTable *nfs_name_snoop_unmatched = NULL;

static GHashTable *nfs_name_snoop_matched = NULL;

static emem_tree_t *nfs_name_snoop_known = NULL;
static emem_tree_t *nfs_file_handles = NULL;

/* This function will store one nfs filehandle in our global tree of
 * filehandles.
 * We store all filehandles we see in this tree so that every unique
 * filehandle is only stored once with a unique pointer.
 * We need to store pointers to filehandles in several of our other
 * structures and this is a way to make sure we dont keep any redundant
 * copiesd around for a specific filehandle.
 *
 * If this is the first time this filehandle has been seen an se block
 * is allocated to store the filehandle in.
 * If this filehandle has already been stored in the tree this function returns
 * a pointer to the original copy.
 */
static nfs_fhandle_data_t *
store_nfs_file_handle(nfs_fhandle_data_t *nfs_fh)
{
	guint32 fhlen;
	emem_tree_key_t fhkey[3];
	nfs_fhandle_data_t *new_nfs_fh;

	fhlen=nfs_fh->len/4;
	fhkey[0].length=1;
	fhkey[0].key=&fhlen;
	fhkey[1].length=fhlen;
	fhkey[1].key=(guint32 *)nfs_fh->fh;
	fhkey[2].length=0;

	new_nfs_fh=se_tree_lookup32_array(nfs_file_handles, &fhkey[0]);
	if(new_nfs_fh){
		return new_nfs_fh;
	}

	new_nfs_fh=se_alloc(sizeof(nfs_fhandle_data_t));
	new_nfs_fh->len=nfs_fh->len;
	new_nfs_fh->fh=se_alloc(sizeof(guint32)*(nfs_fh->len/4));
	memcpy(new_nfs_fh->fh, nfs_fh->fh, nfs_fh->len);
	new_nfs_fh->tvb=tvb_new_real_data(new_nfs_fh->fh, new_nfs_fh->len, new_nfs_fh->len);
	fhlen=nfs_fh->len/4;
	fhkey[0].length=1;
	fhkey[0].key=&fhlen;
	fhkey[1].length=fhlen;
	fhkey[1].key=(guint32 *)nfs_fh->fh;
	fhkey[2].length=0;
	se_tree_insert32_array(nfs_file_handles, &fhkey[0], new_nfs_fh);

	return new_nfs_fh;
}

static gint
nfs_name_snoop_matched_equal(gconstpointer k1, gconstpointer k2)
{
	const nfs_name_snoop_key_t *key1 = (const nfs_name_snoop_key_t *)k1;
	const nfs_name_snoop_key_t *key2 = (const nfs_name_snoop_key_t *)k2;

	return (key1->key==key2->key)
	     &&(key1->fh_length==key2->fh_length)
	     &&(!memcmp(key1->fh, key2->fh, key1->fh_length));
}
static guint
nfs_name_snoop_matched_hash(gconstpointer k)
{
	const nfs_name_snoop_key_t *key = (const nfs_name_snoop_key_t *)k;
	int i;
	guint hash;

	hash=key->key;
	for(i=0;i<key->fh_length;i++)
		hash ^= key->fh[i];

	return hash;
}
static gint
nfs_name_snoop_unmatched_equal(gconstpointer k1, gconstpointer k2)
{
	guint32 key1 = GPOINTER_TO_UINT(k1);
	guint32 key2 = GPOINTER_TO_UINT(k2);

	return key1==key2;
}
static guint
nfs_name_snoop_unmatched_hash(gconstpointer k)
{
	guint32 key = GPOINTER_TO_UINT(k);

	return key;
}
static gboolean
nfs_name_snoop_unmatched_free_all(gpointer key_arg _U_, gpointer value, gpointer user_data _U_)
{
	nfs_name_snoop_t *nns = (nfs_name_snoop_t *)value;

	if(nns->name){
		g_free((gpointer)nns->name);
		nns->name=NULL;
		nns->name_len=0;
	}
	if(nns->full_name){
		g_free((gpointer)nns->full_name);
		nns->full_name=NULL;
		nns->full_name_len=0;
	}
	if(nns->parent){
		g_free((gpointer)nns->parent);
		nns->parent=NULL;
		nns->parent_len=0;
	}
	if(nns->fh){
		g_free((gpointer)nns->fh);
		nns->fh=NULL;
		nns->fh_length=0;
	}
	return TRUE;
}

static void
nfs_name_snoop_init(void)
{
	if (nfs_name_snoop_unmatched != NULL) {
		g_hash_table_foreach_remove(nfs_name_snoop_unmatched,
				nfs_name_snoop_unmatched_free_all, NULL);
	} else {
		/* The fragment table does not exist. Create it */
		nfs_name_snoop_unmatched=g_hash_table_new(nfs_name_snoop_unmatched_hash,
			nfs_name_snoop_unmatched_equal);
	}
	if (nfs_name_snoop_matched != NULL) {
		g_hash_table_foreach_remove(nfs_name_snoop_matched,
				nfs_name_snoop_unmatched_free_all, NULL);
	} else {
		/* The fragment table does not exist. Create it */
		nfs_name_snoop_matched=g_hash_table_new(nfs_name_snoop_matched_hash,
			nfs_name_snoop_matched_equal);
	}
}

void
nfs_name_snoop_add_name(int xid, tvbuff_t *tvb, int name_offset, int name_len, int parent_offset, int parent_len, unsigned char *name)
{
	nfs_name_snoop_t *nns, *old_nns;
	const unsigned char *ptr=NULL;

	/* filter out all '.' and '..' names */
	if(!name){
		ptr=(const unsigned char *)tvb_get_ptr(tvb, name_offset, name_len);
		if(ptr[0]=='.'){
			if(ptr[1]==0){
				return;
			}
			if(ptr[1]=='.'){
				if(ptr[2]==0){
					return;
				}
			}
		}
	}

	nns=se_alloc(sizeof(nfs_name_snoop_t));

	nns->fh_length=0;
	nns->fh=NULL;

	if(parent_len){
		nns->parent_len=parent_len;
		nns->parent=tvb_memdup(tvb, parent_offset, parent_len);
	} else {
		nns->parent_len=0;
		nns->parent=NULL;
	}

	if(name){
		nns->name_len=strlen(name);
		nns->name=g_strdup(name);
	} else {
		nns->name_len=name_len;
		nns->name=g_malloc(name_len+1);
		memcpy(nns->name, ptr, name_len);
	}
	nns->name[nns->name_len]=0;

	nns->full_name_len=0;
	nns->full_name=NULL;

	/* remove any old entry for this */
	old_nns=g_hash_table_lookup(nfs_name_snoop_unmatched, GINT_TO_POINTER(xid));
	if(old_nns){
		/* if we haven't seen the reply yet, then there are no
		   matched entries for it, thus we can dealloc the arrays*/
		if(!old_nns->fh){
			g_free(old_nns->name);
			old_nns->name=NULL;
			old_nns->name_len=0;

			g_free(old_nns->parent);
			old_nns->parent=NULL;
			old_nns->parent_len=0;
		}
		g_hash_table_remove(nfs_name_snoop_unmatched, GINT_TO_POINTER(xid));
	}

	g_hash_table_insert(nfs_name_snoop_unmatched, GINT_TO_POINTER(xid), nns);
}

static void
nfs_name_snoop_add_fh(int xid, tvbuff_t *tvb, int fh_offset, int fh_length)
{
	unsigned char *fh;
	nfs_name_snoop_t *nns, *old_nns;
	nfs_name_snoop_key_t *key;

	/* find which request we correspond to */
	nns=g_hash_table_lookup(nfs_name_snoop_unmatched, GINT_TO_POINTER(xid));
	if(!nns){
		/* oops couldnt find matching request, bail out */
		return;
	}

	/* if we have already seen this response earlier */
	if(nns->fh){
		return;
	}

	/* oki, we have a new entry */
	fh=tvb_memdup(tvb, fh_offset, fh_length);
	nns->fh=fh;
	nns->fh_length=fh_length;

	key=se_alloc(sizeof(nfs_name_snoop_key_t));
	key->key=0;
	key->fh_length=nns->fh_length;
	key->fh    =nns->fh;

	/* already have something matched for this fh, remove it from
	   the table */
	old_nns=g_hash_table_lookup(nfs_name_snoop_matched, key);
	if(old_nns){
		g_hash_table_remove(nfs_name_snoop_matched, key);
	}

	g_hash_table_remove(nfs_name_snoop_unmatched, GINT_TO_POINTER(xid));
	g_hash_table_insert(nfs_name_snoop_matched, key, nns);
}

static void
nfs_full_name_snoop(nfs_name_snoop_t *nns, int *len, unsigned char **name, unsigned char **pos)
{
	nfs_name_snoop_t *parent_nns = NULL;
	nfs_name_snoop_key_t key;

	/* check if the nns component ends with a '/' else we just allocate
	   an extra byte to len to accommodate for it later */
	if(nns->name[nns->name_len-1]!='/'){
		(*len)++;
	}

	(*len) += nns->name_len;

	if(nns->parent==NULL){
		*name = g_malloc((*len)+1);
		*pos = *name;

		*pos += g_snprintf(*pos, (*len)+1, "%s", nns->name);
		return;
	}

	key.key=0;
	key.fh_length=nns->parent_len;
	key.fh=nns->parent;

	parent_nns=g_hash_table_lookup(nfs_name_snoop_matched, &key);

	if(parent_nns){
		nfs_full_name_snoop(parent_nns, len, name, pos);
		if(*name){
			/* make sure components are '/' separated */
			*pos += g_snprintf(*pos, (*len)+1, "%s%s", ((*pos)[-1]!='/')?"/":"", nns->name);
		}
		return;
	}

	return;
}

static void
nfs_name_snoop_fh(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int fh_offset, int fh_length, gboolean hidden)
{
	nfs_name_snoop_key_t key;
	nfs_name_snoop_t *nns = NULL;

	/* if this is a new packet, see if we can register the mapping */
	if(!pinfo->fd->flags.visited){
		key.key=0;
		key.fh_length=fh_length;
		key.fh=(const unsigned char *)tvb_get_ptr(tvb, fh_offset, fh_length);

		nns=g_hash_table_lookup(nfs_name_snoop_matched, &key);
		if(nns){
			guint32 fhlen;
			emem_tree_key_t fhkey[3];

			fhlen=nns->fh_length;
			fhkey[0].length=1;
			fhkey[0].key=&fhlen;
			fhkey[1].length=fhlen/4;
			fhkey[1].key=(guint32 *)nns->fh;
			fhkey[2].length=0;
			se_tree_insert32_array(nfs_name_snoop_known, &fhkey[0], nns);

			if(nfs_file_name_full_snooping){
				unsigned char *name=NULL, *pos=NULL;
				int len=0;

				nfs_full_name_snoop(nns, &len, &name, &pos);
				if(name){
					nns->full_name=name;
					nns->full_name_len=len;
				}
			}
		}
	}

	/* see if we know this mapping */
	if(!nns){
		guint32 fhlen;
		emem_tree_key_t fhkey[3];

		fhlen=fh_length;
		fhkey[0].length=1;
		fhkey[0].key=&fhlen;
		fhkey[1].length=fhlen/4;
		fhkey[1].key=(guint32 *)tvb_get_ptr(tvb, fh_offset, fh_length);
		fhkey[2].length=0;

		nns=se_tree_lookup32_array(nfs_name_snoop_known, &fhkey[0]);
	}

	/* if we know the mapping, print the filename */
	if(nns){
		proto_item *fh_item;

		if(hidden){
			fh_item=proto_tree_add_string_hidden(tree, hf_nfs_name, tvb,
				fh_offset, 0, nns->name);
		}else {
			fh_item=proto_tree_add_string_format(tree, hf_nfs_name, tvb,
				fh_offset, 0, nns->name, "Name: %s", nns->name);
		}
		PROTO_ITEM_SET_GENERATED(fh_item);

		if(nns->full_name){
			if(hidden){
				fh_item=proto_tree_add_string_hidden(tree, hf_nfs_full_name, tvb,
					fh_offset, 0, nns->name);
			} else {
				fh_item=proto_tree_add_string_format(tree, hf_nfs_full_name, tvb,
					fh_offset, 0, nns->name, "Full Name: %s", nns->full_name);
			}
			PROTO_ITEM_SET_GENERATED(fh_item);
		}
	}
}

/* file handle dissection */

static const value_string names_fhtype[] =
{
	{	FHT_UNKNOWN,		"unknown"				},
	{	FHT_SVR4,		"System V R4"				},
	{	FHT_LINUX_KNFSD_LE,	"Linux knfsd (little-endian)"		},
	{	FHT_LINUX_NFSD_LE,	"Linux user-land nfsd (little-endian)"	},
	{	FHT_LINUX_KNFSD_NEW,	"Linux knfsd (new)"			},
	{	FHT_NETAPP,		"ONTAP 7G nfs v3 file handle"		},
	{	FHT_NETAPP_V4,	 	"ONTAP 7G nfs v4 file handle"		},
	{	FHT_NETAPP_GX_V3,	"ONTAP GX nfs v3 file handle"		},
	{	0,			NULL					}
};


/* SVR4: checked with ReliantUNIX (5.43, 5.44, 5.45) */

static void
dissect_fhandle_data_SVR4(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	guint32 nof=0;

	/* file system id */
	{
	guint32 fsid_O;
	guint32 fsid_L;
	guint32 temp;
	guint32 fsid_major;
	guint32 fsid_minor;

	fsid_O = nof;
	fsid_L = 4;
	temp = tvb_get_ntohl(tvb, fsid_O);
	fsid_major = ( temp>>18 ) &  0x3fff; /* 14 bits */
	fsid_minor = ( temp     ) & 0x3ffff; /* 18 bits */
	if (tree) {
		proto_item* fsid_item = NULL;
		proto_tree* fsid_tree = NULL;

		fsid_item = proto_tree_add_text(tree, tvb,
			fsid_O, fsid_L,
			"file system ID: %d,%d", fsid_major, fsid_minor);
		if (fsid_item) {
			fsid_tree = proto_item_add_subtree(fsid_item,
					ett_nfs_fh_fsid);
			proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_major,
				tvb, fsid_O,   2, fsid_major);
			proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_minor,
				tvb, fsid_O+1, 3, fsid_minor);
		}
	}
	nof = fsid_O + fsid_L;
	}

	/* file system type */
	{
	guint32 fstype_O;
	guint32 fstype_L;
	guint32 fstype;

	fstype_O = nof;
	fstype_L = 4;
	fstype = tvb_get_ntohl(tvb, fstype_O);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_fh_fstype, tvb,
			fstype_O, fstype_L, fstype);
	}
	nof = fstype_O + fstype_L;
	}

	/* file number */
	{
	guint32 fn_O;
	guint32 fn_len_O;
	guint32 fn_len_L;
	guint32 fn_len;
	guint32 fn_data_O;
	guint32 fn_data_inode_O;
	guint32 fn_data_inode_L;
	guint32 inode;
	guint32 fn_data_gen_O;
	guint32 fn_data_gen_L;
	guint32 gen;
	guint32 fn_L;

	fn_O = nof;
	fn_len_O = fn_O;
	fn_len_L = 2;
	fn_len = tvb_get_ntohs(tvb, fn_len_O);
	fn_data_O = fn_O + fn_len_L;
	fn_data_inode_O = fn_data_O + 2;
	fn_data_inode_L = 4;
	inode = tvb_get_ntohl(tvb, fn_data_inode_O);
	fn_data_gen_O = fn_data_inode_O + fn_data_inode_L;
	fn_data_gen_L = 4;
	gen = tvb_get_ntohl(tvb, fn_data_gen_O);
	fn_L = fn_len_L + fn_len;
	if (tree) {
		proto_item* fn_item = NULL;
		proto_tree* fn_tree = NULL;

		fn_item = proto_tree_add_uint(tree, hf_nfs_fh_fn, tvb,
			fn_O, fn_L, inode);
		if (fn_item) {
			fn_tree = proto_item_add_subtree(fn_item,
					ett_nfs_fh_fn);
			proto_tree_add_uint(fn_tree, hf_nfs_fh_fn_len,
				tvb, fn_len_O, fn_len_L, fn_len);
			proto_tree_add_uint(fn_tree, hf_nfs_fh_fn_inode,
				tvb, fn_data_inode_O, fn_data_inode_L, inode);
			proto_tree_add_uint(fn_tree, hf_nfs_fh_fn_generation,
				tvb, fn_data_gen_O, fn_data_gen_L, gen);
		}
	}
	nof = fn_O + fn_len_L + fn_len;
	}

	/* exported file number */
	{
	guint32 xfn_O;
	guint32 xfn_len_O;
	guint32 xfn_len_L;
	guint32 xfn_len;
	guint32 xfn_data_O;
	guint32 xfn_data_inode_O;
	guint32 xfn_data_inode_L;
	guint32 xinode;
	guint32 xfn_data_gen_O;
	guint32 xfn_data_gen_L;
	guint32 xgen;
	guint32 xfn_L;

	xfn_O = nof;
	xfn_len_O = xfn_O;
	xfn_len_L = 2;
	xfn_len = tvb_get_ntohs(tvb, xfn_len_O);
	xfn_data_O = xfn_O + xfn_len_L;
	xfn_data_inode_O = xfn_data_O + 2;
	xfn_data_inode_L = 4;
	xinode = tvb_get_ntohl(tvb, xfn_data_inode_O);
	xfn_data_gen_O = xfn_data_inode_O + xfn_data_inode_L;
	xfn_data_gen_L = 4;
	xgen = tvb_get_ntohl(tvb, xfn_data_gen_O);
	xfn_L = xfn_len_L + xfn_len;
	if (tree) {
		proto_item* xfn_item = NULL;
		proto_tree* xfn_tree = NULL;

		xfn_item = proto_tree_add_uint(tree, hf_nfs_fh_xfn, tvb,
			xfn_O, xfn_L, xinode);
		if (xfn_item) {
			xfn_tree = proto_item_add_subtree(xfn_item,
					ett_nfs_fh_xfn);
			proto_tree_add_uint(xfn_tree, hf_nfs_fh_xfn_len,
				tvb, xfn_len_O, xfn_len_L, xfn_len);
			proto_tree_add_uint(xfn_tree, hf_nfs_fh_xfn_inode,
				tvb, xfn_data_inode_O, xfn_data_inode_L, xinode);
			proto_tree_add_uint(xfn_tree, hf_nfs_fh_xfn_generation,
				tvb, xfn_data_gen_O, xfn_data_gen_L, xgen);
		}
	}
	}
}


/* Checked with RedHat Linux 6.2 (kernel 2.2.14 knfsd) */

static void
dissect_fhandle_data_LINUX_KNFSD_LE(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int offset=0;
	guint32 dentry;
	guint32 inode;
	guint32 dirinode;
	guint32 temp;
	guint32 fsid_major;
	guint32 fsid_minor;
	guint32 xfsid_major;
	guint32 xfsid_minor;
	guint32 xinode;
	guint32 gen;

	dentry   = tvb_get_letohl(tvb, offset+0);
	inode    = tvb_get_letohl(tvb, offset+4);
	dirinode = tvb_get_letohl(tvb, offset+8);
	temp     = tvb_get_letohs (tvb,offset+12);
	fsid_major = (temp >> 8) & 0xff;
	fsid_minor = (temp     ) & 0xff;
	temp     = tvb_get_letohs(tvb,offset+16);
	xfsid_major = (temp >> 8) & 0xff;
	xfsid_minor = (temp     ) & 0xff;
	xinode   = tvb_get_letohl(tvb,offset+20);
	gen      = tvb_get_letohl(tvb,offset+24);

	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_fh_dentry,
			tvb, offset+0, 4, dentry);
		proto_tree_add_uint(tree, hf_nfs_fh_fn_inode,
			tvb, offset+4, 4, inode);
		proto_tree_add_uint(tree, hf_nfs_fh_dirinode,
			tvb, offset+8, 4, dirinode);

		/* file system id (device) */
		{
		proto_item* fsid_item = NULL;
		proto_tree* fsid_tree = NULL;

		fsid_item = proto_tree_add_text(tree, tvb,
			offset+12, 4,
			"file system ID: %d,%d", fsid_major, fsid_minor);
		if (fsid_item) {
			fsid_tree = proto_item_add_subtree(fsid_item,
					ett_nfs_fh_fsid);
			proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_major,
				tvb, offset+13, 1, fsid_major);
			proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_minor,
				tvb, offset+12, 1, fsid_minor);
		}
		}

		/* exported file system id (device) */
		{
		proto_item* xfsid_item = NULL;
		proto_tree* xfsid_tree = NULL;

		xfsid_item = proto_tree_add_text(tree, tvb,
			offset+16, 4,
			"exported file system ID: %d,%d", xfsid_major, xfsid_minor);
		if (xfsid_item) {
			xfsid_tree = proto_item_add_subtree(xfsid_item,
					ett_nfs_fh_xfsid);
			proto_tree_add_uint(xfsid_tree, hf_nfs_fh_xfsid_major,
				tvb, offset+17, 1, xfsid_major);
			proto_tree_add_uint(xfsid_tree, hf_nfs_fh_xfsid_minor,
				tvb, offset+16, 1, xfsid_minor);
		}
		}

		proto_tree_add_uint(tree, hf_nfs_fh_xfn_inode,
			tvb, offset+20, 4, xinode);
		proto_tree_add_uint(tree, hf_nfs_fh_fn_generation,
			tvb, offset+24, 4, gen);
	}
}


/* Checked with RedHat Linux 5.2 (nfs-server 2.2beta47 user-land nfsd) */

static void
dissect_fhandle_data_LINUX_NFSD_LE(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int offset=0;

	/* pseudo inode */
	{
	guint32 pinode;
	pinode   = tvb_get_letohl(tvb, offset+0);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_fh_pinode,
			tvb, offset+0, 4, pinode);
	}
	}

	/* hash path */
	{
	guint32 hashlen;

	hashlen  = tvb_get_guint8(tvb, offset+4);
	if (tree) {
		proto_item* hash_item = NULL;
		proto_tree* hash_tree = NULL;

		hash_item = proto_tree_add_text(tree, tvb, offset+4,
				hashlen + 1,
				"hash path: %s",
				tvb_bytes_to_str(tvb,offset+5,hashlen));
		if (hash_item) {
			hash_tree = proto_item_add_subtree(hash_item,
					ett_nfs_fh_hp);
			if (hash_tree) {
		 		proto_tree_add_uint(hash_tree,
					hf_nfs_fh_hp_len, tvb, offset+4, 1,
					hashlen);
				proto_tree_add_text(hash_tree, tvb, offset+5,
					hashlen,
					"key: %s",
					tvb_bytes_to_str(tvb,offset+5,hashlen));
			}
		}
	}
	}
}


static void
dissect_fhandle_data_NETAPP(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int offset=0;

	if (tree) {
		guint32 mount = tvb_get_letohl(tvb, offset + 0);
		guint32 mount_gen = tvb_get_letohl(tvb, offset + 4);
		guint16 flags = tvb_get_letohs(tvb, offset + 8);
		guint8 snapid = tvb_get_guint8(tvb, offset + 10);
		guint8 unused = tvb_get_guint8(tvb, offset + 11);
		guint32 inum = tvb_get_ntohl(tvb, offset + 12);
		guint32 generation = tvb_get_letohl(tvb, offset + 16);
		guint32 fsid = tvb_get_letohl(tvb, offset + 20);
		guint32 export = tvb_get_letohl(tvb, offset + 24);
		guint32 export_snapgen = tvb_get_letohl(tvb, offset + 28);
		
		proto_item *item;
		proto_tree *subtree;
		char *flag_string;
		const char *strings[] = { " MNT_PNT", " SNAPDIR", " SNAPDIR_ENT",
				    " EMPTY", " VBN_ACCESS", " MULTIVOLUME",
				    " METADATA" };
		guint16 bit = sizeof(strings) / sizeof(strings[0]);

		flag_string=ep_alloc(512);
		flag_string[0]=0;
		while (bit--)
			if (flags & (1<<bit))
				strcat(flag_string, strings[bit]);
		item = proto_tree_add_text(tree, tvb, offset + 0, 8,
					   "mount (inode %u)", mount);
		subtree = proto_item_add_subtree(item, ett_nfs_fh_mount);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_mount_fileid,
					   tvb, offset + 0, 4, mount);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_mount_generation,
					   tvb, offset + 4, 4, mount_gen);
		item = proto_tree_add_text(tree, tvb, offset + 8, 16,
					   "file (inode %u)", inum);
		subtree = proto_item_add_subtree(item, ett_nfs_fh_file);
		item = proto_tree_add_uint_format(subtree, hf_nfs_fh_flags,
						  tvb, offset + 8, 2, flags,
						  "Flags: %#02x%s", flags,
						  flag_string);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_snapid, tvb,
					   offset + 10, 1, snapid);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_unused, tvb,
					   offset + 11, 1, unused);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_fileid, tvb,
					   offset + 12, 4, inum);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_generation, tvb,
					   offset + 16, 4, generation);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_fsid, tvb,
					   offset + 20, 4, fsid);
		item = proto_tree_add_text(tree, tvb, offset + 24, 8,
					   "export (inode %u)", export);
		subtree = proto_item_add_subtree(item, ett_nfs_fh_export);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_export_fileid,
					   tvb, offset + 24, 4, export);
		item = proto_tree_add_uint(subtree,
					   hf_nfs_fh_export_generation,
					   tvb, offset + 28, 3,
					   export_snapgen & 0xffffff);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_export_snapid,
					   tvb, offset + 31, 1,
					   export_snapgen >> 24);
	}
}

const value_string netapp_file_flag_vals[] =  {
						{ 0x0000,	"Not set"},
						{ 0x0001,	"Set"},
						{ 0,		NULL}
					       };

static void
dissect_fhandle_data_NETAPP_V4(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int offset=0;
	proto_item *item = NULL;
	proto_tree *subtree = NULL;
	guint8  snapid, unused;
	guint16 flags;
	guint32 fileid, snapgen, generation, fsid;
	guint32 handle_type = tvb_get_ntohl(tvb, offset + 24);
	guint32 inum = tvb_get_ntohl(tvb, offset + 12);
		
	char *handle_string=NULL;
	const char *handle_type_strings [] = { "NORMAL",
					       "UNEXP",
					       "VOLDIR",
					       "ROOT",
					       "ABSENT",
					       "INVALID"
					     };

	char *flag_string;
	const char *strings[] = { " MNT_PNT", 
				  " SNAPDIR", 
				  " SNAPDIR_ENT",
				  " EMPTY", 
				  " VBN_ACCESS", 
				  " MULTIVOLUME",
				  " METADATA",
				  " ORPHAN",
				  " FOSTER",
				  " NAMED_ATTR",
				  " EXP_SNAPDIR",
				  " VFILER",
				  " NS_AGGR",
				  " STRIPED",
				  " NS_PRIVATE",
				  " NEXT_GEN_FH"
				};
	guint16 bit = sizeof(strings) / sizeof(strings[0]);
	proto_tree *flag_tree = NULL;

	flag_string=ep_alloc(512);
	flag_string[0]=0;

	if(tree){
		if( handle_type !=0 && handle_type <= 255) {
			fileid = tvb_get_ntohl(tvb, offset + 0);
			snapgen = tvb_get_ntohl(tvb, offset + 4);
			flags = tvb_get_ntohs(tvb, offset + 8);
			snapid = tvb_get_guint8(tvb, offset + 10);
			unused = tvb_get_guint8(tvb, offset + 11);
			generation = tvb_get_ntohl(tvb, offset + 16);
			fsid = tvb_get_ntohl(tvb, offset + 20);
		} else {
			fileid = tvb_get_letohl(tvb, offset + 0);
			snapgen = tvb_get_letohl(tvb, offset + 4);
			flags = tvb_get_letohs(tvb, offset + 8);
			snapid = tvb_get_guint8(tvb, offset + 10);
			unused = tvb_get_guint8(tvb, offset + 11);
			generation = tvb_get_letohl(tvb, offset + 16);
			fsid = tvb_get_letohl(tvb, offset + 20);
			handle_type = tvb_get_letohl(tvb, offset + 24);
		}

		if(handle_type <= 4) {
			handle_string=handle_type_strings[handle_type];
		} else {
			handle_string=handle_type_strings[5];
		}

		while (bit--) {
			if (flags & (1<<bit)) {
				strcat(flag_string, strings[bit]);
			}
		}

		item = proto_tree_add_text(tree, tvb, offset + 0, 8, "export (inode %u)", fileid);
		subtree = proto_item_add_subtree(item, ett_nfsv4_fh_export);
		
		item = proto_tree_add_uint(subtree, hf_nfs_fh_export_fileid,
					   tvb, offset + 0, 4, fileid);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_export_generation,
					   tvb, offset + 4, 4, snapgen);
		item = proto_tree_add_text(tree, tvb, offset + 8, 16, "file (inode %u)", inum);
		subtree = proto_item_add_subtree(item, ett_nfsv4_fh_file);
		item = proto_tree_add_uint_format(subtree, hf_nfs_fh_flags,
						  tvb, offset + 8, 2, flags,
						  "Flags: %#02x%s", flags,
						  flag_string);
		flag_tree = proto_item_add_subtree(item, ett_nfsv4_fh_file_flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_mntpoint, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_snapdir, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_snapdir_ent, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_empty, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_vbn_access, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_multivolume, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_metadata, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_orphan, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_foster, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_named_attr, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_exp_snapdir, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_vfiler, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_aggr, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_striped, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_private, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(flag_tree, hf_nfs_fh_file_flag_next_gen, tvb, offset+8, 2, flags);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_snapid, tvb,
					   offset + 10, 1, snapid);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_unused, tvb,
					   offset + 11, 1, unused);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_fileid, tvb,
					   offset + 12, 4, inum);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_generation, tvb,
					   offset + 16, 4, generation);
		item = proto_tree_add_uint(subtree, hf_nfs_fh_fsid, tvb,
					   offset + 20, 4, fsid);
		item = proto_tree_add_uint_format(tree, hf_nfs_fh_handle_type,
						  tvb, offset+24, 4, handle_type,
						  "Handle type: %s(%#02x)", handle_string, handle_type);
	}
}

#define NETAPP_GX_FH3_LENGTH		44
#define NFS3GX_FH_TREE_MASK		0x80
#define NFS3GX_FH_JUN_MASK		0x40
#define NFS3GX_FH_VER_MASK		0x3F
#define SPINNP_FH_FLAG_RESV1            0x80
#define SPINNP_FH_FLAG_RESV2            0x40
#define SPINNP_FH_FLAG_ONTAP_MASK	0x20
#define SPINNP_FH_FLAG_STRIPED_MASK	0x10
#define SPINNP_FH_FLAG_EMPTY_MASK	0x08
#define SPINNP_FH_FLAG_SNAPDIR_ENT_MASK 0x04
#define SPINNP_FH_FLAG_SNAPDIR_MASK	0x02
#define SPINNP_FH_FLAG_STREAMDIR_MASK	0x01

static void
dissect_fhandle_data_NETAPP_GX_v3(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
    proto_tree *field_tree;
    proto_item *tf;
    guint16     cluster_id;
    guint16     epoch;
    guint32     export_id;
    guint32     export_uid;
    guint8      flags;
    guint8      reserved;
    guint32     local_dsid;
    guint32     spinfile_id;
    guint32     spinfile_uid;
    guint8      utility;
    guint8      volcnt;
    guint32	offset = 0;
	 
    if (tree) {
        /* = utility = */		 
        utility = tvb_get_guint8(tvb, offset);	
	tf = proto_tree_add_uint_format(tree, hf_gxfh3_utlfield, tvb,
                                        offset, 1, utility,
		                        "  utility: 0x%02x",utility);


        field_tree = proto_item_add_subtree(tf, ett_nfs_gxfh3_utlfield);
        if (utility & NFS3GX_FH_TREE_MASK) {
		proto_tree_add_uint(field_tree, hf_gxfh3_utlfield_tree_w, tvb, 
	                            offset, 1, utility);
	} 
	else {
		proto_tree_add_uint(field_tree, hf_gxfh3_utlfield_tree_r, tvb, 
	                            offset, 1, utility);
	}				                    	
        if (utility & NFS3GX_FH_JUN_MASK) {
		proto_tree_add_uint(field_tree, hf_gxfh3_utlfield_jun, tvb, 
	                            offset, 1, utility);
	} 
	else {
		proto_tree_add_uint(field_tree, hf_gxfh3_utlfield_jun_not, tvb, 
	                            offset, 1, utility);
	}				                    	
	proto_tree_add_uint(field_tree, hf_gxfh3_utlfield_ver, tvb, 
	                            offset, 1, utility);
	                            
        /* = volume count== */		 
        volcnt = tvb_get_guint8(tvb, offset+1);	
	proto_tree_add_uint_format(tree, hf_gxfh3_volcnt, tvb,
                                   offset+1, 1, volcnt,
		                   "  volume count: 0x%02x (%d)", volcnt, volcnt);
        /* = epoch = */		 
        epoch = tvb_get_letohs(tvb, offset+2);	
	proto_tree_add_uint_format(tree, hf_gxfh3_epoch, tvb,
                                   offset+2, 2, epoch,
		                   "  epoch: 0x%04x (%u)", epoch, epoch);       
        /* = spin file handle = */		 
        local_dsid   = tvb_get_letohl(tvb, offset+4); 
        cluster_id   = tvb_get_letohs(tvb, offset+8); 
        reserved     = tvb_get_guint8(tvb, offset+10);	
        flags        = tvb_get_guint8(tvb, offset+11);	
        spinfile_id  = tvb_get_letohl(tvb, offset+12); 
        spinfile_uid = tvb_get_letohl(tvb, offset+16); 
        
	tf = proto_tree_add_text(tree, tvb, offset+4, 16, 
	                         "  spin file handle");
        field_tree = proto_item_add_subtree(tf, ett_nfs_gxfh3_sfhfield);
        
	proto_tree_add_uint_format(field_tree, hf_gxfh3_ldsid, tvb, 
	                           offset+4, 4, local_dsid,
	                           " local dsid: 0x%08x (%u)", local_dsid, local_dsid);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_cid, tvb,
                                   offset+8, 2, cluster_id,
 	                           " cluster id: 0x%04x (%u)", cluster_id, cluster_id);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_resv, tvb,
                                   offset+10, 1, reserved,
 	                           " reserved: 0x%02x (%u)", reserved, reserved);
 	                           
 	                           
       	tf = proto_tree_add_uint_format(field_tree, hf_gxfh3_sfhflags, tvb,
                                        offset+11, 1, utility,
		                        " flags: 0x%02x", flags);
        field_tree = proto_item_add_subtree(tf, ett_nfs_gxfh3_sfhflags);
	proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_resv1, tvb, 
	                    offset+11, 1, flags);
	proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_resv2, tvb, 
	                    offset+11, 1, flags);
	                    
        if (flags & SPINNP_FH_FLAG_ONTAP_MASK) {
		proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_ontap7G, tvb, 
	                            offset+11, 1, flags);
	} 
	else {
		proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_ontapGX, tvb, 
	                            offset+11, 1, flags);
	}				                    	
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_striped, tvb, 
	                            offset+11, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_empty, tvb, 
	                            offset+11, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_snapdirent, tvb, 
	                            offset+11, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_snapdir, tvb, 
	                            offset+11, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_streamdir, tvb, 
	                            offset+11, 1, flags);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_spinfid, tvb, 
	                           offset+12, 4, spinfile_id,
	                           "spin file id: 0x%08x (%u)", spinfile_id, spinfile_id);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_spinfuid, tvb, 
	                           offset+16, 4, spinfile_id,
	                           "spin file unique id: 0x%08x (%u)", spinfile_uid, spinfile_uid);
        
        /* = spin file handle (mount point) = */		 
        local_dsid   = tvb_get_letohl(tvb, offset+20); 
        cluster_id   = tvb_get_letohs(tvb, offset+24); 
        reserved     = tvb_get_guint8(tvb, offset+26);	
        flags        = tvb_get_guint8(tvb, offset+27);	
        spinfile_id  = tvb_get_letohl(tvb, offset+28); 
        spinfile_uid = tvb_get_letohl(tvb, offset+32); 
        
	tf = proto_tree_add_text(tree, tvb, offset+20, 16, 
	                         "  spin (mount point) file handle");
        field_tree = proto_item_add_subtree(tf, ett_nfs_gxfh3_sfhfield);
        
	proto_tree_add_uint_format(field_tree, hf_gxfh3_ldsid, tvb, 
	                           offset+20, 4, local_dsid,
	                           " local dsid: 0x%08x (%u)", local_dsid, local_dsid);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_cid, tvb,
                                   offset+24, 2, cluster_id,
 	                           " cluster id: 0x%04x (%u)", cluster_id, cluster_id);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_resv, tvb,
                                   offset+26, 1, reserved,
 	                           " reserved: 0x%02x (%u)", reserved, reserved);
 	                           
 	                           
       	tf = proto_tree_add_uint_format(field_tree, hf_gxfh3_sfhflags, tvb,
                                        offset+27, 1, utility,
		                        " flags: 0x%02x", flags);
        field_tree = proto_item_add_subtree(tf, ett_nfs_gxfh3_sfhflags);
	proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_resv1, tvb, 
	                    offset+27, 1, flags);
	proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_resv2, tvb, 
	                    offset+27, 1, flags);
	                    
        if (flags & SPINNP_FH_FLAG_ONTAP_MASK) {
		proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_ontap7G, tvb, 
	                            offset+27, 1, flags);
	} 
	else {
		proto_tree_add_uint(field_tree, hf_gxfh3_sfhflags_ontapGX, tvb, 
	                            offset+27, 1, flags);
	}				                    	
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_striped, tvb, 
	                            offset+27, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_empty, tvb, 
	                            offset+27, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_snapdirent, tvb, 
	                            offset+27, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_snapdir, tvb, 
	                            offset+27, 1, flags);
	proto_tree_add_boolean(field_tree, hf_gxfh3_sfhflags_streamdir, tvb, 
	                            offset+27, 1, flags);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_spinfid, tvb, 
	                           offset+28, 4, spinfile_id,
	                           "spin file id: 0x%08x (%u)", spinfile_id, spinfile_id);
	proto_tree_add_uint_format(field_tree, hf_gxfh3_spinfuid, tvb, 
	                           offset+32, 4, spinfile_id,
	                           "spin file unique id: 0x%08x (%u)", spinfile_uid, spinfile_uid);    
        /* = export point id  = */		 
        export_id  = tvb_get_letohl(tvb, offset+36); 
        export_uid = tvb_get_letohl(tvb, offset+40); 
	proto_tree_add_uint_format(tree, hf_gxfh3_exportptid, tvb, 
	                           offset+36, 4, spinfile_id,
	                           "  export point id: 0x%08x (%u)", export_id, export_id);
        /* = export point unique id  = */		 
        export_uid = tvb_get_letohl(tvb, offset+40); 
	proto_tree_add_uint_format(tree, hf_gxfh3_exportptuid, tvb, 
	                           offset+40, 4, spinfile_id,
	                           "  export point unique id: 0x%08x (%u)", export_uid, export_uid);
	                           
    }  /* end of (tree) */
}

/* Checked with SuSE 7.1 (kernel 2.4.0 knfsd) */
/* read linux-2.4.5/include/linux/nfsd/nfsfh.h for more details */

#define AUTH_TYPE_NONE 0
static const value_string auth_type_names[] = {
	{	AUTH_TYPE_NONE,				"no authentication"		},
	{0,NULL}
};

#define FSID_TYPE_MAJOR_MINOR_INODE 0
static const value_string fsid_type_names[] = {
	{	FSID_TYPE_MAJOR_MINOR_INODE,		"major/minor/inode"		},
	{0,NULL}
};

#define FILEID_TYPE_ROOT			0
#define FILEID_TYPE_INODE_GENERATION		1
#define FILEID_TYPE_INODE_GENERATION_PARENT	2
static const value_string fileid_type_names[] = {
	{	FILEID_TYPE_ROOT,			"root"				},
	{	FILEID_TYPE_INODE_GENERATION,		"inode/generation"		},
	{	FILEID_TYPE_INODE_GENERATION_PARENT,	"inode/generation/parent"	},
	{0,NULL}
};

static void
dissect_fhandle_data_LINUX_KNFSD_NEW(tvbuff_t* tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	int offset=0;
	guint8 version;
	guint8 auth_type;
	guint8 fsid_type;
	guint8 fileid_type;

	version     = tvb_get_guint8(tvb, offset + 0);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_fh_version,
			tvb, offset+0, 1, version);
	}

	switch (version) {
		case 1: {
			auth_type   = tvb_get_guint8(tvb, offset + 1);
			fsid_type   = tvb_get_guint8(tvb, offset + 2);
			fileid_type = tvb_get_guint8(tvb, offset + 3);
			if (tree) {
				proto_item* encoding_item = proto_tree_add_text(tree, tvb,
					offset + 1, 3,
					"encoding: %u %u %u",
					auth_type, fsid_type, fileid_type);
				if (encoding_item) {
					proto_tree* encoding_tree = proto_item_add_subtree(encoding_item,
						ett_nfs_fh_encoding);
					if (encoding_tree) {
						proto_tree_add_uint(encoding_tree, hf_nfs_fh_auth_type,
							tvb, offset+1, 1, auth_type);
						proto_tree_add_uint(encoding_tree, hf_nfs_fh_fsid_type,
							tvb, offset+2, 1, fsid_type);
						proto_tree_add_uint(encoding_tree, hf_nfs_fh_fileid_type,
							tvb, offset+3, 1, fileid_type);
					}
				}
			}
			offset += 4;
		} break;
		default: {
			/* unknown version */
			goto out;
		}
	}

	switch (auth_type) {
		case 0: {
			/* no authentication */
			if (tree) {
				proto_tree_add_text(tree, tvb,
					offset + 0, 0,
					"authentication: none");
			}
		} break;
		default: {
			/* unknown authentication type */
			goto out;
		}
	}

	switch (fsid_type) {
		case 0: {
			guint16 fsid_major;
			guint16 fsid_minor;
			guint32 fsid_inode;

			fsid_major = tvb_get_ntohs(tvb, offset + 0);
			fsid_minor = tvb_get_ntohs(tvb, offset + 2);
			fsid_inode = tvb_get_letohl(tvb, offset + 4);
			if (tree) {
				proto_item* fsid_item = proto_tree_add_text(tree, tvb,
					offset+0, 8,
					"file system ID: %u,%u (inode %u)",
					fsid_major, fsid_minor, fsid_inode);
				if (fsid_item) {
					proto_tree* fsid_tree = proto_item_add_subtree(fsid_item,
						ett_nfs_fh_fsid);
					if (fsid_tree) {
						proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_major,
							tvb, offset+0, 2, fsid_major);
						proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_minor,
							tvb, offset+2, 2, fsid_minor);
						proto_tree_add_uint(fsid_tree, hf_nfs_fh_fsid_inode,
							tvb, offset+4, 4, fsid_inode);
					}
				}
			}
			offset += 8;
		} break;
		default: {
			/* unknown fsid type */
			goto out;
		}
	}

	switch (fileid_type) {
		case 0: {
			if (tree) {
				proto_tree_add_text(tree, tvb,
					offset+0, 0,
					"file ID: root inode");
			}
		} break;
		case 1: {
			guint32 inode;
			guint32 generation;

			inode = tvb_get_letohl(tvb, offset + 0);
			generation = tvb_get_letohl(tvb, offset + 4);

			if (tree) {
				proto_item* fileid_item = proto_tree_add_text(tree, tvb,
					offset+0, 8,
					"file ID: %u (%u)",
					inode, generation);
				if (fileid_item) {
					proto_tree* fileid_tree = proto_item_add_subtree(
						fileid_item, ett_nfs_fh_fn);
					if (fileid_tree) {
						proto_tree_add_uint(fileid_tree, hf_nfs_fh_fn_inode,
						tvb, offset+0, 4, inode);
						proto_tree_add_uint(fileid_tree, hf_nfs_fh_fn_generation,
						tvb, offset+4, 4, generation);
					}
				}
			}

			offset += 8;
		} break;
		case 2: {
			guint32 inode;
			guint32 generation;
			guint32 parent_inode;

			inode = tvb_get_letohl(tvb, offset + 0);
			generation = tvb_get_letohl(tvb, offset + 4);
			parent_inode = tvb_get_letohl(tvb, offset + 8);

			if (tree) {
				 proto_item* fileid_item = proto_tree_add_text(tree, tvb,
					offset+0, 8,
					"file ID: %u (%u)",
					inode, generation);
				if (fileid_item) {
					proto_tree* fileid_tree = proto_item_add_subtree(
						fileid_item, ett_nfs_fh_fn);
					if (fileid_tree) {
						proto_tree_add_uint(fileid_tree, hf_nfs_fh_fn_inode,
						tvb, offset+0, 4, inode);
						proto_tree_add_uint(fileid_tree, hf_nfs_fh_fn_generation,
						tvb, offset+4, 4, generation);
						proto_tree_add_uint(fileid_tree, hf_nfs_fh_dirinode,
						tvb, offset+8, 4, parent_inode);
					}
				}
			}

			offset += 12;
		} break;
		default: {
			/* unknown fileid type */
			goto out;
		}
	}

out:
	;
}


static void
dissect_fhandle_data_unknown(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
{
	guint fhlen=tvb_length(tvb);

	proto_tree_add_item(tree, hf_nfs_fh_fhandle_data, tvb, 0, fhlen, FALSE);
}


static void
dissect_fhandle_data(tvbuff_t *tvb, int offset, packet_info *pinfo,
    proto_tree *tree, unsigned int fhlen, gboolean hidden, guint32 *hash)
{
	/* this is to set up fhandle display filters to find both packets
	   of an RPC call */
	if(nfs_fhandle_reqrep_matching && (!hidden) ){
		nfs_fhandle_data_t *old_fhd=NULL;

		if( !pinfo->fd->flags.visited ){
			nfs_fhandle_data_t fhd;

			/* first check if we have seen this fhandle before */
			fhd.len=fhlen;
			fhd.fh=(const unsigned char *)tvb_get_ptr(tvb, offset, fhlen);
			old_fhd=store_nfs_file_handle(&fhd);

			/* XXX here we should really check that we havent stored
			   this fhandle for this frame number already.
		   	   We should also make sure we can handle when we have multiple
		   	   fhandles seen for the same frame, which WILL happen for certain
		   	   nfs calls. For now, we dont handle this and those calls will
		   	   not work properly with this feature
			*/
			se_tree_insert32(nfs_fhandle_frame_table, pinfo->fd->num, old_fhd);
		}
	}

	/* create a semiunique hash value for the filehandle */
	{
		guint32 fhhash;
		guint32 i;
		proto_item *fh_item;

		for(fhhash=0,i=0;i<(fhlen-3);i+=4){
			guint32 val;
			val = tvb_get_ntohl(tvb, offset+i);
			fhhash ^= val;
			fhhash += val;
		}
		if(hidden){
			fh_item=proto_tree_add_uint_hidden(tree, hf_nfs_fh_hash, tvb, offset,
				fhlen, fhhash);
		} else {
			fh_item=proto_tree_add_uint(tree, hf_nfs_fh_hash, tvb, offset,
				fhlen, fhhash);
		}
		PROTO_ITEM_SET_GENERATED(fh_item);
		if(hash){
			*hash=fhhash;
		}
	}
	if(nfs_file_name_snooping){
		nfs_name_snoop_fh(pinfo, tree, tvb, offset, fhlen, hidden);
	}

	if(!hidden){
		tvbuff_t *fh_tvb;
		int real_length;

		proto_tree_add_text(tree, tvb, offset, 0,
			"decode type as: %s", val_to_str(default_nfs_fhandle_type, names_fhtype, "Unknown"));


		real_length=fhlen;
		if(real_length<tvb_length_remaining(tvb, offset)){
			real_length=tvb_length_remaining(tvb, offset);
		}
		fh_tvb=tvb_new_subset(tvb, offset, real_length, fhlen);
		if(!dissector_try_port(nfs_fhandle_table, default_nfs_fhandle_type, fh_tvb, pinfo, tree)){
			dissect_fhandle_data_unknown(fh_tvb, pinfo, tree);
		}
	}
}

void
dissect_fhandle_hidden(packet_info *pinfo, proto_tree *tree, int frame)
{
	nfs_fhandle_data_t *nfd;

	nfd=se_tree_lookup32(nfs_fhandle_frame_table, frame);
	if(nfd && nfd->len){
		dissect_fhandle_data(nfd->tvb, 0, pinfo, tree, nfd->len, TRUE, NULL);
	}
}


/***************************/
/* NFS Version 2, RFC 1094 */
/***************************/


/* RFC 1094, Page 12..14 */
static const value_string names_nfs_stat[] =
{
	{	0,	"NFS_OK" },
	{	1,	"NFSERR_PERM" },
	{	2,	"NFSERR_NOENT" },
	{	5,	"NFSERR_IO" },
	{	6,	"NFSERR_NXIO" },
	{	13,	"NFSERR_ACCES" },
	{	17,	"NFSERR_EXIST" },
	{	18,	"NFSERR_XDEV" },	/* not in spec, but can happen */
	{	19,	"NFSERR_NODEV" },
	{	20,	"NFSERR_NOTDIR" },
	{	21,	"NFSERR_ISDIR" },
	{	22,	"NFSERR_INVAL" },	/* not in spec, but I think it can happen */
	{	26,	"NFSERR_TXTBSY" },	/* not in spec, but I think it can happen */
	{	27,	"NFSERR_FBIG" },
	{	28,	"NFSERR_NOSPC" },
	{	30,	"NFSERR_ROFS" },
	{	31,	"NFSERR_MLINK" },	/* not in spec, but can happen */
	{	45,	"NFSERR_OPNOTSUPP" }, /* not in spec, but I think it can happen */
	{	63,	"NFSERR_NAMETOOLONG" },
	{	66,	"NFSERR_NOTEMPTY" },
	{	69,	"NFSERR_DQUOT" },
	{	70,	"NFSERR_STALE" },
	{	99,	"NFSERR_WFLUSH" },
	{	0,	NULL }
};

/* RFC 1094, Page 12..14 */
static int
dissect_stat(tvbuff_t *tvb, int offset, proto_tree *tree,
	guint32 *status)
{
	guint32 stat;
	proto_item *stat_item;

	stat = tvb_get_ntohl(tvb, offset+0);

	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_stat, tvb, offset+0, 4,
			stat);
		stat_item = proto_tree_add_uint(tree, hf_nfs_nfsstat, tvb,
			offset+0, 4, stat);
		PROTO_ITEM_SET_HIDDEN(stat_item);
	}

	offset += 4;

	if (status) *status = stat;

	return offset;
}


/* RFC 1094, Page 12..14 */
static int
dissect_nfs2_rmdir_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", RMDIR Reply");
			break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", RMDIR Reply  Error:%s", err);
	}

	return offset;
}

static int
dissect_nfs2_symlink_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", SYMLINK Reply");
			break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", SYMLINK Reply  Error:%s", err);
	}

	return offset;
}

static int
dissect_nfs2_link_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", LINK Reply");
			break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", LINK Reply  Error:%s", err);
	}

	return offset;
}

static int
dissect_nfs2_rename_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", RENAME Reply");
			break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", RENAME Reply  Error:%s", err);
	}

	return offset;
}

static int
dissect_nfs2_remove_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", REMOVE Reply");
			break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", REMOVE Reply  Error:%s", err);
	}

	return offset;
}


/* RFC 1094, Page 15 */
static int
dissect_ftype(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	guint32 ftype;
	const char* ftype_name = NULL;

	const value_string nfs2_ftype[] =
	{
		{	0,	"Non-File" },
		{	1,	"Regular File" },
		{	2,	"Directory" },
		{	3,	"Block Special Device" },
		{	4,	"Character Special Device" },
		{	5,	"Symbolic Link" },
		{	0,	NULL }
	};

	ftype = tvb_get_ntohl(tvb, offset+0);
	ftype_name = val_to_str(ftype, nfs2_ftype, "%u");

	if (tree) {
		proto_tree_add_text(tree, tvb, offset, 4,
			"%s: %s (%u)", name, ftype_name, ftype);
	}

	offset += 4;
	return offset;
}


/* RFC 1094, Page 15 */
int
dissect_fhandle(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree,
    const char *name, guint32 *hash)
{
	proto_item* fitem;
	proto_tree* ftree = NULL;

	if (tree) {
		fitem = proto_tree_add_text(tree, tvb, offset, FHSIZE,
			"%s", name);
		if (fitem)
			ftree = proto_item_add_subtree(fitem, ett_nfs_fhandle);
	}

	/* are we snooping fh to filenames ?*/
	if((!pinfo->fd->flags.visited) && nfs_file_name_snooping){
		rpc_call_info_value *civ=pinfo->private_data;

		/* NFS v2 LOOKUP, CREATE, MKDIR calls might give us a mapping*/
		if( (civ->prog==100003)
		  &&(civ->vers==2)
		  &&(!civ->request)
		  &&((civ->proc==4)||(civ->proc==9)||(civ->proc==14))
		) {
			nfs_name_snoop_add_fh(civ->xid, tvb,
				offset, 32);
		}

		/* MOUNT v1,v2 MNT replies might give us a filehandle*/
		if( (civ->prog==100005)
		  &&(civ->proc==1)
		  &&((civ->vers==1)||(civ->vers==2))
		  &&(!civ->request)
		) {
			nfs_name_snoop_add_fh(civ->xid, tvb,
				offset, 32);
		}
	}

	dissect_fhandle_data(tvb, offset, pinfo, ftree, FHSIZE, FALSE, hash);

	offset += FHSIZE;
	return offset;
}

/* RFC 1094, Page 15 */
static int
dissect_nfs2_statfs_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "object", &hash);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
	}
	proto_item_append_text(tree, ", STATFS Call FH:0x%08x", hash);

	return offset;
}

static int
dissect_nfs2_readlink_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "object", &hash);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
	}
	proto_item_append_text(tree, ", READLINK Call FH:0x%08x", hash);

	return offset;
}

static int
dissect_nfs2_getattr_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "object", &hash);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
	}
	proto_item_append_text(tree, ", GETATTR Call FH:0x%08x", hash);

	return offset;
}


/* RFC 1094, Page 15 */
static int
dissect_timeval(tvbuff_t *tvb, int offset, proto_tree *tree, int hf_time, int hf_time_sec, int hf_time_usec)
{
	guint32	seconds;
	guint32 useconds;
	nstime_t ts;

	proto_item* time_item;
	proto_tree* time_tree = NULL;

	seconds = tvb_get_ntohl(tvb, offset+0);
	useconds = tvb_get_ntohl(tvb, offset+4);
	ts.secs = seconds;
	ts.nsecs = useconds*1000;

	if (tree) {
		time_item = proto_tree_add_time(tree, hf_time, tvb, offset, 8,
				&ts);
		if (time_item)
			time_tree = proto_item_add_subtree(time_item, ett_nfs_timeval);
	}

	if (time_tree) {
		proto_tree_add_uint(time_tree, hf_time_sec, tvb, offset, 4,
					seconds);
		proto_tree_add_uint(time_tree, hf_time_usec, tvb, offset+4, 4,
					useconds);
	}
	offset += 8;
	return offset;
}


/* RFC 1094, Page 16 */
static const value_string nfs2_mode_names[] = {
	{	0040000,	"Directory"	},
	{	0020000,	"Character Special Device"	},
	{	0060000,	"Block Special Device"	},
	{	0100000,	"Regular File"	},
	{	0120000,	"Symbolic Link"	},
	{	0140000,	"Named Socket"	},
	{	0000000,	NULL		},
};

static int
dissect_mode(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	guint32 mode;
	proto_item* mode_item = NULL;
	proto_tree* mode_tree = NULL;

	mode = tvb_get_ntohl(tvb, offset+0);

	if (tree) {
		mode_item = proto_tree_add_text(tree, tvb, offset, 4,
			"%s: 0%o", name, mode);
		if (mode_item)
			mode_tree = proto_item_add_subtree(mode_item, ett_nfs_mode);
	}

	if (mode_tree) {
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
			decode_enumerated_bitfield(mode,  0160000, 16,
			nfs2_mode_names, "%s"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,   04000, 16, "Set user id on exec", "not SUID"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,   02000, 16, "Set group id on exec", "not SGID"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,   01000, 16, "Save swapped text even after use", "not save swapped text"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,    0400, 16, "Read permission for owner", "no Read permission for owner"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,    0200, 16, "Write permission for owner", "no Write permission for owner"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,    0100, 16, "Execute permission for owner", "no Execute permission for owner"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,     040, 16, "Read permission for group", "no Read permission for group"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,     020, 16, "Write permission for group", "no Write permission for group"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,     010, 16, "Execute permission for group", "no Execute permission for group"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,      04, 16, "Read permission for others", "no Read permission for others"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,      02, 16, "Write permission for others", "no Write permission for others"));
		proto_tree_add_text(mode_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode,      01, 16, "Execute permission for others", "no Execute permission for others"));
	}

	offset += 4;
	return offset;
}


/* RFC 1094, Page 15 */
int
dissect_fattr(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* fattr_item = NULL;
	proto_tree* fattr_tree = NULL;
	int old_offset = offset;

	if (tree) {
		fattr_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		fattr_tree = proto_item_add_subtree(fattr_item, ett_nfs_fattr);
	}

	offset = dissect_ftype(tvb, offset, fattr_tree, "type");
	offset = dissect_mode(tvb, offset, fattr_tree, "mode");
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_nlink, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_uid, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_gid, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_size, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_blocksize, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_rdev, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_blocks, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_fsid, offset);
	offset = dissect_rpc_uint32(tvb, fattr_tree, hf_nfs_fattr_fileid, offset);

	offset = dissect_timeval(tvb, offset, fattr_tree, hf_nfs_atime, hf_nfs_atime_sec, hf_nfs_atime_usec);
	offset = dissect_timeval(tvb, offset, fattr_tree, hf_nfs_mtime, hf_nfs_mtime_sec, hf_nfs_mtime_usec);
	offset = dissect_timeval(tvb, offset, fattr_tree, hf_nfs_ctime, hf_nfs_ctime_sec, hf_nfs_ctime_usec);

	/* now we know, that fattr is shorter */
	if (fattr_item) {
		proto_item_set_len(fattr_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1094, Page 17 */
static int
dissect_sattr(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* sattr_item = NULL;
	proto_tree* sattr_tree = NULL;
	int old_offset = offset;

	if (tree) {
		sattr_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		sattr_tree = proto_item_add_subtree(sattr_item, ett_nfs_sattr);
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff)
		offset = dissect_mode(tvb, offset, sattr_tree, "mode");
	else {
		proto_tree_add_text(sattr_tree, tvb, offset, 4, "mode: no value");
		offset += 4;
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff)
		offset = dissect_rpc_uint32(tvb, sattr_tree, hf_nfs_fattr_uid,
			offset);
	else {
		proto_tree_add_text(sattr_tree, tvb, offset, 4, "uid: no value");
		offset += 4;
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff)
		offset = dissect_rpc_uint32(tvb, sattr_tree, hf_nfs_fattr_gid,
			offset);
	else {
		proto_tree_add_text(sattr_tree, tvb, offset, 4, "gid: no value");
		offset += 4;
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff)
		offset = dissect_rpc_uint32(tvb, sattr_tree, hf_nfs_fattr_size,
			offset);
	else {
		proto_tree_add_text(sattr_tree, tvb, offset, 4, "size: no value");
		offset += 4;
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff) {
		offset = dissect_timeval(tvb, offset, sattr_tree, hf_nfs_atime, hf_nfs_atime_sec, hf_nfs_atime_usec);
	} else {
		proto_tree_add_text(sattr_tree, tvb, offset, 8, "atime: no value");
		offset += 8;
	}

	if (tvb_get_ntohl(tvb, offset+0) != 0xffffffff) {
		offset = dissect_timeval(tvb, offset, sattr_tree, hf_nfs_mtime, hf_nfs_mtime_sec, hf_nfs_mtime_usec);
	} else {
		proto_tree_add_text(sattr_tree, tvb, offset, 8, "mtime: no value");
		offset += 8;
	}

	/* now we know, that sattr is shorter */
	if (sattr_item) {
		proto_item_set_len(sattr_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1094, Page 17 */
static int
dissect_filename(tvbuff_t *tvb, int offset,
    proto_tree *tree, int hf, char **string_ret)
{
	offset = dissect_rpc_string(tvb, tree, hf, offset, string_ret);
	return offset;
}


/* RFC 1094, Page 17 */
static int
dissect_path(tvbuff_t *tvb, int offset, proto_tree *tree, int hf, char **name)
{
	offset = dissect_rpc_string(tvb, tree, hf, offset, name);
	return offset;
}


/* RFC 1094, Page 17,18 */
static int
dissect_attrstat(tvbuff_t *tvb, int offset, proto_tree *tree, packet_info *pinfo, const char *funcname)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			offset = dissect_fattr(tvb, offset, tree, "attributes");
			proto_item_append_text(tree, ", %s Reply", funcname);
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", %s Reply  Error:%s", funcname, err);
		break;
	}

	return offset;
}


/* RFC 1094, Page 17,18 */
static int
dissect_nfs2_write_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	offset = dissect_attrstat(tvb, offset, tree, pinfo, "WRITE");

	return offset;
}

static int
dissect_nfs2_setattr_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	offset = dissect_attrstat(tvb, offset, tree, pinfo, "SETATTR");

	return offset;
}

static int
dissect_nfs2_getattr_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree* tree)
{
	offset = dissect_attrstat(tvb, offset, tree, pinfo, "GETATTR");

	return offset;
}


/* RFC 1094, Page 18 */
static int
dissect_diropargs(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, const char* label, guint32 *hash, char **name)
{
	proto_item* diropargs_item = NULL;
	proto_tree* diropargs_tree = NULL;
	int old_offset = offset;

	if (tree) {
		diropargs_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", label);
		diropargs_tree = proto_item_add_subtree(diropargs_item, ett_nfs_diropargs);
	}

	/* are we snooping fh to filenames ?*/
	if((!pinfo->fd->flags.visited) && nfs_file_name_snooping){
		/* v2 LOOKUP, CREATE, MKDIR calls might give us a mapping*/
		rpc_call_info_value *civ=pinfo->private_data;

		if( (civ->prog==100003)
		  &&(civ->vers==2)
		  &&(civ->request)
		  &&((civ->proc==4)||(civ->proc==9)||(civ->proc==14))
		) {
			nfs_name_snoop_add_name(civ->xid, tvb,
				offset+36, tvb_get_ntohl(tvb, offset+32),
				offset, 32, NULL);
		}
	}

	offset = dissect_fhandle(tvb, offset, pinfo, diropargs_tree, "dir", hash);
	offset = dissect_filename(tvb, offset, diropargs_tree, hf_nfs_name, name);

	/* now we know, that diropargs is shorter */
	if (diropargs_item) {
		proto_item_set_len(diropargs_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1094, Page 18 */
static int
dissect_nfs2_rmdir_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;
	char *name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "where", &hash, &name);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", DH:0x%08x/%s", hash, name);
	}
	proto_item_append_text(tree, ", RMDIR Call DH:0x%08x/%s", hash, name);

	return offset;
}

static int
dissect_nfs2_remove_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;
	char *name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "where", &hash, &name);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", DH:0x%08x/%s", hash, name);
	}
	proto_item_append_text(tree, ", REMOVE Call DH:0x%08x/%s", hash, name);

	return offset;
}

static int
dissect_nfs2_lookup_call(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
	guint32 hash;
	char *name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "where", &hash, &name);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", DH:0x%08x/%s", hash, name);
	}
	proto_item_append_text(tree, ", LOOKUP Call DH:0x%08x/%s", hash, name);

	return offset;
}


/* RFC 1094, Page 18 */
static int
dissect_diropres(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, const char *funcname)
{
	guint32	status;
	guint32 hash;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			offset = dissect_fhandle(tvb, offset, pinfo, tree, "file", &hash);
			offset = dissect_fattr  (tvb, offset, tree, "attributes");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
			}
			proto_item_append_text(tree, ", %s Reply FH:0x%08x", funcname, hash);
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", %s Reply  Error:%s", funcname, err);
		break;
	}

	return offset;
}


/* nfsdata is simply a chunk of RPC opaque data (length, data, fill bytes) */
static int
dissect_nfsdata(tvbuff_t *tvb, int offset, proto_tree *tree, int hf)
{
	offset = dissect_rpc_data(tvb, tree, hf, offset);
	return offset;
}


/* RFC 1094, Page 18 */
static int
dissect_nfs2_mkdir_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree* tree)
{
	offset = dissect_diropres(tvb, offset, pinfo, tree, "MKDIR");
	return offset;
}

static int
dissect_nfs2_create_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree* tree)
{
	offset = dissect_diropres(tvb, offset, pinfo, tree, "CREATE");
	return offset;
}

static int
dissect_nfs2_lookup_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree* tree)
{
	offset = dissect_diropres(tvb, offset, pinfo, tree, "LOOKUP");
	return offset;
}


/* RFC 1094, Page 6 */
static int
dissect_nfs2_setattr_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "file", &hash);
	offset = dissect_sattr  (tvb, offset,        tree, "attributes");

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
	}
	proto_item_append_text(tree, ", SETATTR Call FH:0x%08x", hash);
	return offset;
}


/* RFC 1094, Page 6 */
static int
dissect_nfs2_readlink_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
	proto_tree *tree)
{
	guint32	status;
	const char *err;
	char *name=NULL;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			offset = dissect_path(tvb, offset, tree, hf_nfs_readlink_data, &name);
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Path:%s", name);
			}
			proto_item_append_text(tree, ", READLINK Reply Path:%s", name);
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", READLINK Reply  Error:%s", err);
		break;
	}

	return offset;
}


/* RFC 1094, Page 7 */
static int
dissect_nfs2_read_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 offset_value;
	guint32 count;
	guint32 totalcount;
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "file", &hash);
	offset_value = tvb_get_ntohl(tvb, offset+0);
	count        = tvb_get_ntohl(tvb, offset+4);
	totalcount   = tvb_get_ntohl(tvb, offset+8);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_read_offset, tvb,
		offset+0, 4, offset_value);
		proto_tree_add_uint(tree, hf_nfs_read_count, tvb,
		offset+4, 4, count);
		proto_tree_add_uint(tree, hf_nfs_read_totalcount, tvb,
		offset+8, 4, totalcount);
	}
	offset += 12;

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x Offset:%d Count:%d TotalCount:%d", hash, offset_value, count, totalcount);
	}
	proto_item_append_text(tree, ", READ Call FH:0x%08x Offset:%d Count:%d TotalCount:%d", hash, offset_value, count, totalcount);

	return offset;
}


/* RFC 1094, Page 7 */
static int
dissect_nfs2_read_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
	proto_tree* tree)
{
	guint32 status;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			offset = dissect_fattr(tvb, offset, tree, "attributes");
			proto_item_append_text(tree, ", READ Reply");
			offset = dissect_nfsdata(tvb, offset, tree, hf_nfs_data);
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", READ Reply  Error:%s", err);
		break;
	}

	return offset;
}


/* RFC 1094, Page 8 */
static int
dissect_nfs2_write_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 beginoffset;
	guint32 offset_value;
	guint32 totalcount;
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "file", &hash);
	beginoffset  = tvb_get_ntohl(tvb, offset+0);
	offset_value = tvb_get_ntohl(tvb, offset+4);
	totalcount   = tvb_get_ntohl(tvb, offset+8);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_write_beginoffset, tvb,
		offset+0, 4, beginoffset);
		proto_tree_add_uint(tree, hf_nfs_write_offset, tvb,
		offset+4, 4, offset_value);
		proto_tree_add_uint(tree, hf_nfs_write_totalcount, tvb,
		offset+8, 4, totalcount);
	}
	offset += 12;

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x BeginOffset:%d Offset:%d TotalCount:%d", hash, beginoffset, offset_value, totalcount);
	}
	proto_item_append_text(tree, ", WRITE Call FH:0x%08x BeginOffset:%d Offset:%d TotalCount:%d", hash, beginoffset, offset_value, totalcount);

	offset = dissect_nfsdata(tvb, offset, tree, hf_nfs_data);

	return offset;
}


/* RFC 1094, Page 8 */
static int
dissect_nfs2_mkdir_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 hash;
	char *name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "where", &hash, &name);
	offset = dissect_sattr    (tvb, offset,        tree, "attributes");

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", DH:0x%08x/%s", hash, name);
	}
	proto_item_append_text(tree, ", MKDIR Call DH:0x%08x/%s", hash, name);

	return offset;
}

static int
dissect_nfs2_create_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 hash;
	char *name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "where", &hash, &name);
	offset = dissect_sattr    (tvb, offset,        tree, "attributes");

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", DH:0x%08x/%s", hash, name);
	}
	proto_item_append_text(tree, ", CREATE Call DH:0x%08x/%s", hash, name);

	return offset;
}


/* RFC 1094, Page 9 */
static int
dissect_nfs2_rename_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 from_hash;
	char *from_name=NULL;
	guint32 to_hash;
	char *to_name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "from", &from_hash, &from_name);
	offset = dissect_diropargs(tvb, offset, pinfo, tree, "to", &to_hash, &to_name);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", From DH:0x%08x/%s To DH:0x%08x/%s", from_hash, from_name, to_hash, to_name);
	}
	proto_item_append_text(tree, ", RENAME Call From DH:0x%08x/%s To DH:0x%08x/%s", from_hash, from_name, to_hash, to_name);

	return offset;
}


/* RFC 1094, Page 9 */
static int
dissect_nfs2_link_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 from_hash;
	guint32 to_hash;
	char *to_name=NULL;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "from", &from_hash);
	offset = dissect_diropargs(tvb, offset, pinfo, tree, "to", &to_hash, &to_name);

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", From DH:0x%08x To DH:0x%08x/%s", from_hash, to_hash, to_name);
	}
	proto_item_append_text(tree, ", LINK Call From DH:0x%08x To DH:0x%08x/%s", from_hash, to_hash, to_name);

	return offset;
}


/* RFC 1094, Page 10 */
static int
dissect_nfs2_symlink_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32 from_hash;
	char *from_name=NULL;
	char *to_name=NULL;

	offset = dissect_diropargs(tvb, offset, pinfo, tree, "from", &from_hash, &from_name);
	offset = dissect_path(tvb, offset, tree, hf_nfs_symlink_to, &to_name);
	offset = dissect_sattr(tvb, offset, tree, "attributes");

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", From DH:0x%08x/%s To %s", from_hash, from_name, to_name);
	}
	proto_item_append_text(tree, ", SYMLINK Call From DH:0x%08x/%s To %s", from_hash, from_name, to_name);

	return offset;
}


/* RFC 1094, Page 11 */
static int
dissect_nfs2_readdir_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree)
{
	guint32	cookie;
	guint32	count;
	guint32 hash;

	offset = dissect_fhandle(tvb, offset, pinfo, tree, "dir", &hash);
	cookie  = tvb_get_ntohl(tvb, offset+ 0);
	count = tvb_get_ntohl(tvb, offset+ 4);
	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_readdir_cookie, tvb,
			offset+ 0, 4, cookie);
		proto_tree_add_uint(tree, hf_nfs_readdir_count, tvb,
			offset+ 4, 4, count);
	}
	offset += 8;

	if (check_col(pinfo->cinfo, COL_INFO)) {
		col_append_fstr(pinfo->cinfo, COL_INFO,", FH:0x%08x", hash);
	}
	proto_item_append_text(tree, ", READDIR Call FH:0x%08x", hash);

	return offset;
}


/* RFC 1094, Page 11 */
static int
dissect_readdir_entry(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
	proto_tree* tree)
{
	proto_item* entry_item = NULL;
	proto_tree* entry_tree = NULL;
	int old_offset = offset;
	guint32 fileid;
	guint32 cookie;
	char *name;

	if (tree) {
		entry_item = proto_tree_add_item(tree, hf_nfs_readdir_entry, tvb,
			offset+0, -1, FALSE);
		entry_tree = proto_item_add_subtree(entry_item, ett_nfs_readdir_entry);
	}

	fileid = tvb_get_ntohl(tvb, offset + 0);
	if (entry_tree)
		proto_tree_add_uint(entry_tree, hf_nfs_readdir_entry_fileid, tvb,
			offset+0, 4, fileid);
	offset += 4;

	offset = dissect_filename(tvb, offset, entry_tree,
		hf_nfs_readdir_entry_name, &name);
	if (entry_item)
		proto_item_set_text(entry_item, "Entry: file ID %u, name %s",
		fileid, name);

	cookie = tvb_get_ntohl(tvb, offset + 0);
	if (entry_tree)
		proto_tree_add_uint(entry_tree, hf_nfs_readdir_entry_cookie, tvb,
			offset+0, 4, cookie);
	offset += 4;

	/* now we know, that a readdir entry is shorter */
	if (entry_item) {
		proto_item_set_len(entry_item, offset - old_offset);
	}

	return offset;
}

/* RFC 1094, Page 11 */
static int
dissect_nfs2_readdir_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree* tree)
{
	guint32 status;
	guint32 eof_value;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			proto_item_append_text(tree, ", READDIR Reply");

			offset = dissect_rpc_list(tvb, pinfo, tree, offset,
				dissect_readdir_entry);
			eof_value = tvb_get_ntohl(tvb, offset+0);
			if (tree)
				proto_tree_add_uint(tree, hf_nfs_readdir_eof, tvb,
					offset+ 0, 4, eof_value);
			offset += 4;
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", READDIR Reply  Error:%s", err);
		break;
	}

	return offset;
}


/* RFC 1094, Page 12 */
static int
dissect_nfs2_statfs_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
	proto_tree* tree)
{
	guint32 status;
	guint32 tsize;
	guint32 bsize;
	guint32 blocks;
	guint32 bfree;
	guint32 bavail;
	const char *err;

	offset = dissect_stat(tvb, offset, tree, &status);
	switch (status) {
		case 0:
			tsize  = tvb_get_ntohl(tvb, offset+ 0);
			bsize  = tvb_get_ntohl(tvb, offset+ 4);
			blocks = tvb_get_ntohl(tvb, offset+ 8);
			bfree  = tvb_get_ntohl(tvb, offset+12);
			bavail = tvb_get_ntohl(tvb, offset+16);
			if (tree) {
				proto_tree_add_uint(tree, hf_nfs_statfs_tsize, tvb,
					offset+ 0, 4, tsize);
				proto_tree_add_uint(tree, hf_nfs_statfs_bsize, tvb,
					offset+ 4, 4, bsize);
				proto_tree_add_uint(tree, hf_nfs_statfs_blocks, tvb,
					offset+ 8, 4, blocks);
				proto_tree_add_uint(tree, hf_nfs_statfs_bfree, tvb,
					offset+12, 4, bfree);
				proto_tree_add_uint(tree, hf_nfs_statfs_bavail, tvb,
					offset+16, 4, bavail);
			}
			offset += 20;
			proto_item_append_text(tree, ", STATFS Reply");
		break;
		default:
			err=val_to_str(status, names_nfs_stat, "Unknown error:%u");
			if (check_col(pinfo->cinfo, COL_INFO)) {
				col_append_fstr(pinfo->cinfo, COL_INFO," Error:%s", err);
			}
			proto_item_append_text(tree, ", STATFS Reply  Error:%s", err);
		break;
	}

	return offset;
}


/* proc number, "proc name", dissect_request, dissect_reply */
/* NULL as function pointer means: type of arguments is "void". */
static const vsff nfs2_proc[] = {
	{ 0,	"NULL",		/* OK */
	NULL,				NULL },
	{ 1,	"GETATTR",	/* OK */
	dissect_nfs2_getattr_call,	dissect_nfs2_getattr_reply },
	{ 2,	"SETATTR",	/* OK */
	dissect_nfs2_setattr_call,	dissect_nfs2_setattr_reply },
	{ 3,	"ROOT",		/* OK */
	NULL,				NULL },
	{ 4,	"LOOKUP",	/* OK */
	dissect_nfs2_lookup_call,	dissect_nfs2_lookup_reply },
	{ 5,	"READLINK",	/* OK */
	dissect_nfs2_readlink_call,	dissect_nfs2_readlink_reply },
	{ 6,	"READ",		/* OK */
	dissect_nfs2_read_call,		dissect_nfs2_read_reply },
	{ 7,	"WRITECACHE",	/* OK */
	NULL,				NULL },
	{ 8,	"WRITE",	/* OK */
	dissect_nfs2_write_call,	dissect_nfs2_write_reply },
	{ 9,	"CREATE",	/* OK */
	dissect_nfs2_create_call,	dissect_nfs2_create_reply },
	{ 10,	"REMOVE",	/* OK */
	dissect_nfs2_remove_call,	dissect_nfs2_remove_reply },
	{ 11,	"RENAME",	/* OK */
	dissect_nfs2_rename_call,	dissect_nfs2_rename_reply },
	{ 12,	"LINK",		/* OK */
	dissect_nfs2_link_call,		dissect_nfs2_link_reply },
	{ 13,	"SYMLINK",	/* OK */
	dissect_nfs2_symlink_call,	dissect_nfs2_symlink_reply },
	{ 14,	"MKDIR",	/* OK */
	dissect_nfs2_mkdir_call,	dissect_nfs2_mkdir_reply },
	{ 15,	"RMDIR",	/* OK */
	dissect_nfs2_rmdir_call,	dissect_nfs2_rmdir_reply },
	{ 16,	"READDIR",	/* OK */
	dissect_nfs2_readdir_call,	dissect_nfs2_readdir_reply },
	{ 17,	"STATFS",	/* OK */
	dissect_nfs2_statfs_call,	dissect_nfs2_statfs_reply },
	{ 0,NULL,NULL,NULL }
};

static const value_string nfsv2_proc_vals[] = {
	{ 0,	"NULL" },
	{ 1,	"GETATTR" },
	{ 2,	"SETATTR" },
	{ 3,	"ROOT" },
	{ 4,	"LOOKUP" },
	{ 5,	"READLINK" },
	{ 6,	"READ" },
	{ 7,	"WRITECACHE" },
	{ 8,	"WRITE" },
	{ 9,	"CREATE" },
	{ 10,	"REMOVE" },
	{ 11,	"RENAME" },
	{ 12,	"LINK" },
	{ 13,	"SYMLINK" },
	{ 14,	"MKDIR" },
	{ 15,	"RMDIR" },
	{ 16,	"READDIR" },
	{ 17,	"STATFS" },
	{ 0,	NULL }
};

/* end of NFS Version 2 */


/***************************/
/* NFS Version 3, RFC 1813 */
/***************************/


/* RFC 1813, Page 15 */
static int
dissect_filename3(tvbuff_t *tvb, int offset,
    proto_tree *tree, int hf, char **string_ret)
{
	offset = dissect_rpc_string(tvb, tree, hf, offset, string_ret);
	return offset;
}


/* RFC 1813, Page 15 */
static int
dissect_nfspath3(tvbuff_t *tvb, int offset, proto_tree *tree, int hf, char **name)
{
	offset = dissect_rpc_string(tvb, tree, hf, offset, name);
	return offset;
}

/* RFC 1813, Page 15 */
static int
dissect_cookieverf3(tvbuff_t *tvb, int offset, proto_tree *tree)
{
	proto_tree_add_text(tree, tvb, offset, NFS3_COOKIEVERFSIZE,
		"Verifier: Opaque Data");
	offset += NFS3_COOKIEVERFSIZE;
	return offset;
}


/* RFC 1813, Page 16 */
static int
dissect_createverf3(tvbuff_t *tvb, int offset, proto_tree *tree)
{
	proto_tree_add_text(tree, tvb, offset, NFS3_CREATEVERFSIZE,
		"Verifier: Opaque Data");
	offset += NFS3_CREATEVERFSIZE;
	return offset;
}


/* RFC 1813, Page 16 */
static int
dissect_writeverf3(tvbuff_t *tvb, int offset, proto_tree *tree)
{
	proto_tree_add_text(tree, tvb, offset, NFS3_WRITEVERFSIZE,
		"Verifier: Opaque Data");
	offset += NFS3_WRITEVERFSIZE;
	return offset;
}

/* RFC 1813, Page 16 */
static int
dissect_mode3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name, guint32 *mode)
{
	guint32 mode3;
	proto_item* mode3_item = NULL;
	proto_tree* mode3_tree = NULL;

	mode3 = tvb_get_ntohl(tvb, offset+0);
	if(mode){
		*mode=mode3;
	}

	if (tree) {
		mode3_item = proto_tree_add_text(tree, tvb, offset, 4,
			"%s: 0%o", name, mode3);
		if (mode3_item)
			mode3_tree = proto_item_add_subtree(mode3_item, ett_nfs_mode3);
	}

	/* RFC 1813, Page 23 */
	if (mode3_tree) {
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,   0x800, 12, "Set user id on exec", "not SUID"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,   0x400, 12, "Set group id on exec", "not SGID"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,   0x200, 12, "Save swapped text even after use", "not save swapped text"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,   0x100, 12, "Read permission for owner", "no Read permission for owner"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,    0x80, 12, "Write permission for owner", "no Write permission for owner"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,    0x40, 12, "Execute permission for owner", "no Execute permission for owner"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,    0x20, 12, "Read permission for group", "no Read permission for group"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,    0x10, 12, "Write permission for group", "no Write permission for group"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,     0x8, 12, "Execute permission for group", "no Execute permission for group"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,     0x4, 12, "Read permission for others", "no Read permission for others"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,     0x2, 12, "Write permission for others", "no Write permission for others"));
		proto_tree_add_text(mode3_tree, tvb, offset, 4, "%s",
		decode_boolean_bitfield(mode3,     0x1, 12, "Execute permission for others", "no Execute permission for others"));
	}

	offset += 4;
	return offset;
}

/* RFC 1813, Page 16,17 */
static const value_string names_nfs_nfsstat3[] =
{
	{	0,	"NFS3_OK" },
	{	1,	"NFS3ERR_PERM" },
	{	2,	"NFS3ERR_NOENT" },
	{	5,	"NFS3ERR_IO" },
	{	6,	"NFS3ERR_NXIO" },
	{	13,	"NFS3ERR_ACCES" },
	{	17,	"NFS3ERR_EXIST" },
	{	18,	"NFS3ERR_XDEV" },
	{	19,	"NFS3ERR_NODEV" },
	{	20,	"NFS3ERR_NOTDIR" },
	{	21,	"NFS3ERR_ISDIR" },
	{	22,	"NFS3ERR_INVAL" },
	{	27,	"NFS3ERR_FBIG" },
	{	28,	"NFS3ERR_NOSPC" },
	{	30,	"NFS3ERR_ROFS" },
	{	31,	"NFS3ERR_MLINK" },
	{	63,	"NFS3ERR_NAMETOOLONG" },
	{	66,	"NFS3ERR_NOTEMPTY" },
	{	69,	"NFS3ERR_DQUOT" },
	{	70,	"NFS3ERR_STALE" },
	{	71,	"NFS3ERR_REMOTE" },
	{	10001,	"NFS3ERR_BADHANDLE" },
	{	10002,	"NFS3ERR_NOT_SYNC" },
	{	10003,	"NFS3ERR_BAD_COOKIE" },
	{	10004,	"NFS3ERR_NOTSUPP" },
	{	10005,	"NFS3ERR_TOOSMALL" },
	{	10006,	"NFS3ERR_SERVERFAULT" },
	{	10007,	"NFS3ERR_BADTYPE" },
	{	10008,	"NFS3ERR_JUKEBOX" },
	{	0,	NULL }
};


/* RFC 1813, Page 16 */
static int
dissect_nfsstat3(tvbuff_t *tvb, int offset,
	proto_tree *tree,guint32 *status)
{
	guint32 nfsstat3;
	proto_item *stat_item;

	nfsstat3 = tvb_get_ntohl(tvb, offset+0);

	if (tree) {
		proto_tree_add_uint(tree, hf_nfs_nfsstat3, tvb,
			offset+0, 4, nfsstat3);
		stat_item = proto_tree_add_uint(tree, hf_nfs_nfsstat, tvb,
			offset+0, 4, nfsstat3);
		PROTO_ITEM_SET_HIDDEN(stat_item);
	}

	offset += 4;
	*status = nfsstat3;
	return offset;
}


static const value_string names_nfs_ftype3[] =
{
	{	NF3REG,	"Regular File" },
	{	NF3DIR,	"Directory" },
	{	NF3BLK,	"Block Special Device" },
	{	NF3CHR,	"Character Special Device" },
	{	NF3LNK,	"Symbolic Link" },
	{	NF3SOCK,"Socket" },
	{	NF3FIFO,"Named Pipe" },
	{	0,	NULL }
};


/* RFC 1813, Page 20 */
static int
dissect_ftype3(tvbuff_t *tvb, int offset, proto_tree *tree,
	int hf, guint32* ftype3)
{
	guint32 type;

	type = tvb_get_ntohl(tvb, offset+0);

	if (tree) {
		proto_tree_add_uint(tree, hf, tvb, offset, 4, type);
	}

	offset += 4;
	*ftype3 = type;
	return offset;
}


/* RFC 1813, Page 20 */
static int
dissect_specdata3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	guint32	specdata1;
	guint32	specdata2;

	proto_item* specdata3_item;
	proto_tree* specdata3_tree = NULL;

	specdata1 = tvb_get_ntohl(tvb, offset+0);
	specdata2 = tvb_get_ntohl(tvb, offset+4);

	if (tree) {
		specdata3_item = proto_tree_add_text(tree, tvb, offset, 8,
			"%s: %u,%u", name, specdata1, specdata2);
		if (specdata3_item)
			specdata3_tree = proto_item_add_subtree(specdata3_item,
					ett_nfs_specdata3);
	}

	if (specdata3_tree) {
		proto_tree_add_text(specdata3_tree, tvb,offset+0,4,
					"specdata1: %u", specdata1);
		proto_tree_add_text(specdata3_tree, tvb,offset+4,4,
					"specdata2: %u", specdata2);
	}

	offset += 8;
	return offset;
}


/* RFC 1813, Page 21 */
int
dissect_nfs_fh3(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree, const char *name, guint32 *hash)
{
	guint fh3_len;
	guint fh3_len_full;
	guint fh3_fill;
	proto_item* fitem = NULL;
	proto_tree* ftree = NULL;
	int fh_offset,fh_length;

	fh3_len = tvb_get_ntohl(tvb, offset+0);
	fh3_len_full = rpc_roundup(fh3_len);
	fh3_fill = fh3_len_full - fh3_len;

	if (tree) {
		fitem = proto_tree_add_text(tree, tvb, offset, 4+fh3_len_full,
			"%s", name);
		if (fitem)
			ftree = proto_item_add_subtree(fitem, ett_nfs_fh3);
	}

	/* are we snooping fh to filenames ?*/
	if((!pinfo->fd->flags.visited) && nfs_file_name_snooping){
		rpc_call_info_value *civ=pinfo->private_data;

		/* NFS v3 LOOKUP, CREATE, MKDIR, READDIRPLUS
			calls might give us a mapping*/
		if( (civ->prog==100003)
		  &&(civ->vers==3)
		  &&(!civ->request)
		  &&((civ->proc==3)||(civ->proc==8)||(civ->proc==9)||(civ->proc==17))
		) {
			fh_length=tvb_get_ntohl(tvb, offset);
			fh_offset=offset+4;
			nfs_name_snoop_add_fh(civ->xid, tvb,
				fh_offset, fh_length);
		}

		/* MOUNT v3 MNT replies might give us a filehandle */
		if( (civ->prog==100005)
		  &&(civ->vers==3)
		  &&(!civ->request)
		  &&(civ->proc==1)
		) {
			fh_length=tvb_get_ntohl(tvb, offset);
			fh_offset=offset+4;
			nfs_name_snoop_add_fh(civ->xid, tvb,
				fh_offset, fh_length);
		}
	}

	proto_tree_add_uint(ftree, hf_nfs_fh_length, tvb, offset+0, 4,
			fh3_len);

	/* Handle WebNFS requests where filehandle may be 0 length */
	if (fh3_len > 0)
	{
		dissect_fhandle_data(tvb, offset+4, pinfo, ftree, fh3_len, FALSE, hash);

		offset += fh3_len_full;
	}

	offset += 4;

	return offset;
}


/* RFC 1813, Page 21 */
static int
dissect_nfstime3(tvbuff_t *tvb, int offset,
	proto_tree *tree, int hf_time, int hf_time_sec, int hf_time_nsec)
{
	guint32	seconds;
	guint32 nseconds;
	nstime_t ts;

	proto_item* time_item;
	proto_tree* time_tree = NULL;

	seconds = tvb_get_ntohl(tvb, offset+0);
	nseconds = tvb_get_ntohl(tvb, offset+4);
	ts.secs = seconds;
	ts.nsecs = nseconds;

	if (tree) {
		time_item = proto_tree_add_time(tree, hf_time, tvb, offset, 8,
				&ts);
		if (time_item)
			time_tree = proto_item_add_subtree(time_item, ett_nfs_nfstime3);
	}

	if (time_tree) {
		proto_tree_add_uint(time_tree, hf_time_sec, tvb, offset, 4,
					seconds);
		proto_tree_add_uint(time_tree, hf_time_nsec, tvb, offset+4, 4,
					nseconds);
	}
	offset += 8;
	return offset;
}


/* RFC 1813, Page 22
 * The levels parameter tells this helper how many levels up in the tree it
 * should display useful info such as type,mode,uid,gid
 * If level has the COL_INFO_LEVEL flag set it will also display
 * this info in the info column.
 */
static int
dissect_nfs_fattr3(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree, const char* name, guint32 levels)
{
	proto_item* fattr3_item = NULL;
	proto_tree* fattr3_tree = NULL;
	int old_offset = offset;
	guint32 type, mode, uid, gid;

	if (tree) {
		fattr3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		fattr3_tree = proto_item_add_subtree(fattr3_item, ett_nfs_fattr3);
	}

	/* ftype */
	offset = dissect_ftype3(tvb,offset,fattr3_tree,hf_nfs_fattr3_type,&type);

	/* mode */
	offset = dissect_mode3(tvb,offset,fattr3_tree,"mode",&mode);

	/* nlink */
	offset = dissect_rpc_uint32(tvb, fattr3_tree, hf_nfs_fattr3_nlink,
		offset);

	/* uid */
	uid=tvb_get_ntohl(tvb, offset);
	offset = dissect_rpc_uint32(tvb, fattr3_tree, hf_nfs_fattr3_uid,
		offset);

	/* gid */
	gid=tvb_get_ntohl(tvb, offset);
	offset = dissect_rpc_uint32(tvb, fattr3_tree, hf_nfs_fattr3_gid,
		offset);

	/* size*/
	offset = dissect_rpc_uint64(tvb, fattr3_tree, hf_nfs_fattr3_size,
		offset);

	/* used */
	offset = dissect_rpc_uint64(tvb, fattr3_tree, hf_nfs_fattr3_used,
		offset);

	/* rdev */
	offset = dissect_specdata3(tvb,offset,fattr3_tree,"rdev");

	/* fsid */
	offset = dissect_rpc_uint64(tvb, fattr3_tree, hf_nfs_fattr3_fsid,
		offset);

	/* fileid */
	offset = dissect_rpc_uint64(tvb, fattr3_tree, hf_nfs_fattr3_fileid,
		offset);

	/* atime */
	offset = dissect_nfstime3 (tvb,offset,fattr3_tree,hf_nfs_atime,hf_nfs_atime_sec,hf_nfs_atime_nsec);

	/* mtime */
	offset = dissect_nfstime3 (tvb,offset,fattr3_tree,hf_nfs_mtime,hf_nfs_mtime_sec,hf_nfs_mtime_nsec);

	/* ctime */
	offset = dissect_nfstime3 (tvb,offset,fattr3_tree,hf_nfs_ctime,hf_nfs_ctime_sec,hf_nfs_ctime_nsec);

	/* now we know, that fattr3 is shorter */
	if (fattr3_item) {
		proto_item_set_len(fattr3_item, offset - old_offset);
	}


	/* put some nice info in COL_INFO for GETATTR replies */
	if(levels&COL_INFO_LEVEL){
		levels&=(~COL_INFO_LEVEL);
		if (check_col(pinfo->cinfo, COL_INFO)) {
			col_append_fstr(pinfo->cinfo, COL_INFO,
				"  %s mode:%04o uid:%d gid:%d",
				val_to_str(type, names_nfs_ftype3,"Unknown Type:0x%x"),
				mode&0x0fff,
				uid,
				gid
			);
		}
	}
	/* populate the expansion lines with some nice useable info */
	while( fattr3_tree && levels-- ){
		if(fattr3_tree){
			proto_item_append_text(fattr3_tree, "  %s mode:%04o uid:%d gid:%d",
				val_to_str(type, names_nfs_ftype3,"Unknown Type:0x%x"),
				mode&0x0fff,
				uid,
				gid
			);
		}
		fattr3_tree=fattr3_tree->parent;
	}

	return offset;
}


static const value_string value_follows[] =
	{
		{ 0, "no value" },
		{ 1, "value follows"},
		{ 0, NULL }
	};


/* RFC 1813, Page 23 */
int
dissect_nfs_post_op_attr(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree,
		const char* name)
{
	proto_item* post_op_attr_item = NULL;
	proto_tree* post_op_attr_tree = NULL;
	int old_offset = offset;
	guint32 attributes_follow;

	if (tree) {
		post_op_attr_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		post_op_attr_tree = proto_item_add_subtree(post_op_attr_item,
			ett_nfs_post_op_attr);
	}

	attributes_follow = tvb_get_ntohl(tvb, offset+0);
	proto_tree_add_text(post_op_attr_tree, tvb, offset, 4,
		"attributes_follow: %s (%u)",
		val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
	offset += 4;
	switch (attributes_follow) {
		case TRUE:
			offset = dissect_nfs_fattr3(pinfo, tvb, offset, post_op_attr_tree,
					"attributes",2);
		break;
		case FALSE:
			/* void */
		break;
	}

	/* now we know, that post_op_attr_tree is shorter */
	if (post_op_attr_item) {
		proto_item_set_len(post_op_attr_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 24 */
static int
dissect_wcc_attr(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* wcc_attr_item = NULL;
	proto_tree* wcc_attr_tree = NULL;
	int old_offset = offset;

	if (tree) {
		wcc_attr_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		wcc_attr_tree = proto_item_add_subtree(wcc_attr_item,
			ett_nfs_wcc_attr);
	}

	offset = dissect_rpc_uint64(tvb, wcc_attr_tree, hf_nfs_wcc_attr_size,
		offset);
	offset = dissect_nfstime3(tvb, offset, wcc_attr_tree, hf_nfs_mtime, hf_nfs_mtime_sec, hf_nfs_mtime_nsec);
	offset = dissect_nfstime3(tvb, offset, wcc_attr_tree, hf_nfs_ctime, hf_nfs_ctime_sec, hf_nfs_ctime_nsec);
	/* now we know, that wcc_attr_tree is shorter */
	if (wcc_attr_item) {
		proto_item_set_len(wcc_attr_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 24 */
static int
dissect_pre_op_attr(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* pre_op_attr_item = NULL;
	proto_tree* pre_op_attr_tree = NULL;
	int old_offset = offset;
	guint32 attributes_follow;

	if (tree) {
		pre_op_attr_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		pre_op_attr_tree = proto_item_add_subtree(pre_op_attr_item,
			ett_nfs_pre_op_attr);
	}

	attributes_follow = tvb_get_ntohl(tvb, offset+0);
	proto_tree_add_text(pre_op_attr_tree, tvb, offset, 4,
		"attributes_follow: %s (%u)",
		val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
	offset += 4;
	switch (attributes_follow) {
		case TRUE:
			offset = dissect_wcc_attr(tvb, offset, pre_op_attr_tree,
					"attributes");
		break;
		case FALSE:
			/* void */
		break;
	}

	/* now we know, that pre_op_attr_tree is shorter */
	if (pre_op_attr_item) {
		proto_item_set_len(pre_op_attr_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 24 */
static int
dissect_wcc_data(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, const char* name)
{
	proto_item* wcc_data_item = NULL;
	proto_tree* wcc_data_tree = NULL;
	int old_offset = offset;

	if (tree) {
		wcc_data_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		wcc_data_tree = proto_item_add_subtree(wcc_data_item,
			ett_nfs_wcc_data);
	}

	offset = dissect_pre_op_attr (tvb, offset, wcc_data_tree, "before");
	offset = dissect_nfs_post_op_attr(tvb, offset, pinfo, wcc_data_tree, "after" );

	/* now we know, that wcc_data is shorter */
	if (wcc_data_item) {
		proto_item_set_len(wcc_data_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 25 */
static int
dissect_post_op_fh3(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree, const char* name)
{
	proto_item* post_op_fh3_item = NULL;
	proto_tree* post_op_fh3_tree = NULL;
	int old_offset = offset;
	guint32 handle_follows;

	if (tree) {
		post_op_fh3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		post_op_fh3_tree = proto_item_add_subtree(post_op_fh3_item,
			ett_nfs_post_op_fh3);
	}

	handle_follows = tvb_get_ntohl(tvb, offset+0);
	proto_tree_add_text(post_op_fh3_tree, tvb, offset, 4,
		"handle_follows: %s (%u)",
		val_to_str(handle_follows,value_follows,"Unknown"), handle_follows);
	offset += 4;
	switch (handle_follows) {
		case TRUE:
			offset = dissect_nfs_fh3(tvb, offset, pinfo, post_op_fh3_tree,
					"handle", NULL);
		break;
		case FALSE:
			/* void */
		break;
	}

	/* now we know, that post_op_fh3_tree is shorter */
	if (post_op_fh3_item) {
		proto_item_set_len(post_op_fh3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 25 */
static int
dissect_set_mode3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_mode3_item = NULL;
	proto_tree* set_mode3_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,value_follows,"Unknown");

	if (tree) {
		set_mode3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_mode3_tree = proto_item_add_subtree(set_mode3_item,
			ett_nfs_set_mode3);
	}

	if (set_mode3_tree)
		proto_tree_add_text(set_mode3_tree, tvb, offset, 4,
			"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case 1:
			offset = dissect_mode3(tvb, offset, set_mode3_tree,
					"mode", NULL);
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_mode3 is shorter */
	if (set_mode3_item) {
		proto_item_set_len(set_mode3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 26 */
static int
dissect_set_uid3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_uid3_item = NULL;
	proto_tree* set_uid3_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,value_follows,"Unknown");

	if (tree) {
		set_uid3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_uid3_tree = proto_item_add_subtree(set_uid3_item,
			ett_nfs_set_uid3);
	}

	if (set_uid3_tree)
		proto_tree_add_text(set_uid3_tree, tvb, offset, 4,
			"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case 1:
			offset = dissect_rpc_uint32(tvb, set_uid3_tree,
								 hf_nfs_uid3, offset);
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_uid3 is shorter */
	if (set_uid3_item) {
		proto_item_set_len(set_uid3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 26 */
static int
dissect_set_gid3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_gid3_item = NULL;
	proto_tree* set_gid3_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,value_follows,"Unknown");

	if (tree) {
		set_gid3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_gid3_tree = proto_item_add_subtree(set_gid3_item,
			ett_nfs_set_gid3);
	}

	if (set_gid3_tree)
		proto_tree_add_text(set_gid3_tree, tvb, offset, 4,
			"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case 1:
			offset = dissect_rpc_uint32(tvb, set_gid3_tree,
				hf_nfs_gid3, offset);
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_gid3 is shorter */
	if (set_gid3_item) {
		proto_item_set_len(set_gid3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 26 */
static int
dissect_set_size3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_size3_item = NULL;
	proto_tree* set_size3_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,value_follows,"Unknown");

	if (tree) {
		set_size3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_size3_tree = proto_item_add_subtree(set_size3_item,
			ett_nfs_set_size3);
	}

	if (set_size3_tree)
		proto_tree_add_text(set_size3_tree, tvb, offset, 4,
			"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case 1:
			offset = dissect_rpc_uint64(tvb, set_size3_tree,
				hf_nfs_set_size3_size, offset);
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_size3 is shorter */
	if (set_size3_item) {
		proto_item_set_len(set_size3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 25 */
#define DONT_CHANGE 0
#define SET_TO_SERVER_TIME 1
#define SET_TO_CLIENT_TIME 2

static const value_string time_how[] =
	{
		{ DONT_CHANGE,	"don't change" },
		{ SET_TO_SERVER_TIME, "set to server time" },
		{ SET_TO_CLIENT_TIME, "set to client time" },
		{ 0, NULL }
	};


/* RFC 1813, Page 26 */
static int
dissect_set_atime(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_atime_item = NULL;
	proto_tree* set_atime_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,time_how,"Unknown");

	if (tree) {
		set_atime_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_atime_tree = proto_item_add_subtree(set_atime_item,
			ett_nfs_set_atime);
	}

	if (set_atime_tree)
		proto_tree_add_text(set_atime_tree, tvb, offset, 4,
			"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case SET_TO_CLIENT_TIME:
			if (set_atime_item) {
				offset = dissect_nfstime3(tvb, offset, set_atime_tree,
					hf_nfs_atime, hf_nfs_atime_sec, hf_nfs_atime_nsec);
			}
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_atime is shorter */
	if (set_atime_item) {
		proto_item_set_len(set_atime_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 26 */
static int
dissect_set_mtime(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* set_mtime_item = NULL;
	proto_tree* set_mtime_tree = NULL;
	int old_offset = offset;
	guint32 set_it;
	const char* set_it_name;

	set_it = tvb_get_ntohl(tvb, offset+0);
	set_it_name = val_to_str(set_it,time_how,"Unknown");

	if (tree) {
		set_mtime_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s: %s", name, set_it_name);
		set_mtime_tree = proto_item_add_subtree(set_mtime_item,
			ett_nfs_set_mtime);
	}

	if (set_mtime_tree)
		proto_tree_add_text(set_mtime_tree, tvb, offset, 4,
				"set_it: %s (%u)", set_it_name, set_it);

	offset += 4;

	switch (set_it) {
		case SET_TO_CLIENT_TIME:
			if (set_mtime_item) {
				offset = dissect_nfstime3(tvb, offset, set_mtime_tree,
					hf_nfs_atime, hf_nfs_atime_sec, hf_nfs_atime_nsec);
			}
		break;
		default:
			/* void */
		break;
	}

	/* now we know, that set_mtime is shorter */
	if (set_mtime_item) {
		proto_item_set_len(set_mtime_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 25..27 */
static int
dissect_sattr3(tvbuff_t *tvb, int offset, proto_tree *tree, const char* name)
{
	proto_item* sattr3_item = NULL;
	proto_tree* sattr3_tree = NULL;
	int old_offset = offset;

	if (tree) {
		sattr3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", name);
		sattr3_tree = proto_item_add_subtree(sattr3_item, ett_nfs_sattr3);
	}

	offset = dissect_set_mode3(tvb, offset, sattr3_tree, "mode");
	offset = dissect_set_uid3 (tvb, offset, sattr3_tree, "uid");
	offset = dissect_set_gid3 (tvb, offset, sattr3_tree, "gid");
	offset = dissect_set_size3(tvb, offset, sattr3_tree, "size");
	offset = dissect_set_atime(tvb, offset, sattr3_tree, "atime");
	offset = dissect_set_mtime(tvb, offset, sattr3_tree, "mtime");

	/* now we know, that sattr3 is shorter */
	if (sattr3_item) {
		proto_item_set_len(sattr3_item, offset - old_offset);
	}

	return offset;
}


/* RFC 1813, Page 27 */
static int
dissect_diropargs3(tvbuff_t *tvb, int offset, packet_info *pinfo,
	proto_tree *tree, const char* label, guint32 *hash, char **name)
{
	proto_item* diropargs3_item = NULL;
	proto_tree* diropargs3_tree = NULL;
	int old_offset = offset;
	int parent_offset, parent_len;
	int name_offset, name_len;

	if (tree) {
		diropargs3_item = proto_tree_add_text(tree, tvb, offset, -1,
			"%s", label);
		diropargs3_tree = proto_item_add_subtree(diropargs3_item,
			ett_nfs_diropargs3);
	}

	parent_offset=offset+4;
	parent_len=tvb_get_ntohl(tvb, offset);
	offset =