Fix Gerald's e-mail address.
[obnox/wireshark/wip.git] / packet-nfs.c
1 /* packet-nfs.c
2  * Routines for nfs dissection
3  * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
4  *
5  * $Id: packet-nfs.c,v 1.18 2000/01/07 22:05:33 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from packet-smb.c
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36
37
38 #include "packet-rpc.h"
39 #include "packet-nfs.h"
40
41
42 static int proto_nfs = -1;
43
44
45 static int hf_nfs_stat = -1;
46 static int hf_nfs_name = -1;
47 static int hf_nfs_readlink_data = -1;
48 static int hf_nfs_read_offset = -1;
49 static int hf_nfs_read_count = -1;
50 static int hf_nfs_read_totalcount = -1;
51 static int hf_nfs_data = -1;
52 static int hf_nfs_write_beginoffset = -1;
53 static int hf_nfs_write_offset = -1;
54 static int hf_nfs_write_totalcount = -1;
55 static int hf_nfs_symlink_to = -1;
56 static int hf_nfs_readdir_cookie = -1;
57 static int hf_nfs_readdir_count = -1;
58 static int hf_nfs_readdir_value_follows = -1;
59 static int hf_nfs_readdir_entry = -1;
60 static int hf_nfs_readdir_entry_fileid = -1;
61 static int hf_nfs_readdir_entry_name = -1;
62 static int hf_nfs_readdir_entry_cookie = -1;
63 static int hf_nfs_readdir_eof = -1;
64 static int hf_nfs_statfs_tsize = -1;
65 static int hf_nfs_statfs_bsize = -1;
66 static int hf_nfs_statfs_blocks = -1;
67 static int hf_nfs_statfs_bfree = -1;
68 static int hf_nfs_statfs_bavail = -1;
69 static int hf_nfs_nfsstat3 = -1;
70 static int hf_nfs_read_eof = -1;
71 static int hf_nfs_write_stable = -1;
72 static int hf_nfs_write_committed = -1;
73 static int hf_nfs_createmode3 = -1;
74 static int hf_nfs_fsstat_invarsec = -1;
75 static int hf_nfs_fsinfo_rtmax = -1;
76 static int hf_nfs_fsinfo_rtpref = -1;
77 static int hf_nfs_fsinfo_rtmult = -1;
78 static int hf_nfs_fsinfo_wtmax = -1;
79 static int hf_nfs_fsinfo_wtpref = -1;
80 static int hf_nfs_fsinfo_wtmult = -1;
81 static int hf_nfs_fsinfo_dtpref = -1;
82 static int hf_nfs_fsinfo_properties = -1;
83 static int hf_nfs_pathconf_linkmax = -1;
84 static int hf_nfs_pathconf_name_max = -1;
85 static int hf_nfs_pathconf_no_trunc = -1;
86 static int hf_nfs_pathconf_chown_restricted = -1;
87 static int hf_nfs_pathconf_case_insensitive = -1;
88 static int hf_nfs_pathconf_case_preserving = -1;
89
90
91 static gint ett_nfs = -1;
92 static gint ett_nfs_fhandle = -1;
93 static gint ett_nfs_timeval = -1;
94 static gint ett_nfs_mode = -1;
95 static gint ett_nfs_fattr = -1;
96 static gint ett_nfs_sattr = -1;
97 static gint ett_nfs_diropargs = -1;
98 static gint ett_nfs_readdir_entry = -1;
99 static gint ett_nfs_mode3 = -1;
100 static gint ett_nfs_specdata3 = -1;
101 static gint ett_nfs_fh3 = -1;
102 static gint ett_nfs_nfstime3 = -1;
103 static gint ett_nfs_fattr3 = -1;
104 static gint ett_nfs_post_op_fh3 = -1;
105 static gint ett_nfs_sattr3 = -1;
106 static gint ett_nfs_diropargs3 = -1;
107 static gint ett_nfs_sattrguard3 = -1;
108 static gint ett_nfs_set_mode3 = -1;
109 static gint ett_nfs_set_uid3 = -1;
110 static gint ett_nfs_set_gid3 = -1;
111 static gint ett_nfs_set_size3 = -1;
112 static gint ett_nfs_set_atime = -1;
113 static gint ett_nfs_set_mtime = -1;
114 static gint ett_nfs_pre_op_attr = -1;
115 static gint ett_nfs_post_op_attr = -1;
116 static gint ett_nfs_wcc_attr = -1;
117 static gint ett_nfs_wcc_data = -1;
118 static gint ett_nfs_access = -1;
119 static gint ett_nfs_fsinfo_properties = -1;
120
121
122 /***************************/
123 /* NFS Version 2, RFC 1094 */
124 /***************************/
125
126
127 /* base 32 bit type for NFS v2 */
128 int
129 dissect_unsigned_int(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
130 char* name)
131 {
132         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"unsigned int");
133         return offset;
134 }
135
136
137 /* RFC 1094, Page 12..14 */
138 const value_string names_nfs_stat[] =
139 {
140         {       0,      "OK" },
141         {       1,      "ERR_PERM" },
142         {       2,      "ERR_NOENT" },
143         {       5,      "ERR_IO" },
144         {       6,      "ERR_NX_IO" },
145         {       13,     "ERR_ACCES" },
146         {       17,     "ERR_EXIST" },
147         {       19,     "ERR_NODEV" },
148         {       20,     "ERR_NOTDIR" },
149         {       21,     "ERR_ISDIR" },
150         {       27,     "ERR_FBIG" },
151         {       28,     "ERR_NOSPC" },
152         {       30,     "ERR_ROFS" },
153         {       63,     "ERR_NAMETOOLONG" },
154         {       66,     "ERR_NOTEMPTY" },
155         {       69,     "ERR_DQUOT" },
156         {       70,     "ERR_STALE" },
157         {       99,     "ERR_WFLUSH" },
158         {       0,      NULL }
159 };
160
161
162 /* RFC 1094, Page 12..14 */
163 int
164 dissect_stat(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
165 guint32* status)
166 {
167         guint32 stat;
168
169         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
170         stat = EXTRACT_UINT(pd, offset+0);
171         
172         if (tree) {
173                 /* this gives the right NFSv2 number<->message relation */
174                 /* and makes it searchable via "nfs.status" */
175                 proto_tree_add_item_format(tree, hf_nfs_nfsstat3,
176                         offset+0, 4, stat, "Status: %s (%u)", 
177                         val_to_str(stat,names_nfs_stat,"%u"), stat);
178         }
179
180         offset += 4;
181         *status = stat;
182         return offset;
183 }
184
185
186 /* RFC 1094, Page 12..14 */
187 int
188 dissect_nfs2_stat_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
189 {
190         guint32 status;
191
192         offset = dissect_stat(pd, offset, fd, tree, &status);
193
194         return offset;
195 }
196
197
198 /* RFC 1094, Page 15 */
199 int
200 dissect_ftype(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
201 char* name)
202 {
203         guint32 ftype;
204         char* ftype_name = NULL;
205
206         const value_string nfs2_ftype[] =
207         {
208                 {       0,      "Non-File" },
209                 {       1,      "Regular File" },
210                 {       2,      "Directory" },
211                 {       3,      "Block Special Device" },
212                 {       4,      "Character Special Device" },
213                 {       5,      "Symbolic Link" },
214                 {       0,      NULL }
215         };
216
217         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
218         ftype = EXTRACT_UINT(pd, offset+0);
219         ftype_name = val_to_str(ftype, nfs2_ftype, "%u");
220         
221         if (tree) {
222                 proto_tree_add_text(tree, offset, 4,
223                         "%s: %s (%u)", name, ftype_name, ftype);
224         }
225
226         offset += 4;
227         return offset;
228 }
229
230
231 /* RFC 1094, Page 15 */
232 int
233 dissect_fhandle(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
234 {
235         proto_item* fitem;
236         proto_tree* ftree = NULL;
237
238         if (tree) {
239                 fitem = proto_tree_add_text(tree, offset, FHSIZE,
240                         "%s", name);
241                 if (fitem)
242                         ftree = proto_item_add_subtree(fitem, ett_nfs_fhandle);
243         }
244
245         if (ftree) {
246                 proto_tree_add_text(ftree,offset+0,FHSIZE,
247                                         "file handle (opaque data)");
248         }
249
250         offset += FHSIZE;
251         return offset;
252 }
253
254
255 /* RFC 1094, Page 15 */
256 int
257 dissect_nfs2_fhandle_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
258 {
259         offset = dissect_fhandle(pd, offset, fd, tree, "object");
260
261         return offset;
262 }
263
264
265 /* RFC 1094, Page 15 */
266 int
267 dissect_timeval(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
268 {
269         guint32 seconds;
270         guint32 mseconds;
271
272         proto_item* time_item;
273         proto_tree* time_tree = NULL;
274
275         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
276         seconds = EXTRACT_UINT(pd, offset+0);
277         mseconds = EXTRACT_UINT(pd, offset+4);
278         
279         if (tree) {
280                 time_item = proto_tree_add_text(tree, offset, 8,
281                         "%s: %u.%06u", name, seconds, mseconds);
282                 if (time_item)
283                         time_tree = proto_item_add_subtree(time_item, ett_nfs_timeval);
284         }
285
286         if (time_tree) {
287                 proto_tree_add_text(time_tree,offset+0,4,
288                                         "seconds: %u", seconds);
289                 proto_tree_add_text(time_tree,offset+4,4,
290                                         "micro seconds: %u", mseconds);
291         }
292         offset += 8;
293         return offset;
294 }
295
296
297 /* RFC 1094, Page 16 */
298 const value_string nfs2_mode_names[] = {
299         {       0040000,        "Directory"     },
300         {       0020000,        "Character Special Device"      },
301         {       0060000,        "Block Special Device"  },
302         {       0100000,        "Regular File"  },
303         {       0120000,        "Symbolic Link" },
304         {       0140000,        "Named Socket"  },
305         {       0000000,        NULL            },
306 };
307
308 int
309 dissect_mode(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
310 char* name)
311 {
312         guint32 mode;
313         proto_item* mode_item = NULL;
314         proto_tree* mode_tree = NULL;
315
316         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
317         mode = EXTRACT_UINT(pd, offset+0);
318         
319         if (tree) {
320                 mode_item = proto_tree_add_text(tree, offset, 4,
321                         "%s: 0%o", name, mode);
322                 if (mode_item)
323                         mode_tree = proto_item_add_subtree(mode_item, ett_nfs_mode);
324         }
325
326         if (mode_tree) {
327                 proto_tree_add_text(mode_tree, offset, 4, "%s",
328                         decode_enumerated_bitfield(mode,  0160000, 16,
329                         nfs2_mode_names, "%s"));
330                 proto_tree_add_text(mode_tree, offset, 4, "%s",
331                 decode_boolean_bitfield(mode,   04000, 16, "Set user id on exec", "not SUID"));
332                 proto_tree_add_text(mode_tree, offset, 4, "%s",
333                 decode_boolean_bitfield(mode,   02000, 16, "Set group id on exec", "not SGID"));
334                 proto_tree_add_text(mode_tree, offset, 4, "%s",
335                 decode_boolean_bitfield(mode,   01000, 16, "Save swapped text even after use", "not save swapped text"));
336                 proto_tree_add_text(mode_tree, offset, 4, "%s",
337                 decode_boolean_bitfield(mode,    0400, 16, "Read permission for owner", "no Read permission for owner"));
338                 proto_tree_add_text(mode_tree, offset, 4, "%s",
339                 decode_boolean_bitfield(mode,    0200, 16, "Write permission for owner", "no Write permission for owner"));
340                 proto_tree_add_text(mode_tree, offset, 4, "%s",
341                 decode_boolean_bitfield(mode,    0100, 16, "Execute permission for owner", "no Execute permission for owner"));
342                 proto_tree_add_text(mode_tree, offset, 4, "%s",
343                 decode_boolean_bitfield(mode,     040, 16, "Read permission for group", "no Read permission for group"));
344                 proto_tree_add_text(mode_tree, offset, 4, "%s",
345                 decode_boolean_bitfield(mode,     020, 16, "Write permission for group", "no Write permission for group"));
346                 proto_tree_add_text(mode_tree, offset, 4, "%s",
347                 decode_boolean_bitfield(mode,     010, 16, "Execute permission for group", "no Execute permission for group"));
348                 proto_tree_add_text(mode_tree, offset, 4, "%s",
349                 decode_boolean_bitfield(mode,      04, 16, "Read permission for others", "no Read permission for others"));
350                 proto_tree_add_text(mode_tree, offset, 4, "%s",
351                 decode_boolean_bitfield(mode,      02, 16, "Write permission for others", "no Write permission for others"));
352                 proto_tree_add_text(mode_tree, offset, 4, "%s",
353                 decode_boolean_bitfield(mode,      01, 16, "Execute permission for others", "no Execute permission for others"));
354         }
355
356         offset += 4;
357         return offset;
358 }
359
360
361 /* RFC 1094, Page 15 */
362 int
363 dissect_fattr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
364 {
365         proto_item* fattr_item = NULL;
366         proto_tree* fattr_tree = NULL;
367         int old_offset = offset;
368
369         if (tree) {
370                 fattr_item = proto_tree_add_text(tree, offset,
371                         END_OF_FRAME, "%s", name);
372                 if (fattr_item)
373                         fattr_tree = proto_item_add_subtree(fattr_item, ett_nfs_fattr);
374         }
375
376         offset = dissect_ftype        (pd,offset,fd,fattr_tree,"type");
377         offset = dissect_mode         (pd,offset,fd,fattr_tree,"mode");
378         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"nlink");
379         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"uid");
380         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"gid");
381         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"size");
382         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"blocksize");
383         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"rdev");
384         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"blocks");
385         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"fsid");
386         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"fileid");
387         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"atime");
388         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"mtime");
389         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"ctime");
390
391         /* now we know, that fattr is shorter */
392         if (fattr_item) {
393                 proto_item_set_len(fattr_item, offset - old_offset);
394         }
395
396         return offset;
397 }
398
399
400 /* RFC 1094, Page 17 */
401 int
402 dissect_sattr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
403 {
404         proto_item* sattr_item = NULL;
405         proto_tree* sattr_tree = NULL;
406         int old_offset = offset;
407
408         if (tree) {
409                 sattr_item = proto_tree_add_text(tree, offset,
410                         END_OF_FRAME, "%s", name);
411                 if (sattr_item)
412                         sattr_tree = proto_item_add_subtree(sattr_item, ett_nfs_sattr);
413         }
414
415         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
416         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
417                 offset = dissect_mode         (pd,offset,fd,sattr_tree,"mode");
418         else {
419                 proto_tree_add_text(sattr_tree, offset, 4, "mode: no value");
420                 offset += 4;
421         }
422
423         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
424         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
425                 offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"uid");
426         else {
427                 proto_tree_add_text(sattr_tree, offset, 4, "uid: no value");
428                 offset += 4;
429         }
430
431         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
432         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
433                 offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"gid");
434         else {
435                 proto_tree_add_text(sattr_tree, offset, 4, "gid: no value");
436                 offset += 4;
437         }
438
439         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
440         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
441                 offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"size");
442         else {
443                 proto_tree_add_text(sattr_tree, offset, 4, "size: no value");
444                 offset += 4;
445         }
446
447         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
448         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
449                 offset = dissect_timeval      (pd,offset,fd,sattr_tree,"atime");
450         else {
451                 proto_tree_add_text(sattr_tree, offset, 8, "atime: no value");
452                 offset += 8;
453         }
454
455         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
456         if (EXTRACT_UINT(pd, offset+0) != 0xffffffff)
457                 offset = dissect_timeval      (pd,offset,fd,sattr_tree,"mtime");
458         else {
459                 proto_tree_add_text(sattr_tree, offset, 8, "mtime: no value");
460                 offset += 8;
461         }
462
463         /* now we know, that sattr is shorter */
464         if (sattr_item) {
465                 proto_item_set_len(sattr_item, offset - old_offset);
466         }
467
468         return offset;
469 }
470
471
472 /* RFC 1094, Page 17 */
473 int
474 dissect_filename(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf)
475 {
476         offset = dissect_rpc_string(pd,offset,fd,tree,hf);
477         return offset;
478 }
479
480
481 /* RFC 1094, Page 17 */
482 int
483 dissect_path(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf)
484 {
485         offset = dissect_rpc_string(pd,offset,fd,tree,hf);
486         return offset;
487 }
488
489
490 /* RFC 1094, Page 17,18 */
491 int
492 dissect_attrstat(const u_char *pd, int offset, frame_data *fd, proto_tree *tree){
493         guint32 status;
494
495         offset = dissect_stat(pd, offset, fd, tree, &status);
496         switch (status) {
497                 case 0:
498                         offset = dissect_fattr(pd, offset, fd, tree, "attributes");
499                 break;
500                 default:
501                         /* do nothing */
502                 break;
503         }
504
505         return offset;
506 }
507
508
509 /* RFC 1094, Page 17,18 */
510 int
511 dissect_nfs2_attrstat_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
512 {
513         offset = dissect_attrstat(pd, offset, fd, tree);
514
515         return offset;
516 }
517
518
519 /* RFC 1094, Page 18 */
520 int
521 dissect_diropargs(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
522 {
523         proto_item* diropargs_item = NULL;
524         proto_tree* diropargs_tree = NULL;
525         int old_offset = offset;
526
527         if (tree) {
528                 diropargs_item = proto_tree_add_text(tree, offset,
529                         END_OF_FRAME, "%s", name);
530                 if (diropargs_item)
531                         diropargs_tree = proto_item_add_subtree(diropargs_item, ett_nfs_diropargs);
532         }
533
534         offset = dissect_fhandle (pd,offset,fd,diropargs_tree,"dir");
535         offset = dissect_filename(pd,offset,fd,diropargs_tree,hf_nfs_name);
536
537         /* now we know, that diropargs is shorter */
538         if (diropargs_item) {
539                 proto_item_set_len(diropargs_item, offset - old_offset);
540         }
541
542         return offset;
543 }
544
545
546 /* RFC 1094, Page 18 */
547 int
548 dissect_nfs2_diropargs_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
549 {
550         offset = dissect_diropargs(pd, offset, fd, tree, "where");
551
552         return offset;
553 }
554
555
556 /* RFC 1094, Page 18 */
557 int
558 dissect_diropres(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
559 {
560         guint32 status;
561
562         offset = dissect_stat(pd, offset, fd, tree, &status);
563         switch (status) {
564                 case 0:
565                         offset = dissect_fhandle(pd, offset, fd, tree, "file");
566                         offset = dissect_fattr  (pd, offset, fd, tree, "attributes");
567                 break;
568                 default:
569                         /* do nothing */
570                 break;
571         }
572
573         return offset;
574 }
575
576
577 /* nfsdata is simply a RPC string (length, data, fill bytes) */
578 int
579 dissect_nfsdata(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
580 int hf)
581 {
582         offset = dissect_rpc_data(pd,offset,fd,tree,hf);
583
584         return offset;
585 }
586
587
588 /* RFC 1094, Page 18 */
589 int
590 dissect_nfs2_diropres_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
591 {
592         offset = dissect_diropres(pd, offset, fd, tree);
593
594         return offset;
595 }
596
597
598 /* RFC 1094, Page 6 */
599 int
600 dissect_nfs2_setattr_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
601 {
602         offset = dissect_fhandle(pd, offset, fd, tree, "file"      );
603         offset = dissect_sattr  (pd, offset, fd, tree, "attributes");
604
605         return offset;
606 }
607
608
609 /* RFC 1094, Page 6 */
610 int
611 dissect_nfs2_readlink_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
612 {
613         guint32 status;
614
615         offset = dissect_stat(pd, offset, fd, tree, &status);
616         switch (status) {
617                 case 0:
618                         offset = dissect_path(pd, offset, fd, tree, hf_nfs_readlink_data);
619                 break;
620                 default:
621                         /* do nothing */
622                 break;
623         }
624
625         return offset;
626 }
627
628
629 /* RFC 1094, Page 7 */
630 int
631 dissect_nfs2_read_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
632 {
633         guint32 offset_value;
634         guint32 count;
635         guint32 totalcount;
636
637         offset = dissect_fhandle(pd, offset, fd, tree, "file"      );
638         if (!BYTES_ARE_IN_FRAME(offset,12)) return offset;
639         offset_value = EXTRACT_UINT(pd, offset+0);
640         count        = EXTRACT_UINT(pd, offset+4);
641         totalcount   = EXTRACT_UINT(pd, offset+8);
642         if (tree) {
643                 proto_tree_add_item(tree, hf_nfs_read_offset, 
644                 offset+0, 4, offset_value);
645                 proto_tree_add_item(tree, hf_nfs_read_count, 
646                 offset+4, 4, count);
647                 proto_tree_add_item(tree, hf_nfs_read_totalcount, 
648                 offset+8, 4, totalcount);
649         }
650         offset += 12;
651
652         return offset;
653 }
654
655
656 /* RFC 1094, Page 7 */
657 int
658 dissect_nfs2_read_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
659 {
660         guint32 status;
661
662         offset = dissect_stat(pd, offset, fd, tree, &status);
663         switch (status) {
664                 case 0:
665                         offset = dissect_fattr(pd, offset, fd, tree, "attributes");
666                         offset = dissect_nfsdata(pd, offset, fd, tree, hf_nfs_data); 
667                 break;
668                 default:
669                         /* do nothing */
670                 break;
671         }
672
673         return offset;
674 }
675
676
677 /* RFC 1094, Page 8 */
678 int
679 dissect_nfs2_write_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
680 {
681         guint32 beginoffset;
682         guint32 offset_value;
683         guint32 totalcount;
684
685         offset = dissect_fhandle(pd, offset, fd, tree, "file"      );
686         if (!BYTES_ARE_IN_FRAME(offset,12)) return offset;
687         beginoffset  = EXTRACT_UINT(pd, offset+0);
688         offset_value = EXTRACT_UINT(pd, offset+4);
689         totalcount   = EXTRACT_UINT(pd, offset+8);
690         if (tree) {
691                 proto_tree_add_item(tree, hf_nfs_write_beginoffset, 
692                 offset+0, 4, beginoffset);
693                 proto_tree_add_item(tree, hf_nfs_write_offset, 
694                 offset+4, 4, offset_value);
695                 proto_tree_add_item(tree, hf_nfs_write_totalcount, 
696                 offset+8, 4, totalcount);
697         }
698         offset += 12;
699
700         offset = dissect_nfsdata(pd, offset, fd, tree, hf_nfs_data); 
701
702         return offset;
703 }
704
705
706 /* RFC 1094, Page 8 */
707 int
708 dissect_nfs2_createargs_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
709 {
710         offset = dissect_diropargs(pd, offset, fd, tree, "where"     );
711         offset = dissect_sattr    (pd, offset, fd, tree, "attributes");
712
713         return offset;
714 }
715
716
717 /* RFC 1094, Page 9 */
718 int
719 dissect_nfs2_rename_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
720 {
721         offset = dissect_diropargs(pd, offset, fd, tree, "from");
722         offset = dissect_diropargs(pd, offset, fd, tree, "to"  );
723
724         return offset;
725 }
726
727
728 /* RFC 1094, Page 9 */
729 int
730 dissect_nfs2_link_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
731 {
732         offset = dissect_fhandle  (pd, offset, fd, tree, "from");
733         offset = dissect_diropargs(pd, offset, fd, tree, "to"  );
734
735         return offset;
736 }
737
738
739 /* RFC 1094, Page 10 */
740 int
741 dissect_nfs2_symlink_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
742 {
743         offset = dissect_diropargs(pd, offset, fd, tree, "from"           );
744         offset = dissect_path     (pd, offset, fd, tree, hf_nfs_symlink_to);
745         offset = dissect_sattr    (pd, offset, fd, tree, "attributes"     );
746
747         return offset;
748 }
749
750
751 /* RFC 1094, Page 11 */
752 int
753 dissect_nfs2_readdir_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
754 {
755         guint32 cookie;
756         guint32 count;
757
758         offset = dissect_fhandle (pd, offset, fd, tree, "dir");
759         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
760         cookie  = EXTRACT_UINT(pd, offset+ 0);
761         count = EXTRACT_UINT(pd, offset+ 4);
762         if (tree) {
763                 proto_tree_add_item(tree, hf_nfs_readdir_cookie,
764                         offset+ 0, 4, cookie);
765                 proto_tree_add_item(tree, hf_nfs_readdir_count,
766                         offset+ 4, 4, count);
767         }
768         offset += 8;
769
770         return offset;
771 }
772
773
774 /* RFC 1094, Page 11 */
775 int
776 dissect_readdir_entry(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
777 {
778         proto_item* entry_item = NULL;
779         proto_tree* entry_tree = NULL;
780         int old_offset = offset;
781         guint32 fileid;
782         guint32 cookie;
783
784         if (tree) {
785                 entry_item = proto_tree_add_item(tree, hf_nfs_readdir_entry,
786                         offset+0, END_OF_FRAME, NULL);
787                 if (entry_item)
788                         entry_tree = proto_item_add_subtree(entry_item, ett_nfs_readdir_entry);
789         }
790
791         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
792         fileid = EXTRACT_UINT(pd, offset + 0);
793         if (entry_tree)
794                 proto_tree_add_item(entry_tree, hf_nfs_readdir_entry_fileid,
795                         offset+0, 4, fileid);
796         offset += 4;
797
798         offset = dissect_filename(pd, offset, fd, entry_tree, hf_nfs_readdir_entry_name);
799         
800         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
801         cookie = EXTRACT_UINT(pd, offset + 0);
802         if (entry_tree)
803                 proto_tree_add_item(entry_tree, hf_nfs_readdir_entry_cookie,
804                         offset+0, 4, cookie);
805         offset += 4;
806
807         /* now we know, that a readdir entry is shorter */
808         if (entry_item) {
809                 proto_item_set_len(entry_item, offset - old_offset);
810         }
811
812         return offset;
813 }
814
815 /* RFC 1094, Page 11 */
816 int
817 dissect_nfs2_readdir_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
818 {
819         guint32 status;
820         guint32 value_follows;
821         guint32 eof_value;
822
823         offset = dissect_stat(pd, offset, fd, tree, &status);
824         switch (status) {
825                 case 0:
826                         while (1) {
827                                 if (!BYTES_ARE_IN_FRAME(offset,4)) break;
828                                 value_follows = EXTRACT_UINT(pd, offset+0);
829                                 proto_tree_add_item(tree,hf_nfs_readdir_value_follows,
830                                         offset+0, 4, value_follows);
831                                 offset += 4;
832                                 if (value_follows == 1) {
833                                         offset = dissect_readdir_entry(pd, offset, fd, tree);
834                                 }
835                                 else {
836                                         break;
837                                 }
838                         }
839                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
840                         eof_value = EXTRACT_UINT(pd, offset+0);
841                         if (tree)
842                                 proto_tree_add_item(tree, hf_nfs_readdir_eof,
843                                         offset+ 0, 4, eof_value);
844                         offset += 4;
845                 break;
846                 default:
847                         /* do nothing */
848                 break;
849         }
850
851         return offset;
852 }
853
854
855 /* RFC 1094, Page 12 */
856 int
857 dissect_nfs2_statfs_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
858 {
859         guint32 status;
860         guint32 tsize;
861         guint32 bsize;
862         guint32 blocks;
863         guint32 bfree;
864         guint32 bavail;
865
866         offset = dissect_stat(pd, offset, fd, tree, &status);
867         switch (status) {
868                 case 0:
869                         if (!BYTES_ARE_IN_FRAME(offset,5 * 4)) return offset;
870                         tsize  = EXTRACT_UINT(pd, offset+ 0);
871                         bsize  = EXTRACT_UINT(pd, offset+ 4);
872                         blocks = EXTRACT_UINT(pd, offset+ 8);
873                         bfree  = EXTRACT_UINT(pd, offset+12);
874                         bavail = EXTRACT_UINT(pd, offset+16);
875                         if (tree) {
876                                 proto_tree_add_item(tree, hf_nfs_statfs_tsize,
877                                         offset+ 0, 4, tsize);
878                                 proto_tree_add_item(tree, hf_nfs_statfs_bsize,
879                                         offset+ 4, 4, bsize);
880                                 proto_tree_add_item(tree, hf_nfs_statfs_blocks,
881                                         offset+ 8, 4, blocks);
882                                 proto_tree_add_item(tree, hf_nfs_statfs_bfree,
883                                         offset+12, 4, bfree);
884                                 proto_tree_add_item(tree, hf_nfs_statfs_bavail,
885                                         offset+16, 4, bavail);
886                         }
887                         offset += 20;
888                 break;
889                 default:
890                         /* do nothing */
891                 break;
892         }
893
894         return offset;
895 }
896
897
898 /* proc number, "proc name", dissect_request, dissect_reply */
899 /* NULL as function pointer means: take the generic one. */
900 const vsff nfs2_proc[] = {
901         { 0,    "NULL",         /* OK */
902         NULL,                           NULL },
903         { 1,    "GETATTR",      /* OK */
904         dissect_nfs2_fhandle_call,      dissect_nfs2_attrstat_reply },
905         { 2,    "SETATTR",      /* OK */
906         dissect_nfs2_setattr_call,      dissect_nfs2_attrstat_reply },
907         { 3,    "ROOT",         /* OK */
908         NULL,                           NULL },
909         { 4,    "LOOKUP",       /* OK */
910         dissect_nfs2_diropargs_call,    dissect_nfs2_diropres_reply },
911         { 5,    "READLINK",     /* OK */
912         dissect_nfs2_fhandle_call,      dissect_nfs2_readlink_reply },
913         { 6,    "READ",         /* OK */
914         dissect_nfs2_read_call,         dissect_nfs2_read_reply },
915         { 7,    "WRITECACHE",   /* OK */
916         NULL,                           NULL },
917         { 8,    "WRITE",        /* OK */
918         dissect_nfs2_write_call,        dissect_nfs2_attrstat_reply },
919         { 9,    "CREATE",       /* OK */
920         dissect_nfs2_createargs_call,   dissect_nfs2_diropres_reply },
921         { 10,   "REMOVE",       /* OK */
922         dissect_nfs2_diropargs_call,    dissect_nfs2_stat_reply },
923         { 11,   "RENAME",       /* OK */
924         dissect_nfs2_rename_call,       dissect_nfs2_stat_reply },
925         { 12,   "LINK",         /* OK */
926         dissect_nfs2_link_call,         dissect_nfs2_stat_reply },
927         { 13,   "SYMLINK",      /* OK */
928         dissect_nfs2_symlink_call,      dissect_nfs2_stat_reply },
929         { 14,   "MKDIR",        /* OK */
930         dissect_nfs2_createargs_call,   dissect_nfs2_diropres_reply },
931         { 15,   "RMDIR",        /* OK */
932         dissect_nfs2_diropargs_call,    dissect_nfs2_stat_reply },
933         { 16,   "READDIR",      /* OK */
934         dissect_nfs2_readdir_call,      dissect_nfs2_readdir_reply },
935         { 17,   "STATFS",       /* OK */
936         dissect_nfs2_fhandle_call,      dissect_nfs2_statfs_reply },
937         { 0,NULL,NULL,NULL }
938 };
939 /* end of NFS Version 2 */
940
941
942 /***************************/
943 /* NFS Version 3, RFC 1813 */
944 /***************************/
945
946
947 /* RFC 1813, Page 15 */
948 int
949 dissect_uint64(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
950 char* name)
951 {
952         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"uint64");
953         return offset;
954 }
955
956
957 /* RFC 1813, Page 15 */
958 int
959 dissect_uint32(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
960 char* name)
961 {
962         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"uint32");
963         return offset;
964 }
965
966
967 /* RFC 1813, Page 15 */
968 int
969 dissect_filename3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf)
970 {
971         offset = dissect_rpc_string(pd,offset,fd,tree,hf);
972         return offset;
973 }
974
975
976 /* RFC 1813, Page 15 */
977 int
978 dissect_nfspath3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf)
979 {
980         offset = dissect_rpc_string(pd,offset,fd,tree,hf);
981         return offset;
982 }
983
984
985 /* RFC 1813, Page 15 */
986 int
987 dissect_fileid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
988 char* name)
989 {
990         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"fileid3");
991         return offset;
992 }
993
994
995 /* RFC 1813, Page 15 */
996 int
997 dissect_cookie3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
998 char* name)
999 {
1000         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"cookie3");
1001         return offset;
1002 }
1003
1004
1005 /* RFC 1813, Page 15 */
1006 int
1007 dissect_cookieverf3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
1008 {
1009         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
1010         proto_tree_add_text(tree, offset, NFS3_COOKIEVERFSIZE,
1011                 "Verifier: Opaque Data");
1012         offset += NFS3_COOKIEVERFSIZE;
1013         return offset;
1014 }
1015
1016
1017 /* RFC 1813, Page 16 */
1018 int
1019 dissect_createverf3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
1020 {
1021         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
1022         proto_tree_add_text(tree, offset, NFS3_CREATEVERFSIZE,
1023                 "Verifier: Opaque Data");
1024         offset += NFS3_CREATEVERFSIZE;
1025         return offset;
1026 }
1027
1028
1029 /* RFC 1813, Page 16 */
1030 int
1031 dissect_writeverf3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
1032 {
1033         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
1034         proto_tree_add_text(tree, offset, NFS3_WRITEVERFSIZE,
1035                 "Verifier: Opaque Data");
1036         offset += NFS3_WRITEVERFSIZE;
1037         return offset;
1038 }
1039
1040
1041 /* RFC 1813, Page 16 */
1042 int
1043 dissect_uid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1044 char* name)
1045 {
1046         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"uid3"); 
1047         return offset;
1048 }
1049
1050
1051 /* RFC 1813, Page 16 */
1052 int
1053 dissect_gid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1054 char* name)
1055 {
1056         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"gid3"); 
1057         return offset;
1058 }
1059
1060
1061 /* RFC 1813, Page 16 */
1062 int
1063 dissect_size3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1064 char* name)
1065 {
1066         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"size3"); 
1067         return offset;
1068 }
1069
1070
1071 /* RFC 1813, Page 16 */
1072 int
1073 dissect_offset3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1074 char* name)
1075 {
1076         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"offset3"); 
1077         return offset;
1078 }
1079
1080
1081 /* RFC 1813, Page 16 */
1082 int
1083 dissect_mode3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1084 char* name)
1085 {
1086         guint32 mode3;
1087         proto_item* mode3_item = NULL;
1088         proto_tree* mode3_tree = NULL;
1089
1090         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1091         mode3 = EXTRACT_UINT(pd, offset+0);
1092         
1093         if (tree) {
1094                 mode3_item = proto_tree_add_text(tree, offset, 4,
1095                         "%s: 0%o", name, mode3);
1096                 if (mode3_item)
1097                         mode3_tree = proto_item_add_subtree(mode3_item, ett_nfs_mode3);
1098         }
1099
1100         /* RFC 1813, Page 23 */
1101         if (mode3_tree) {
1102                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1103                 decode_boolean_bitfield(mode3,   0x800, 12, "Set user id on exec", "not SUID"));
1104                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1105                 decode_boolean_bitfield(mode3,   0x400, 12, "Set group id on exec", "not SGID"));
1106                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1107                 decode_boolean_bitfield(mode3,   0x200, 12, "Save swapped text even after use", "not save swapped text"));
1108                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1109                 decode_boolean_bitfield(mode3,   0x100, 12, "Read permission for owner", "no Read permission for owner"));
1110                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1111                 decode_boolean_bitfield(mode3,    0x80, 12, "Write permission for owner", "no Write permission for owner"));
1112                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1113                 decode_boolean_bitfield(mode3,    0x40, 12, "Execute permission for owner", "no Execute permission for owner"));
1114                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1115                 decode_boolean_bitfield(mode3,    0x20, 12, "Read permission for group", "no Read permission for group"));
1116                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1117                 decode_boolean_bitfield(mode3,    0x10, 12, "Write permission for group", "no Write permission for group"));
1118                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1119                 decode_boolean_bitfield(mode3,     0x8, 12, "Execute permission for group", "no Execute permission for group"));
1120                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1121                 decode_boolean_bitfield(mode3,     0x4, 12, "Read permission for others", "no Read permission for others"));
1122                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1123                 decode_boolean_bitfield(mode3,     0x2, 12, "Write permission for others", "no Write permission for others"));
1124                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
1125                 decode_boolean_bitfield(mode3,     0x1, 12, "Execute permission for others", "no Execute permission for others"));
1126         }
1127
1128         offset += 4;
1129         return offset;
1130 }
1131
1132
1133 /* RFC 1813, Page 16 */
1134 int
1135 dissect_count3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1136 char* name)
1137 {
1138         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"count3");
1139         return offset;
1140 }
1141
1142
1143 /* RFC 1813, Page 16,17 */
1144 const value_string names_nfs_nfsstat3[] =
1145 {
1146         {       0,      "OK" },
1147         {       1,      "ERR_PERM" },
1148         {       2,      "ERR_NOENT" },
1149         {       5,      "ERR_IO" },
1150         {       6,      "ERR_NX_IO" },
1151         {       13,     "ERR_ACCES" },
1152         {       17,     "ERR_EXIST" },
1153         {       18,     "ERR_XDEV" },
1154         {       19,     "ERR_NODEV" },
1155         {       20,     "ERR_NOTDIR" },
1156         {       21,     "ERR_ISDIR" },
1157         {       22,     "ERR_INVAL" },
1158         {       27,     "ERR_FBIG" },
1159         {       28,     "ERR_NOSPC" },
1160         {       30,     "ERR_ROFS" },
1161         {       31,     "ERR_MLINK" },
1162         {       63,     "ERR_NAMETOOLONG" },
1163         {       66,     "ERR_NOTEMPTY" },
1164         {       69,     "ERR_DQUOT" },
1165         {       70,     "ERR_STALE" },
1166         {       71,     "ERR_REMOTE" },
1167         {       10001,  "ERR_BADHANDLE" },
1168         {       10002,  "ERR_NOT_SYNC" },
1169         {       10003,  "ERR_BAD_COOKIE" },
1170         {       10004,  "ERR_NOTSUPP" },
1171         {       10005,  "ERR_TOOSMALL" },
1172         {       10006,  "ERR_SERVERFAULT" },
1173         {       10007,  "ERR_BADTYPE" },
1174         {       10008,  "ERR_JUKEBOX" },
1175         {       0,      NULL }
1176 };
1177
1178
1179 /* RFC 1813, Page 16 */
1180 int
1181 dissect_nfsstat3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,guint32 *status)
1182 {
1183         guint32 nfsstat3;
1184
1185         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1186         nfsstat3 = EXTRACT_UINT(pd, offset+0);
1187         
1188         if (tree) {
1189                 proto_tree_add_item(tree, hf_nfs_nfsstat3,
1190                         offset, 4, nfsstat3);
1191         }
1192
1193         offset += 4;
1194         *status = nfsstat3;
1195         return offset;
1196 }
1197
1198
1199 /* RFC 1813, Page 17, 18, 19, 20: error explanations */
1200
1201
1202 /* RFC 1813, Page 20 */
1203 int
1204 dissect_ftype3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
1205 char* name)
1206 {
1207         guint32 ftype3;
1208         char* ftype3_name = NULL;
1209
1210         const value_string nfs3_ftype3[] =
1211         {
1212                 {       1,      "Regular File" },
1213                 {       2,      "Directory" },
1214                 {       3,      "Block Special Device" },
1215                 {       4,      "Character Special Device" },
1216                 {       5,      "Symbolic Link" },
1217                 {       6,      "Socket" },
1218                 {       7,      "Named Pipe" },
1219                 {       0,      NULL }
1220         };
1221
1222         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1223         ftype3 = EXTRACT_UINT(pd, offset+0);
1224         ftype3_name = val_to_str(ftype3, nfs3_ftype3, "%u");
1225         
1226         if (tree) {
1227                 proto_tree_add_text(tree, offset, 4,
1228                         "%s: %s (%u)", name, ftype3_name, ftype3);
1229         }
1230
1231         offset += 4;
1232         return offset;
1233 }
1234
1235
1236 /* RFC 1813, Page 20 */
1237 int
1238 dissect_specdata3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1239 {
1240         guint32 specdata1;
1241         guint32 specdata2;
1242
1243         proto_item* specdata3_item;
1244         proto_tree* specdata3_tree = NULL;
1245
1246         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
1247         specdata1 = EXTRACT_UINT(pd, offset+0);
1248         specdata2 = EXTRACT_UINT(pd, offset+4);
1249         
1250         if (tree) {
1251                 specdata3_item = proto_tree_add_text(tree, offset, 8,
1252                         "%s: %u,%u", name, specdata1, specdata2);
1253                 if (specdata3_item)
1254                         specdata3_tree = proto_item_add_subtree(specdata3_item,
1255                                         ett_nfs_specdata3);
1256         }
1257
1258         if (specdata3_tree) {
1259                 proto_tree_add_text(specdata3_tree,offset+0,4,
1260                                         "specdata1: %u", specdata1);
1261                 proto_tree_add_text(specdata3_tree,offset+4,4,
1262                                         "specdata2: %u", specdata2);
1263         }
1264
1265         offset += 8;
1266         return offset;
1267 }
1268
1269
1270 /* RFC 1813, Page 21 */
1271 int
1272 dissect_nfs_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1273 {
1274         guint fh3_len;
1275         guint fh3_len_full;
1276         guint fh3_fill;
1277         proto_item* fitem;
1278         proto_tree* ftree = NULL;
1279
1280         fh3_len = EXTRACT_UINT(pd, offset+0);
1281         fh3_len_full = rpc_roundup(fh3_len);
1282         fh3_fill = fh3_len_full - fh3_len;
1283         
1284         if (tree) {
1285                 fitem = proto_tree_add_text(tree, offset, 4+fh3_len_full,
1286                         "%s", name);
1287                 if (fitem)
1288                         ftree = proto_item_add_subtree(fitem, ett_nfs_fh3);
1289         }
1290
1291         if (ftree) {
1292                 proto_tree_add_text(ftree,offset+0,4,
1293                                         "length: %u", fh3_len);
1294                 proto_tree_add_text(ftree,offset+4,fh3_len,
1295                                         "file handle (opaque data)");
1296                 if (fh3_fill)
1297                         proto_tree_add_text(ftree,offset+4+fh3_len,fh3_fill,
1298                                 "fill bytes");
1299         }
1300         offset += 4 + fh3_len_full;
1301         return offset;
1302 }
1303
1304
1305 /* RFC 1813, Page 21 */
1306 int
1307 dissect_nfstime3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,char* name)
1308 {
1309         guint32 seconds;
1310         guint32 nseconds;
1311
1312         proto_item* time_item;
1313         proto_tree* time_tree = NULL;
1314
1315         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
1316         seconds = EXTRACT_UINT(pd, offset+0);
1317         nseconds = EXTRACT_UINT(pd, offset+4);
1318         
1319         if (tree) {
1320                 time_item = proto_tree_add_text(tree, offset, 8,
1321                         "%s: %u.%09u", name, seconds, nseconds);
1322                 if (time_item)
1323                         time_tree = proto_item_add_subtree(time_item, ett_nfs_nfstime3);
1324         }
1325
1326         if (time_tree) {
1327                 proto_tree_add_text(time_tree,offset+0,4,
1328                                         "seconds: %u", seconds);
1329                 proto_tree_add_text(time_tree,offset+4,4,
1330                                         "nano seconds: %u", nseconds);
1331         }
1332         offset += 8;
1333         return offset;
1334 }
1335
1336
1337 /* RFC 1813, Page 22 */
1338 int
1339 dissect_fattr3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1340 {
1341         proto_item* fattr3_item = NULL;
1342         proto_tree* fattr3_tree = NULL;
1343         int old_offset = offset;
1344
1345         if (tree) {
1346                 fattr3_item = proto_tree_add_text(tree, offset,
1347                         END_OF_FRAME, "%s", name);
1348                 if (fattr3_item)
1349                         fattr3_tree = proto_item_add_subtree(fattr3_item, ett_nfs_fattr3);
1350         }
1351
1352         offset = dissect_ftype3   (pd,offset,fd,fattr3_tree,"type");
1353         offset = dissect_mode3    (pd,offset,fd,fattr3_tree,"mode");
1354         offset = dissect_uint32   (pd,offset,fd,fattr3_tree,"nlink");
1355         offset = dissect_uid3     (pd,offset,fd,fattr3_tree,"uid");
1356         offset = dissect_gid3     (pd,offset,fd,fattr3_tree,"gid");
1357         offset = dissect_size3    (pd,offset,fd,fattr3_tree,"size");
1358         offset = dissect_size3    (pd,offset,fd,fattr3_tree,"used");
1359         offset = dissect_specdata3(pd,offset,fd,fattr3_tree,"rdev");
1360         offset = dissect_uint64   (pd,offset,fd,fattr3_tree,"fsid");
1361         offset = dissect_fileid3  (pd,offset,fd,fattr3_tree,"fileid");
1362         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"atime");
1363         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"mtime");
1364         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"ctime");
1365
1366         /* now we know, that fattr3 is shorter */
1367         if (fattr3_item) {
1368                 proto_item_set_len(fattr3_item, offset - old_offset);
1369         }
1370
1371         return offset;
1372 }
1373
1374
1375 const value_string value_follows[] =
1376         {
1377                 { 0, "no value" },
1378                 { 1, "value follows"},
1379                 { 0, NULL }
1380         };
1381
1382
1383 /* RFC 1813, Page 23 */
1384 int
1385 dissect_post_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1386 {
1387         proto_item* post_op_attr_item = NULL;
1388         proto_tree* post_op_attr_tree = NULL;
1389         int old_offset = offset;
1390         guint32 attributes_follow;
1391
1392         if (tree) {
1393                 post_op_attr_item = proto_tree_add_text(tree, offset,
1394                         END_OF_FRAME, "%s", name);
1395                 if (post_op_attr_item)
1396                         post_op_attr_tree = proto_item_add_subtree(post_op_attr_item, ett_nfs_post_op_attr);
1397         }
1398
1399         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1400         attributes_follow = EXTRACT_UINT(pd, offset+0);
1401         proto_tree_add_text(post_op_attr_tree, offset, 4,
1402                 "attributes_follow: %s (%u)", 
1403                 val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
1404         offset += 4;
1405         switch (attributes_follow) {
1406                 case TRUE:
1407                         offset = dissect_fattr3(pd, offset, fd, post_op_attr_tree,
1408                                         "attributes");
1409                 break;
1410                 case FALSE:
1411                         /* void */
1412                 break;
1413         }
1414         
1415         /* now we know, that post_op_attr_tree is shorter */
1416         if (post_op_attr_item) {
1417                 proto_item_set_len(post_op_attr_item, offset - old_offset);
1418         }
1419
1420         return offset;
1421 }
1422
1423
1424 /* RFC 1813, Page 24 */
1425 int
1426 dissect_wcc_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1427 {
1428         proto_item* wcc_attr_item = NULL;
1429         proto_tree* wcc_attr_tree = NULL;
1430         int old_offset = offset;
1431
1432         if (tree) {
1433                 wcc_attr_item = proto_tree_add_text(tree, offset,
1434                         END_OF_FRAME, "%s", name);
1435                 if (wcc_attr_item)
1436                         wcc_attr_tree = proto_item_add_subtree(wcc_attr_item, ett_nfs_wcc_attr);
1437         }
1438
1439         offset = dissect_size3   (pd, offset, fd, wcc_attr_tree, "size" );
1440         offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "mtime");
1441         offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "ctime");
1442         
1443         /* now we know, that wcc_attr_tree is shorter */
1444         if (wcc_attr_item) {
1445                 proto_item_set_len(wcc_attr_item, offset - old_offset);
1446         }
1447
1448         return offset;
1449 }
1450
1451
1452 /* RFC 1813, Page 24 */
1453 int
1454 dissect_pre_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1455 {
1456         proto_item* pre_op_attr_item = NULL;
1457         proto_tree* pre_op_attr_tree = NULL;
1458         int old_offset = offset;
1459         guint32 attributes_follow;
1460
1461         if (tree) {
1462                 pre_op_attr_item = proto_tree_add_text(tree, offset,
1463                         END_OF_FRAME, "%s", name);
1464                 if (pre_op_attr_item)
1465                         pre_op_attr_tree = proto_item_add_subtree(pre_op_attr_item, ett_nfs_pre_op_attr);
1466         }
1467
1468         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1469         attributes_follow = EXTRACT_UINT(pd, offset+0);
1470         proto_tree_add_text(pre_op_attr_tree, offset, 4,
1471                 "attributes_follow: %s (%u)", 
1472                 val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
1473         offset += 4;
1474         switch (attributes_follow) {
1475                 case TRUE:
1476                         offset = dissect_wcc_attr(pd, offset, fd, pre_op_attr_tree,
1477                                         "attributes");
1478                 break;
1479                 case FALSE:
1480                         /* void */
1481                 break;
1482         }
1483         
1484         /* now we know, that pre_op_attr_tree is shorter */
1485         if (pre_op_attr_item) {
1486                 proto_item_set_len(pre_op_attr_item, offset - old_offset);
1487         }
1488
1489         return offset;
1490 }
1491
1492
1493 /* RFC 1813, Page 24 */
1494 int
1495 dissect_wcc_data(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1496 {
1497         proto_item* wcc_data_item = NULL;
1498         proto_tree* wcc_data_tree = NULL;
1499         int old_offset = offset;
1500
1501         if (tree) {
1502                 wcc_data_item = proto_tree_add_text(tree, offset,
1503                         END_OF_FRAME, "%s", name);
1504                 if (wcc_data_item)
1505                         wcc_data_tree = proto_item_add_subtree(wcc_data_item, ett_nfs_wcc_data);
1506         }
1507
1508         offset = dissect_pre_op_attr (pd, offset, fd, wcc_data_tree, "before");
1509         offset = dissect_post_op_attr(pd, offset, fd, wcc_data_tree, "after" );
1510
1511         /* now we know, that wcc_data is shorter */
1512         if (wcc_data_item) {
1513                 proto_item_set_len(wcc_data_item, offset - old_offset);
1514         }
1515
1516         return offset;
1517 }
1518
1519
1520 /* RFC 1813, Page 25 */
1521 int
1522 dissect_post_op_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1523 {
1524         proto_item* post_op_fh3_item = NULL;
1525         proto_tree* post_op_fh3_tree = NULL;
1526         int old_offset = offset;
1527         guint32 handle_follows;
1528
1529         if (tree) {
1530                 post_op_fh3_item = proto_tree_add_text(tree, offset,
1531                         END_OF_FRAME, "%s", name);
1532                 if (post_op_fh3_item)
1533                         post_op_fh3_tree = proto_item_add_subtree(post_op_fh3_item, ett_nfs_post_op_fh3);
1534         }
1535
1536         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1537         handle_follows = EXTRACT_UINT(pd, offset+0);
1538         proto_tree_add_text(post_op_fh3_tree, offset, 4,
1539                 "handle_follows: %s (%u)", 
1540                 val_to_str(handle_follows,value_follows,"Unknown"), handle_follows);
1541         offset += 4;
1542         switch (handle_follows) {
1543                 case TRUE:
1544                         offset = dissect_nfs_fh3(pd, offset, fd, post_op_fh3_tree,
1545                                         "handle");
1546                 break;
1547                 case FALSE:
1548                         /* void */
1549                 break;
1550         }
1551         
1552         /* now we know, that post_op_fh3_tree is shorter */
1553         if (post_op_fh3_item) {
1554                 proto_item_set_len(post_op_fh3_item, offset - old_offset);
1555         }
1556
1557         return offset;
1558 }
1559
1560
1561 /* RFC 1813, Page 25 */
1562 int
1563 dissect_set_mode3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1564 {
1565         proto_item* set_mode3_item = NULL;
1566         proto_tree* set_mode3_tree = NULL;
1567         int old_offset = offset;
1568         guint32 set_it;
1569         char* set_it_name;
1570
1571         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1572         set_it = EXTRACT_UINT(pd, offset+0);
1573         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1574
1575         if (tree) {
1576                 set_mode3_item = proto_tree_add_text(tree, offset,
1577                         END_OF_FRAME, "%s: %s", name, set_it_name);
1578                 if (set_mode3_item)
1579                         set_mode3_tree = proto_item_add_subtree(set_mode3_item, ett_nfs_set_mode3);
1580         }
1581
1582         if (set_mode3_tree)
1583                 proto_tree_add_text(set_mode3_tree, offset, 4,
1584                         "set_it: %s (%u)", set_it_name, set_it);
1585
1586         offset += 4;
1587
1588         switch (set_it) {
1589                 case 1:
1590                         offset = dissect_mode3(pd, offset, fd, set_mode3_tree,
1591                                         "mode");
1592                 break;
1593                 default:
1594                         /* void */
1595                 break;
1596         }
1597         
1598         /* now we know, that set_mode3 is shorter */
1599         if (set_mode3_item) {
1600                 proto_item_set_len(set_mode3_item, offset - old_offset);
1601         }
1602
1603         return offset;
1604 }
1605
1606
1607 /* RFC 1813, Page 26 */
1608 int
1609 dissect_set_uid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1610 {
1611         proto_item* set_uid3_item = NULL;
1612         proto_tree* set_uid3_tree = NULL;
1613         int old_offset = offset;
1614         guint32 set_it;
1615         char* set_it_name;
1616
1617         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1618         set_it = EXTRACT_UINT(pd, offset+0);
1619         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1620
1621         if (tree) {
1622                 set_uid3_item = proto_tree_add_text(tree, offset,
1623                         END_OF_FRAME, "%s: %s", name, set_it_name);
1624                 if (set_uid3_item)
1625                         set_uid3_tree = proto_item_add_subtree(set_uid3_item, ett_nfs_set_uid3);
1626         }
1627
1628         if (set_uid3_tree)
1629                 proto_tree_add_text(set_uid3_tree, offset, 4,
1630                         "set_it: %s (%u)", set_it_name, set_it);
1631
1632         offset += 4;
1633
1634         switch (set_it) {
1635                 case 1:
1636                         offset = dissect_uid3(pd, offset, fd, set_uid3_tree,
1637                                         "uid");
1638                 break;
1639                 default:
1640                         /* void */
1641                 break;
1642         }
1643
1644         /* now we know, that set_uid3 is shorter */
1645         if (set_uid3_item) {
1646                 proto_item_set_len(set_uid3_item, offset - old_offset);
1647         }
1648
1649         return offset;
1650 }
1651
1652
1653 /* RFC 1813, Page 26 */
1654 int
1655 dissect_set_gid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1656 {
1657         proto_item* set_gid3_item = NULL;
1658         proto_tree* set_gid3_tree = NULL;
1659         int old_offset = offset;
1660         guint32 set_it;
1661         char* set_it_name;
1662
1663         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1664         set_it = EXTRACT_UINT(pd, offset+0);
1665         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1666
1667         if (tree) {
1668                 set_gid3_item = proto_tree_add_text(tree, offset,
1669                         END_OF_FRAME, "%s: %s", name, set_it_name);
1670                 if (set_gid3_item)
1671                         set_gid3_tree = proto_item_add_subtree(set_gid3_item, ett_nfs_set_gid3);
1672         }
1673
1674         if (set_gid3_tree)
1675                 proto_tree_add_text(set_gid3_tree, offset, 4,
1676                         "set_it: %s (%u)", set_it_name, set_it);
1677
1678         offset += 4;
1679
1680         switch (set_it) {
1681                 case 1:
1682                         offset = dissect_gid3(pd, offset, fd, set_gid3_tree,
1683                                         "gid");
1684                 break;
1685                 default:
1686                         /* void */
1687                 break;
1688         }
1689
1690         /* now we know, that set_gid3 is shorter */
1691         if (set_gid3_item) {
1692                 proto_item_set_len(set_gid3_item, offset - old_offset);
1693         }
1694
1695         return offset;
1696 }
1697
1698
1699 /* RFC 1813, Page 26 */
1700 int
1701 dissect_set_size3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1702 {
1703         proto_item* set_size3_item = NULL;
1704         proto_tree* set_size3_tree = NULL;
1705         int old_offset = offset;
1706         guint32 set_it;
1707         char* set_it_name;
1708
1709         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1710         set_it = EXTRACT_UINT(pd, offset+0);
1711         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1712
1713         if (tree) {
1714                 set_size3_item = proto_tree_add_text(tree, offset,
1715                         END_OF_FRAME, "%s: %s", name, set_it_name);
1716                 if (set_size3_item)
1717                         set_size3_tree = proto_item_add_subtree(set_size3_item, ett_nfs_set_size3);
1718         }
1719
1720         if (set_size3_tree)
1721                 proto_tree_add_text(set_size3_tree, offset, 4,
1722                         "set_it: %s (%u)", set_it_name, set_it);
1723
1724         offset += 4;
1725
1726         switch (set_it) {
1727                 case 1:
1728                         offset = dissect_size3(pd, offset, fd, set_size3_tree,
1729                                         "size");
1730                 break;
1731                 default:
1732                         /* void */
1733                 break;
1734         }
1735
1736         /* now we know, that set_size3 is shorter */
1737         if (set_size3_item) {
1738                 proto_item_set_len(set_size3_item, offset - old_offset);
1739         }
1740
1741         return offset;
1742 }
1743
1744
1745 /* RFC 1813, Page 25 */
1746 #define DONT_CHANGE 0
1747 #define SET_TO_SERVER_TIME 1
1748 #define SET_TO_CLIENT_TIME 2
1749
1750 const value_string time_how[] =
1751         {
1752                 { DONT_CHANGE,  "don't change" },
1753                 { SET_TO_SERVER_TIME, "set to server time" },
1754                 { SET_TO_CLIENT_TIME, "set to client time" },
1755                 { 0, NULL }
1756         };
1757
1758
1759 /* RFC 1813, Page 26 */
1760 int
1761 dissect_set_atime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1762 {
1763         proto_item* set_atime_item = NULL;
1764         proto_tree* set_atime_tree = NULL;
1765         int old_offset = offset;
1766         guint32 set_it;
1767         char* set_it_name;
1768
1769         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1770         set_it = EXTRACT_UINT(pd, offset+0);
1771         set_it_name = val_to_str(set_it,time_how,"Unknown");
1772
1773         if (tree) {
1774                 set_atime_item = proto_tree_add_text(tree, offset,
1775                         END_OF_FRAME, "%s: %s",
1776                         name, set_it_name, set_it);
1777                 if (set_atime_item)
1778                         set_atime_tree = proto_item_add_subtree(set_atime_item, ett_nfs_set_atime);
1779         }
1780
1781         if (set_atime_tree)
1782                 proto_tree_add_text(set_atime_tree, offset, 4,
1783                         "set_it: %s (%u)", set_it_name, set_it);
1784
1785         offset += 4;
1786
1787         switch (set_it) {
1788                 case SET_TO_CLIENT_TIME:
1789                         if (set_atime_item)
1790                         offset = dissect_nfstime3(pd, offset, fd, set_atime_tree,
1791                                         "atime");
1792                 break;
1793                 default:
1794                         /* void */
1795                 break;
1796         }
1797
1798         /* now we know, that set_atime is shorter */
1799         if (set_atime_item) {
1800                 proto_item_set_len(set_atime_item, offset - old_offset);
1801         }
1802
1803         return offset;
1804 }
1805
1806
1807 /* RFC 1813, Page 26 */
1808 int
1809 dissect_set_mtime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1810 {
1811         proto_item* set_mtime_item = NULL;
1812         proto_tree* set_mtime_tree = NULL;
1813         int old_offset = offset;
1814         guint32 set_it;
1815         char* set_it_name;
1816
1817         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1818         set_it = EXTRACT_UINT(pd, offset+0);
1819         set_it_name = val_to_str(set_it,time_how,"Unknown");
1820
1821         if (tree) {
1822                 set_mtime_item = proto_tree_add_text(tree, offset,
1823                         END_OF_FRAME, "%s: %s",
1824                         name, set_it_name, set_it);
1825                 if (set_mtime_item)
1826                         set_mtime_tree = proto_item_add_subtree(set_mtime_item, ett_nfs_set_mtime);
1827         }
1828
1829         if (set_mtime_tree)
1830                 proto_tree_add_text(set_mtime_tree, offset, 4,
1831                                 "set_it: %s (%u)", set_it_name, set_it);
1832
1833         offset += 4;
1834
1835         switch (set_it) {
1836                 case SET_TO_CLIENT_TIME:
1837                         if (set_mtime_item)
1838                         offset = dissect_nfstime3(pd, offset, fd, set_mtime_tree,
1839                                         "atime");
1840                 break;
1841                 default:
1842                         /* void */
1843                 break;
1844         }
1845
1846         /* now we know, that set_mtime is shorter */
1847         if (set_mtime_item) {
1848                 proto_item_set_len(set_mtime_item, offset - old_offset);
1849         }
1850
1851         return offset;
1852 }
1853
1854
1855 /* RFC 1813, Page 25..27 */
1856 int
1857 dissect_sattr3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1858 {
1859         proto_item* sattr3_item = NULL;
1860         proto_tree* sattr3_tree = NULL;
1861         int old_offset = offset;
1862
1863         if (tree) {
1864                 sattr3_item = proto_tree_add_text(tree, offset,
1865                         END_OF_FRAME, "%s", name);
1866                 if (sattr3_item)
1867                         sattr3_tree = proto_item_add_subtree(sattr3_item, ett_nfs_sattr3);
1868         }
1869
1870         offset = dissect_set_mode3(pd, offset, fd, sattr3_tree, "mode");
1871         offset = dissect_set_uid3 (pd, offset, fd, sattr3_tree, "uid");
1872         offset = dissect_set_gid3 (pd, offset, fd, sattr3_tree, "gid");
1873         offset = dissect_set_size3(pd, offset, fd, sattr3_tree, "size");
1874         offset = dissect_set_atime(pd, offset, fd, sattr3_tree, "atime");
1875         offset = dissect_set_mtime(pd, offset, fd, sattr3_tree, "mtime");
1876
1877         /* now we know, that sattr3 is shorter */
1878         if (sattr3_item) {
1879                 proto_item_set_len(sattr3_item, offset - old_offset);
1880         }
1881
1882         return offset;
1883 }
1884
1885
1886 /* RFC 1813, Page 27 */
1887 int
1888 dissect_diropargs3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1889 {
1890         proto_item* diropargs3_item = NULL;
1891         proto_tree* diropargs3_tree = NULL;
1892         int old_offset = offset;
1893
1894         if (tree) {
1895                 diropargs3_item = proto_tree_add_text(tree, offset,
1896                         END_OF_FRAME, "%s", name);
1897                 if (diropargs3_item)
1898                         diropargs3_tree = proto_item_add_subtree(diropargs3_item, ett_nfs_diropargs3);
1899         }
1900
1901         offset = dissect_nfs_fh3  (pd, offset, fd, diropargs3_tree, "dir");
1902         offset = dissect_filename3(pd, offset, fd, diropargs3_tree, hf_nfs_name);
1903
1904         /* now we know, that diropargs3 is shorter */
1905         if (diropargs3_item) {
1906                 proto_item_set_len(diropargs3_item, offset - old_offset);
1907         }
1908
1909         return offset;
1910 }
1911
1912
1913 /* RFC 1813, Page 27 */
1914 int
1915 dissect_nfs3_diropargs3_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
1916 {
1917         offset = dissect_diropargs3(pd, offset, fd, tree, "object");
1918
1919         return offset;
1920 }
1921
1922
1923 /* RFC 1813, Page 40 */
1924 int
1925 dissect_access(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1926 {
1927         guint32 access;
1928         proto_item* access_item = NULL;
1929         proto_tree* access_tree = NULL;
1930
1931         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1932         access = EXTRACT_UINT(pd, offset+0);
1933         
1934         if (tree) {
1935                 access_item = proto_tree_add_text(tree, offset, 4,
1936                         "%s: 0x%02x", name, access);
1937                 if (access_item)
1938                         access_tree = proto_item_add_subtree(access_item, ett_nfs_access);
1939         }
1940
1941         if (access_tree) {
1942                 proto_tree_add_text(access_tree, offset, 4, "%s READ",
1943                 decode_boolean_bitfield(access,  0x001, 6, "allow", "not allow"));
1944                 proto_tree_add_text(access_tree, offset, 4, "%s LOOKUP",
1945                 decode_boolean_bitfield(access,  0x002, 6, "allow", "not allow"));
1946                 proto_tree_add_text(access_tree, offset, 4, "%s MODIFY",
1947                 decode_boolean_bitfield(access,  0x004, 6, "allowed", "not allow"));
1948                 proto_tree_add_text(access_tree, offset, 4, "%s EXTEND",
1949                 decode_boolean_bitfield(access,  0x008, 6, "allow", "not allow"));
1950                 proto_tree_add_text(access_tree, offset, 4, "%s DELETE",
1951                 decode_boolean_bitfield(access,  0x010, 6, "allow", "not allow"));
1952                 proto_tree_add_text(access_tree, offset, 4, "%s EXECUTE",
1953                 decode_boolean_bitfield(access,  0x020, 6, "allow", "not allow"));
1954         }
1955
1956         offset += 4;
1957         return offset;
1958 }
1959
1960
1961 /* NFS3 file handle dissector */
1962 int
1963 dissect_nfs3_nfs_fh3_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1964 {
1965         offset = dissect_nfs_fh3(pd, offset, fd, tree, "object");
1966         return offset;
1967 }
1968
1969
1970 /* generic NFS3 reply dissector */
1971 int
1972 dissect_nfs3_any_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1973 {
1974         guint32 status;
1975
1976         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
1977
1978         return offset;
1979
1980 }
1981
1982
1983 /* RFC 1813, Page 32,33 */
1984 int
1985 dissect_nfs3_getattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1986 {
1987         offset = dissect_nfs_fh3(pd, offset, fd, tree, "object");
1988         return offset;
1989 }
1990
1991
1992 /* RFC 1813, Page 32,33 */
1993 int
1994 dissect_nfs3_getattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1995 {
1996         guint32 status;
1997
1998         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
1999         switch (status) {
2000                 case 0:
2001                         offset = dissect_fattr3(pd, offset, fd, tree, "obj_attributes");
2002                 break;
2003                 default:
2004                         /* void */
2005                 break;
2006         }
2007                 
2008         return offset;
2009 }
2010
2011
2012 /* RFC 1813, Page 33 */
2013 int
2014 dissect_sattrguard3(const u_char* pd, int offset, frame_data* fd, proto_tree* tree, char *name)
2015 {
2016         proto_item* sattrguard3_item = NULL;
2017         proto_tree* sattrguard3_tree = NULL;
2018         int old_offset = offset;
2019         guint32 check;
2020         char* check_name;
2021
2022         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2023         check = EXTRACT_UINT(pd, offset+0);
2024         check_name = val_to_str(check,value_follows,"Unknown");
2025
2026         if (tree) {
2027                 sattrguard3_item = proto_tree_add_text(tree, offset,
2028                         END_OF_FRAME, "%s: %s", name, check_name);
2029                 if (sattrguard3_item)
2030                         sattrguard3_tree = proto_item_add_subtree(sattrguard3_item, ett_nfs_sattrguard3);
2031         }
2032
2033         if (sattrguard3_tree)
2034                 proto_tree_add_text(sattrguard3_tree, offset, 4,
2035                         "check: %s (%u)", check_name, check);
2036
2037         offset += 4;
2038
2039         switch (check) {
2040                 case TRUE:
2041                         offset = dissect_nfstime3(pd, offset, fd, sattrguard3_tree,
2042                                         "obj_ctime");
2043                 break;
2044                 case FALSE:
2045                         /* void */
2046                 break;
2047         }
2048
2049         /* now we know, that sattrguard3 is shorter */
2050         if (sattrguard3_item) {
2051                 proto_item_set_len(sattrguard3_item, offset - old_offset);
2052         }
2053
2054         return offset;
2055 }
2056
2057
2058 /* RFC 1813, Page 33..36 */
2059 int
2060 dissect_nfs3_setattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2061 {
2062         offset = dissect_nfs_fh3    (pd, offset, fd, tree, "object");
2063         offset = dissect_sattr3     (pd, offset, fd, tree, "new_attributes");
2064         offset = dissect_sattrguard3(pd, offset, fd, tree, "guard");
2065         return offset;
2066 }
2067
2068
2069 /* RFC 1813, Page 33..36 */
2070 int
2071 dissect_nfs3_setattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2072 {
2073         guint32 status;
2074
2075         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2076         switch (status) {
2077                 case 0:
2078                         offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
2079                 break;
2080                 default:
2081                         offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
2082                 break;
2083         }
2084                 
2085         return offset;
2086 }
2087
2088
2089 /* RFC 1813, Page 37..39 */
2090 int
2091 dissect_nfs3_lookup_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2092 {
2093         offset = dissect_diropargs3 (pd, offset, fd, tree, "what");
2094         return offset;
2095 }
2096
2097
2098 /* RFC 1813, Page 37..39 */
2099 int
2100 dissect_nfs3_lookup_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2101 {
2102         guint32 status;
2103
2104         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2105         switch (status) {
2106                 case 0:
2107                         offset = dissect_nfs_fh3     (pd, offset, fd, tree, "object");
2108                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2109                         offset = dissect_post_op_attr(pd, offset, fd, tree, "dir_attributes");
2110                 break;
2111                 default:
2112                         offset = dissect_post_op_attr(pd, offset, fd, tree, "dir_attributes");
2113                 break;
2114         }
2115                 
2116         return offset;
2117 }
2118
2119
2120 /* RFC 1813, Page 40..43 */
2121 int
2122 dissect_nfs3_access_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2123 {
2124         offset = dissect_nfs_fh3(pd, offset, fd, tree, "object");
2125         offset = dissect_access (pd, offset, fd, tree, "access");
2126
2127         return offset;
2128 }
2129
2130
2131 /* RFC 1813, Page 40..43 */
2132 int
2133 dissect_nfs3_access_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2134 {
2135         guint32 status;
2136
2137         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2138         switch (status) {
2139                 case 0:
2140                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2141                         offset = dissect_access      (pd, offset, fd, tree, "access");
2142                 break;
2143                 default:
2144                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2145                 break;
2146         }
2147                 
2148         return offset;
2149 }
2150
2151
2152 /* RFC 1813, Page 44,45 */
2153 int
2154 dissect_nfs3_readlink_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2155 {
2156         guint32 status;
2157
2158         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2159         switch (status) {
2160                 case 0:
2161                         offset = dissect_post_op_attr(pd, offset, fd, tree, "symlink_attributes");
2162                         offset = dissect_nfspath3    (pd, offset, fd, tree, hf_nfs_readlink_data);
2163                 break;
2164                 default:
2165                         offset = dissect_post_op_attr(pd, offset, fd, tree, "symlink_attributes");
2166                 break;
2167         }
2168                 
2169         return offset;
2170 }
2171
2172
2173 /* RFC 1813, Page 46..48 */
2174 int
2175 dissect_nfs3_read_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2176 {
2177         offset = dissect_nfs_fh3(pd, offset, fd, tree, "file");
2178         offset = dissect_offset3(pd, offset, fd, tree, "offset");
2179         offset = dissect_count3 (pd, offset, fd, tree, "count");
2180
2181         return offset;
2182 }
2183
2184
2185 /* RFC 1813, Page 46..48 */
2186 int
2187 dissect_nfs3_read_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2188 {
2189         guint32 status;
2190
2191         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2192         switch (status) {
2193                 case 0:
2194                         offset = dissect_post_op_attr(pd, offset, fd, tree, "file_attributes");
2195                         offset = dissect_count3      (pd, offset, fd, tree, "count");
2196                         offset = dissect_rpc_bool    (pd, offset, fd, tree, hf_nfs_read_eof);
2197                         offset = dissect_nfsdata     (pd, offset, fd, tree, hf_nfs_data);
2198                 break;
2199                 default:
2200                         offset = dissect_post_op_attr(pd, offset, fd, tree, "file_attributes");
2201                 break;
2202         }
2203                 
2204         return offset;
2205 }
2206
2207
2208 /* RFC 1813, Page 49 */
2209 static const value_string names_stable_how[] = {
2210         {       UNSTABLE,  "UNSTABLE"  },
2211         {       DATA_SYNC, "DATA_SYNC" },
2212         {       FILE_SYNC, "FILE_SYNC" },
2213         { 0, NULL }
2214 };
2215
2216
2217 /* RFC 1813, Page 49 */
2218 int
2219 dissect_stable_how(const u_char* pd, int offset, frame_data* fd, proto_tree* tree, int hfindex)
2220 {
2221         guint32 stable_how;
2222
2223         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2224         stable_how = EXTRACT_UINT(pd,offset+0);
2225         if (tree) {
2226                 proto_tree_add_item(tree, hfindex,
2227                         offset, 4, stable_how); 
2228         }
2229         offset += 4;
2230
2231         return offset;
2232 }
2233
2234
2235 /* RFC 1813, Page 49..54 */
2236 int
2237 dissect_nfs3_write_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2238 {
2239         offset = dissect_nfs_fh3   (pd, offset, fd, tree, "file");
2240         offset = dissect_offset3   (pd, offset, fd, tree, "offset");
2241         offset = dissect_count3    (pd, offset, fd, tree, "count");
2242         offset = dissect_stable_how(pd, offset, fd, tree, hf_nfs_write_stable);
2243         offset = dissect_nfsdata   (pd, offset, fd, tree, hf_nfs_data);
2244
2245         return offset;
2246 }
2247
2248
2249 /* RFC 1813, Page 49..54 */
2250 int
2251 dissect_nfs3_write_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2252 {
2253         guint32 status;
2254
2255         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2256         switch (status) {
2257                 case 0:
2258                         offset = dissect_wcc_data  (pd, offset, fd, tree, "file_wcc");
2259                         offset = dissect_count3    (pd, offset, fd, tree, "count");
2260                         offset = dissect_stable_how(pd, offset, fd, tree, hf_nfs_write_committed);
2261                         offset = dissect_writeverf3(pd, offset, fd, tree);
2262                 break;
2263                 default:
2264                         offset = dissect_wcc_data(pd, offset, fd, tree, "file_wcc");
2265                 break;
2266         }
2267                 
2268         return offset;
2269 }
2270
2271
2272 /* RFC 1813, Page 54 */
2273 static const value_string names_createmode3[] = {
2274         {       UNCHECKED, "UNCHECKED" },
2275         {       GUARDED,   "GUARDED" },
2276         {       EXCLUSIVE, "EXCLUSIVE" },
2277         { 0, NULL }
2278 };
2279
2280
2281 /* RFC 1813, Page 54 */
2282 int
2283 dissect_createmode3(const u_char* pd, int offset, frame_data* fd, proto_tree* tree, guint32* mode)
2284 {
2285         guint32 mode_value;
2286         
2287         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
2288         mode_value = EXTRACT_UINT(pd, offset + 0);
2289         if (tree) {
2290                 proto_tree_add_item(tree, hf_nfs_createmode3,
2291                 offset+0, 4, mode_value);
2292         }
2293         offset += 4;
2294
2295         *mode = mode_value;
2296         return offset;
2297 }
2298
2299
2300 /* RFC 1813, Page 54..58 */
2301 int
2302 dissect_nfs3_create_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2303 {
2304         guint32 mode;
2305
2306         offset = dissect_diropargs3 (pd, offset, fd, tree, "where");
2307         offset = dissect_createmode3(pd, offset, fd, tree, &mode);
2308         switch (mode) {
2309                 case UNCHECKED:
2310                 case GUARDED:
2311                         offset = dissect_sattr3     (pd, offset, fd, tree, "obj_attributes");
2312                 break;
2313                 case EXCLUSIVE:
2314                         offset = dissect_createverf3(pd, offset, fd, tree);
2315                 break;
2316         }
2317         
2318         return offset;
2319 }
2320
2321
2322 /* RFC 1813, Page 54..58 */
2323 int
2324 dissect_nfs3_create_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2325 {
2326         guint32 status;
2327
2328         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2329         switch (status) {
2330                 case 0:
2331                         offset = dissect_post_op_fh3 (pd, offset, fd, tree, "obj");
2332                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2333                         offset = dissect_wcc_data    (pd, offset, fd, tree, "dir_wcc");
2334                 break;
2335                 default:
2336                         offset = dissect_wcc_data    (pd, offset, fd, tree, "dir_wcc");
2337                 break;
2338         }
2339                 
2340         return offset;
2341 }
2342
2343
2344 /* RFC 1813, Page 58..60 */
2345 int
2346 dissect_nfs3_mkdir_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2347 {
2348         offset = dissect_diropargs3(pd, offset, fd, tree, "where");
2349         offset = dissect_sattr3    (pd, offset, fd, tree, "attributes");
2350         
2351         return offset;
2352 }
2353
2354
2355 /* RFC 1813, Page 61..63 */
2356 int
2357 dissect_nfs3_symlink_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2358 {
2359         offset = dissect_diropargs3(pd, offset, fd, tree, "where");
2360         offset = dissect_sattr3    (pd, offset, fd, tree, "symlink_attributes");
2361         offset = dissect_nfspath3  (pd, offset, fd, tree, hf_nfs_symlink_to);
2362         
2363         return offset;
2364 }
2365
2366
2367 /* RFC 1813, Page 67..69 */
2368 int
2369 dissect_nfs3_remove_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2370 {
2371         guint32 status;
2372
2373         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2374         switch (status) {
2375                 case 0:
2376                         offset = dissect_wcc_data    (pd, offset, fd, tree, "dir_wcc");
2377                 break;
2378                 default:
2379                         offset = dissect_wcc_data    (pd, offset, fd, tree, "dir_wcc");
2380                 break;
2381         }
2382                 
2383         return offset;
2384 }
2385
2386
2387 /* RFC 1813, Page 71..74 */
2388 int
2389 dissect_nfs3_rename_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2390 {
2391         offset = dissect_diropargs3(pd, offset, fd, tree, "from");
2392         offset = dissect_diropargs3(pd, offset, fd, tree, "to");
2393         
2394         return offset;
2395 }
2396
2397
2398 /* RFC 1813, Page 71..74 */
2399 int
2400 dissect_nfs3_rename_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2401 {
2402         guint32 status;
2403
2404         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2405         switch (status) {
2406                 case 0:
2407                         offset = dissect_wcc_data(pd, offset, fd, tree, "fromdir_wcc");
2408                         offset = dissect_wcc_data(pd, offset, fd, tree, "todir_wcc");
2409                 break;
2410                 default:
2411                         offset = dissect_wcc_data(pd, offset, fd, tree, "fromdir_wcc");
2412                         offset = dissect_wcc_data(pd, offset, fd, tree, "todir_wcc");
2413                 break;
2414         }
2415                 
2416         return offset;
2417 }
2418
2419
2420 /* RFC 1813, Page 74..76 */
2421 int
2422 dissect_nfs3_link_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2423 {
2424         offset = dissect_nfs_fh3   (pd, offset, fd, tree, "file");
2425         offset = dissect_diropargs3(pd, offset, fd, tree, "link");
2426         
2427         return offset;
2428 }
2429
2430
2431 /* RFC 1813, Page 74..76 */
2432 int
2433 dissect_nfs3_link_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2434 {
2435         guint32 status;
2436
2437         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2438         switch (status) {
2439                 case 0:
2440                         offset = dissect_post_op_attr(pd, offset, fd, tree, "file_attributes");
2441                         offset = dissect_wcc_data    (pd, offset, fd, tree, "linkdir_wcc");
2442                 break;
2443                 default:
2444                         offset = dissect_post_op_attr(pd, offset, fd, tree, "file_attributes");
2445                         offset = dissect_wcc_data    (pd, offset, fd, tree, "linkdir_wcc");
2446                 break;
2447         }
2448                 
2449         return offset;
2450 }
2451
2452
2453 /* RFC 1813, Page 76..80 */
2454 int
2455 dissect_nfs3_readdir_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2456 {
2457         offset = dissect_nfs_fh3    (pd, offset, fd, tree, "dir");
2458         offset = dissect_cookie3    (pd, offset, fd, tree, "cookie");
2459         offset = dissect_cookieverf3(pd, offset, fd, tree);
2460         offset = dissect_count3     (pd, offset, fd, tree, "count");
2461         
2462         return offset;
2463 }
2464
2465
2466 /* RFC 1813, Page 76..80 */
2467 int
2468 dissect_readdir_entry3(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2469 {
2470         proto_item* entry_item = NULL;
2471         proto_tree* entry_tree = NULL;
2472         int old_offset = offset;
2473
2474         if (tree) {
2475                 entry_item = proto_tree_add_item(tree, hf_nfs_readdir_entry,
2476                         offset+0, END_OF_FRAME, NULL);
2477                 if (entry_item)
2478                         entry_tree = proto_item_add_subtree(entry_item, ett_nfs_readdir_entry);
2479         }
2480
2481         offset = dissect_fileid3(pd, offset, fd, entry_tree, "fileid");
2482
2483         offset = dissect_filename3(pd, offset, fd, entry_tree, hf_nfs_readdir_entry_name);
2484         
2485         offset = dissect_cookie3(pd, offset, fd, entry_tree, "cookie");
2486
2487         /* now we know, that a readdir entry is shorter */
2488         if (entry_item) {
2489                 proto_item_set_len(entry_item, offset - old_offset);
2490         }
2491
2492         return offset;
2493 }
2494
2495
2496 /* RFC 1813, Page 76..80 */
2497 int
2498 dissect_nfs3_readdir_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2499 {
2500         guint32 status;
2501         guint32 value_follows;
2502         guint32 eof_value;
2503
2504         offset = dissect_stat(pd, offset, fd, tree, &status);
2505         switch (status) {
2506                 case 0:
2507                         offset = dissect_post_op_attr(pd, offset, fd, tree, "dir_attributes");
2508                         offset = dissect_cookieverf3(pd, offset, fd, tree);
2509                         while (1) {
2510                                 if (!BYTES_ARE_IN_FRAME(offset,4)) break;
2511                                 value_follows = EXTRACT_UINT(pd, offset+0);
2512                                 proto_tree_add_item(tree,hf_nfs_readdir_value_follows,
2513                                         offset+0, 4, value_follows);
2514                                 offset += 4;
2515                                 if (value_follows == 1) {
2516                                         offset = dissect_readdir_entry3(pd, offset, fd, tree);
2517                                 }
2518                                 else {
2519                                         break;
2520                                 }
2521                         }
2522                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2523                         eof_value = EXTRACT_UINT(pd, offset+0);
2524                         if (tree)
2525                                 proto_tree_add_item(tree, hf_nfs_readdir_eof,
2526                                         offset+ 0, 4, eof_value);
2527                         offset += 4;
2528                 break;
2529                 default:
2530                         offset = dissect_post_op_attr(pd, offset, fd, tree, "dir_attributes");
2531                 break;
2532         }
2533
2534         return offset;
2535 }
2536
2537
2538 /* RFC 1813, Page 84..86 */
2539 int
2540 dissect_nfs3_fsstat_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2541 {
2542         guint32 status;
2543         guint32 invarsec;
2544
2545         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2546         switch (status) {
2547                 case 0:
2548                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2549                         offset = dissect_size3 (pd, offset, fd, tree, "tbytes");
2550                         offset = dissect_size3 (pd, offset, fd, tree, "fbytes");
2551                         offset = dissect_size3 (pd, offset, fd, tree, "abytes");
2552                         offset = dissect_size3 (pd, offset, fd, tree, "tfiles");
2553                         offset = dissect_size3 (pd, offset, fd, tree, "ffiles");
2554                         offset = dissect_size3 (pd, offset, fd, tree, "afiles");
2555                         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
2556                         invarsec = EXTRACT_UINT(pd, offset + 0);
2557                         if (tree)
2558                                 proto_tree_add_item(tree, hf_nfs_fsstat_invarsec,
2559                                 offset+0, 4, invarsec);
2560                         offset += 4;
2561                 break;
2562                 default:
2563                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2564                 break;
2565         }
2566
2567         return offset;
2568 }
2569
2570
2571 #define FSF3_LINK        0x0001
2572 #define FSF3_SYMLINK     0x0002
2573 #define FSF3_HOMOGENEOUS 0x0008
2574 #define FSF3_CANSETTIME  0x0010
2575
2576
2577 /* RFC 1813, Page 86..90 */
2578 int
2579 dissect_nfs3_fsinfo_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2580 {
2581         guint32 status;
2582         guint32 rtmax;
2583         guint32 rtpref;
2584         guint32 rtmult;
2585         guint32 wtmax;
2586         guint32 wtpref;
2587         guint32 wtmult;
2588         guint32 dtpref;
2589         guint32 properties;
2590         proto_item*     properties_item = NULL;
2591         proto_tree*     properties_tree = NULL;
2592
2593         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2594         switch (status) {
2595                 case 0:
2596                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2597                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2598                         rtmax = EXTRACT_UINT(pd, offset+0);
2599                         if (tree)
2600                                 proto_tree_add_item(tree, hf_nfs_fsinfo_rtmax,
2601                                 offset+0, 4, rtmax);
2602                         offset += 4;
2603                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2604                         rtpref = EXTRACT_UINT(pd, offset+0);
2605                         if (tree)
2606                                 proto_tree_add_item(tree, hf_nfs_fsinfo_rtpref,
2607                                 offset+0, 4, rtpref);
2608                         offset += 4;
2609                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2610                         rtmult = EXTRACT_UINT(pd, offset+0);
2611                         if (tree)
2612                                 proto_tree_add_item(tree, hf_nfs_fsinfo_rtmult,
2613                                 offset+0, 4, rtmult);
2614                         offset += 4;
2615                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2616                         wtmax = EXTRACT_UINT(pd, offset+0);
2617                         if (tree)
2618                                 proto_tree_add_item(tree, hf_nfs_fsinfo_wtmax,
2619                                 offset+0, 4, wtmax);
2620                         offset += 4;
2621                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2622                         wtpref = EXTRACT_UINT(pd, offset+0);
2623                         if (tree)
2624                                 proto_tree_add_item(tree, hf_nfs_fsinfo_wtpref,
2625                                 offset+0, 4, wtpref);
2626                         offset += 4;
2627                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2628                         wtmult = EXTRACT_UINT(pd, offset+0);
2629                         if (tree)
2630                                 proto_tree_add_item(tree, hf_nfs_fsinfo_wtmult,
2631                                 offset+0, 4, wtmult);
2632                         offset += 4;
2633                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2634                         dtpref = EXTRACT_UINT(pd, offset+0);
2635                         if (tree)
2636                                 proto_tree_add_item(tree, hf_nfs_fsinfo_dtpref,
2637                                 offset+0, 4, dtpref);
2638                         offset += 4;
2639
2640                         offset = dissect_size3   (pd, offset, fd, tree, "maxfilesize");
2641                         offset = dissect_nfstime3(pd, offset, fd, tree, "time_delta");
2642                         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
2643                         properties = EXTRACT_UINT(pd, offset+0);
2644                         if (tree) {
2645                                 properties_item = proto_tree_add_item(tree,
2646                                 hf_nfs_fsinfo_properties,
2647                                 offset+0, 4, properties);
2648                                 if (properties_item) 
2649                                         properties_tree = proto_item_add_subtree(properties_item, ett_nfs_fsinfo_properties);
2650                                 if (properties_tree) {
2651                                         proto_tree_add_text(properties_tree,
2652                                         offset, 4, "%s",
2653                                         decode_boolean_bitfield(properties,
2654                                         FSF3_CANSETTIME,5,
2655                                         "SETATTR can set time on server",
2656                                         "SETATTR can't set time on server"));
2657
2658                                         proto_tree_add_text(properties_tree,
2659                                         offset, 4, "%s",
2660                                         decode_boolean_bitfield(properties,
2661                                         FSF3_HOMOGENEOUS,5,
2662                                         "PATHCONF is valid for all files",
2663                                         "PATHCONF should be get for every single file"));
2664
2665                                         proto_tree_add_text(properties_tree,
2666                                         offset, 4, "%s",
2667                                         decode_boolean_bitfield(properties,
2668                                         FSF3_SYMLINK,5,
2669                                         "File System supports symbolic links",
2670                                         "File System does not symbolic hard links"));
2671
2672                                         proto_tree_add_text(properties_tree,
2673                                         offset, 4, "%s",
2674                                         decode_boolean_bitfield(properties,
2675                                         FSF3_LINK,5,
2676                                         "File System supports hard links",
2677                                         "File System does not support hard links"));
2678                                 }
2679                         }
2680                         offset += 4;
2681                 break;
2682                 default:
2683                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2684                 break;
2685         }
2686
2687         return offset;
2688 }
2689
2690
2691 /* RFC 1813, Page 90..92 */
2692 int
2693 dissect_nfs3_pathconf_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
2694 {
2695         guint32 status;
2696         guint32 linkmax;
2697         guint32 name_max;
2698
2699         offset = dissect_nfsstat3(pd, offset, fd, tree, &status);
2700         switch (status) {
2701                 case 0:
2702                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2703                         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
2704                         linkmax = EXTRACT_UINT(pd, offset + 0);
2705                         if (tree)
2706                                 proto_tree_add_item(tree, hf_nfs_pathconf_linkmax,
2707                                 offset+0, 4, linkmax);
2708                         offset += 4;
2709                         if (!BYTES_ARE_IN_FRAME(offset, 4)) return offset;
2710                         name_max = EXTRACT_UINT(pd, offset + 0);
2711                         if (tree)
2712                                 proto_tree_add_item(tree, hf_nfs_pathconf_name_max,
2713                                 offset+0, 4, name_max);
2714                         offset += 4;
2715                         offset = dissect_rpc_bool(pd, offset, fd, tree, hf_nfs_pathconf_no_trunc);
2716                         offset = dissect_rpc_bool(pd, offset, fd, tree, hf_nfs_pathconf_chown_restricted);
2717                         offset = dissect_rpc_bool(pd, offset, fd, tree, hf_nfs_pathconf_case_insensitive);
2718                         offset = dissect_rpc_bool(pd, offset, fd, tree, hf_nfs_pathconf_case_preserving);
2719                 break;
2720                 default:
2721                         offset = dissect_post_op_attr(pd, offset, fd, tree, "obj_attributes");
2722                 break;
2723         }
2724
2725         return offset;
2726 }
2727
2728
2729 /* 5 missing functions */
2730
2731
2732 /* proc number, "proc name", dissect_request, dissect_reply */
2733 /* NULL as function pointer means: take the generic one. */
2734 const vsff nfs3_proc[] = {
2735         { 0,    "NULL",         /* OK */
2736         NULL,                           NULL },
2737         { 1,    "GETATTR",      /* OK */
2738         dissect_nfs3_getattr_call,      dissect_nfs3_getattr_reply },
2739         { 2,    "SETATTR",      /* OK */
2740         dissect_nfs3_setattr_call,      dissect_nfs3_setattr_reply },
2741         { 3,    "LOOKUP",       /* OK */
2742         dissect_nfs3_lookup_call,       dissect_nfs3_lookup_reply },
2743         { 4,    "ACCESS",       /* OK */
2744         dissect_nfs3_access_call,       dissect_nfs3_access_reply },
2745         { 5,    "READLINK",     /* OK */
2746         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_readlink_reply },
2747         { 6,    "READ",         /* OK */
2748         dissect_nfs3_read_call,         dissect_nfs3_read_reply },
2749         { 7,    "WRITE",        /* OK */
2750         dissect_nfs3_write_call,        dissect_nfs3_write_reply },
2751         { 8,    "CREATE",       /* OK */
2752         dissect_nfs3_create_call,       dissect_nfs3_create_reply },
2753         { 9,    "MKDIR",        /* OK */
2754         dissect_nfs3_mkdir_call,        dissect_nfs3_create_reply },
2755         { 10,   "SYMLINK",      /* OK */
2756         dissect_nfs3_symlink_call,      dissect_nfs3_create_reply },
2757         { 11,   "MKNOD",        /* todo: call */
2758         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_create_reply },
2759         { 12,   "REMOVE",       /* OK */
2760         dissect_nfs3_diropargs3_call,   dissect_nfs3_remove_reply },
2761         { 13,   "RMDIR",        /* OK */
2762         dissect_nfs3_diropargs3_call,   dissect_nfs3_remove_reply },
2763         { 14,   "RENAME",       /* OK */
2764         dissect_nfs3_rename_call,       dissect_nfs3_rename_reply },
2765         { 15,   "LINK",         /* OK */
2766         dissect_nfs3_link_call,         dissect_nfs3_link_reply },
2767         { 16,   "READDIR",      /* OK */
2768         dissect_nfs3_readdir_call,      dissect_nfs3_readdir_reply },
2769         { 17,   "READDIRPLUS",  /* todo: call, reply */
2770         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_any_reply },
2771         { 18,   "FSSTAT",       /* OK */
2772         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_fsstat_reply },
2773         { 19,   "FSINFO",       /* OK */
2774         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_fsinfo_reply },
2775         { 20,   "PATHCONF",     /* OK */
2776         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_pathconf_reply },
2777         { 21,   "COMMIT",       /* todo: call, reply */
2778         dissect_nfs3_nfs_fh3_call,      dissect_nfs3_any_reply },
2779         { 0,NULL,NULL,NULL }
2780 };
2781 /* end of NFS Version 3 */
2782
2783
2784 static struct true_false_string yesno = { "Yes", "No" };
2785
2786
2787 void
2788 proto_register_nfs(void)
2789 {
2790         static hf_register_info hf[] = {
2791                 { &hf_nfs_stat, {
2792                         "Status", "nfs.status2", FT_UINT32, BASE_DEC,
2793                         VALS(names_nfs_stat), 0, "Reply status" }},
2794                 { &hf_nfs_name, {
2795                         "Name", "nfs.name", FT_STRING, BASE_DEC,
2796                         NULL, 0, "Name" }},
2797                 { &hf_nfs_readlink_data, {
2798                         "Data", "nfs.readlink.data", FT_STRING, BASE_DEC,
2799                         NULL, 0, "Symbolic Link Data" }},
2800                 { &hf_nfs_read_offset, {
2801                         "Offset", "nfs.read.offset", FT_UINT32, BASE_DEC,
2802                         NULL, 0, "Read Offset" }},
2803                 { &hf_nfs_read_count, {
2804                         "Count", "nfs.read.count", FT_UINT32, BASE_DEC,
2805                         NULL, 0, "Read Count" }},
2806                 { &hf_nfs_read_totalcount, {
2807                         "Total Count", "nfs.read.totalcount", FT_UINT32, BASE_DEC,
2808                         NULL, 0, "Total Count (obsolete)" }},
2809                 { &hf_nfs_data, {
2810                         "Data", "nfs.data", FT_STRING, BASE_DEC,
2811                         NULL, 0, "Data" }},
2812                 { &hf_nfs_write_beginoffset, {
2813                         "Begin Offset", "nfs.write.beginoffset", FT_UINT32, BASE_DEC,
2814                         NULL, 0, "Begin offset (obsolete)" }},
2815                 { &hf_nfs_write_offset, {
2816                         "Offset", "nfs.write.offset", FT_UINT32, BASE_DEC,
2817                         NULL, 0, "Offset" }},
2818                 { &hf_nfs_write_totalcount, {
2819                         "Total Count", "nfs.write.totalcount", FT_UINT32, BASE_DEC,
2820                         NULL, 0, "Total Count (obsolete)" }},
2821                 { &hf_nfs_symlink_to, {
2822                         "To", "nfs.symlink.to", FT_STRING, BASE_DEC,
2823                         NULL, 0, "Symbolic link destination name" }},
2824                 { &hf_nfs_readdir_cookie, {
2825                         "Cookie", "nfs.readdir.cookie", FT_UINT32, BASE_DEC,
2826                         NULL, 0, "Directory Cookie" }},
2827                 { &hf_nfs_readdir_count, {
2828                         "Count", "nfs.readdir.count", FT_UINT32, BASE_DEC,
2829                         NULL, 0, "Directory Count" }},
2830                 { &hf_nfs_readdir_value_follows, {
2831                         "Value Follows", "nfs.readdir.value_follows", FT_BOOLEAN, BASE_NONE,
2832                         &yesno, 0, "Value Follows" }},
2833                 { &hf_nfs_readdir_entry, {
2834                         "Entry", "nfs.readdir.entry", FT_NONE, 0,
2835                         NULL, 0, "Directory Entry" }},
2836                 { &hf_nfs_readdir_entry_fileid, {
2837                         "File ID", "nfs.readdir.entry.fileid", FT_UINT32, BASE_DEC,
2838                         NULL, 0, "File ID" }},
2839                 { &hf_nfs_readdir_entry_name, {
2840                         "Name", "nfs.readdir.entry.name", FT_STRING, BASE_DEC,
2841                         NULL, 0, "Name" }},
2842                 { &hf_nfs_readdir_entry_cookie, {
2843                         "Cookie", "nfs.readdir.entry.cookie", FT_UINT32, BASE_DEC,
2844                         NULL, 0, "Directory Cookie" }},
2845                 { &hf_nfs_readdir_eof, {
2846                         "EOF", "nfs.readdir.eof", FT_UINT32, BASE_DEC,
2847                         NULL, 0, "EOF" }},
2848                 { &hf_nfs_statfs_tsize, {
2849                         "Transfer Size", "nfs.statfs.tsize", FT_UINT32, BASE_DEC,
2850                         NULL, 0, "Transfer Size" }},
2851                 { &hf_nfs_statfs_bsize, {
2852                         "Block Size", "nfs.statfs.bsize", FT_UINT32, BASE_DEC,
2853                         NULL, 0, "Block Size" }},
2854                 { &hf_nfs_statfs_blocks, {
2855                         "Total Blocks", "nfs.statfs.blocks", FT_UINT32, BASE_DEC,
2856                         NULL, 0, "Total Blocks" }},
2857                 { &hf_nfs_statfs_bfree, {
2858                         "Free Blocks", "nfs.statfs.bfree", FT_UINT32, BASE_DEC,
2859                         NULL, 0, "Free Blocks" }},
2860                 { &hf_nfs_statfs_bavail, {
2861                         "Available Blocks", "nfs.statfs.bavail", FT_UINT32, BASE_DEC,
2862                         NULL, 0, "Available Blocks" }},
2863                 { &hf_nfs_nfsstat3, {
2864                         "Status", "nfs.status", FT_UINT32, BASE_DEC,
2865                         VALS(names_nfs_nfsstat3), 0, "Reply status" }},
2866                 { &hf_nfs_read_eof, {
2867                         "EOF", "nfs.read.eof", FT_BOOLEAN, BASE_NONE,
2868                         &yesno, 0, "EOF" }},
2869                 { &hf_nfs_write_stable, {
2870                         "Stable", "nfs.write.stable", FT_UINT32, BASE_DEC,
2871                         VALS(names_stable_how), 0, "Stable" }},
2872                 { &hf_nfs_write_committed, {
2873                         "Committed", "nfs.write.committed", FT_UINT32, BASE_DEC,
2874                         VALS(names_stable_how), 0, "Committed" }},
2875                 { &hf_nfs_createmode3, {
2876                         "Create Mode", "nfs.createmode", FT_UINT32, BASE_DEC,
2877                         VALS(names_createmode3), 0, "Create Mode" }},
2878                 { &hf_nfs_fsstat_invarsec, {
2879                         "invarsec", "nfs.fsstat.invarsec", FT_UINT32, BASE_DEC,
2880                         NULL, 0, "probable number of seconds of file system invariance" }},
2881                 { &hf_nfs_fsinfo_rtmax, {
2882                         "rtmax", "nfs.fsinfo.rtmax", FT_UINT32, BASE_DEC,
2883                         NULL, 0, "maximum READ request" }},
2884                 { &hf_nfs_fsinfo_rtpref, {
2885                         "rtpref", "nfs.fsinfo.rtpref", FT_UINT32, BASE_DEC,
2886                         NULL, 0, "preferred READ request" }},
2887                 { &hf_nfs_fsinfo_rtmult, {
2888                         "rtmult", "nfs.fsinfo.rtmult", FT_UINT32, BASE_DEC,
2889                         NULL, 0, "suggested READ multiple" }},
2890                 { &hf_nfs_fsinfo_wtmax, {
2891                         "wtmax", "nfs.fsinfo.rtmax", FT_UINT32, BASE_DEC,
2892                         NULL, 0, "maximum WRITE request" }},
2893                 { &hf_nfs_fsinfo_wtpref, {
2894                         "wtpref", "nfs.fsinfo.rtpref", FT_UINT32, BASE_DEC,
2895                         NULL, 0, "preferred WRITE request" }},
2896                 { &hf_nfs_fsinfo_wtmult, {
2897                         "wtmult", "nfs.fsinfo.rtmult", FT_UINT32, BASE_DEC,
2898                         NULL, 0, "suggested WRITE multiple" }},
2899                 { &hf_nfs_fsinfo_dtpref, {
2900                         "dtpref", "nfs.fsinfo.dtpref", FT_UINT32, BASE_DEC,
2901                         NULL, 0, "preferred READDIR request" }},
2902                 { &hf_nfs_fsinfo_properties, {
2903                         "Properties", "nfs.fsinfo.propeties", FT_UINT32, BASE_HEX,
2904                         NULL, 0, "File System Properties" }},
2905                 { &hf_nfs_pathconf_linkmax, {
2906                         "linkmax", "nfs.pathconf.linkmax", FT_UINT32, BASE_DEC,
2907                         NULL, 0, "Maximum number of hard links" }},
2908                 { &hf_nfs_pathconf_name_max, {
2909                         "name_max", "nfs.pathconf.name_max", FT_UINT32, BASE_DEC,
2910                         NULL, 0, "Maximum file name length" }},
2911                 { &hf_nfs_pathconf_no_trunc, {
2912                         "no_trunc", "nfs.pathconf.no_trunc", FT_BOOLEAN, BASE_NONE,
2913                         &yesno, 0, "No long file name truncation" }},
2914                 { &hf_nfs_pathconf_chown_restricted, {
2915                         "chown_restricted", "nfs.pathconf.chown_restricted", FT_BOOLEAN, BASE_NONE,
2916                         &yesno, 0, "chown is restricted to root" }},
2917                 { &hf_nfs_pathconf_case_insensitive, {
2918                         "case_insensitive", "nfs.pathconf.case_insensitive", FT_BOOLEAN, BASE_NONE,
2919                         &yesno, 0, "file names are treated case insensitive" }},
2920                 { &hf_nfs_pathconf_case_preserving, {
2921                         "case_preserving", "nfs.pathconf.case_preserving", FT_BOOLEAN, BASE_NONE,
2922                         &yesno, 0, "file name cases are preserved" }}
2923         };
2924
2925         static gint *ett[] = {
2926                 &ett_nfs,
2927                 &ett_nfs_fhandle,
2928                 &ett_nfs_timeval,
2929                 &ett_nfs_mode,
2930                 &ett_nfs_fattr,
2931                 &ett_nfs_sattr,
2932                 &ett_nfs_diropargs,
2933                 &ett_nfs_readdir_entry,
2934                 &ett_nfs_mode3,
2935                 &ett_nfs_specdata3,
2936                 &ett_nfs_fh3,
2937                 &ett_nfs_nfstime3,
2938                 &ett_nfs_fattr3,
2939                 &ett_nfs_post_op_fh3,
2940                 &ett_nfs_sattr3,
2941                 &ett_nfs_diropargs3,
2942                 &ett_nfs_sattrguard3,
2943                 &ett_nfs_set_mode3,
2944                 &ett_nfs_set_uid3,
2945                 &ett_nfs_set_gid3,
2946                 &ett_nfs_set_size3,
2947                 &ett_nfs_set_atime,
2948                 &ett_nfs_set_mtime,
2949                 &ett_nfs_pre_op_attr,
2950                 &ett_nfs_post_op_attr,
2951                 &ett_nfs_wcc_attr,
2952                 &ett_nfs_wcc_data,
2953                 &ett_nfs_access,
2954                 &ett_nfs_fsinfo_properties
2955         };
2956         proto_nfs = proto_register_protocol("Network File System", "nfs");
2957         proto_register_field_array(proto_nfs, hf, array_length(hf));
2958         proto_register_subtree_array(ett, array_length(ett));
2959
2960         /* Register the protocol as RPC */
2961         rpc_init_prog(proto_nfs, NFS_PROGRAM, ett_nfs);
2962         /* Register the procedure tables */
2963         rpc_init_proc_table(NFS_PROGRAM, 2, nfs2_proc);
2964         rpc_init_proc_table(NFS_PROGRAM, 3, nfs3_proc);
2965 }