Added Heikki Vatiainen's <hessu@cs.tut.fi> HSRP dissector.
[metze/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.6 1999/11/19 13:09:55 gram Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@unicom.net>
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 static gint ett_nfs = -1;
45 static gint ett_nfs_fhandle = -1;
46 static gint ett_nfs_timeval = -1;
47 static gint ett_nfs_mode = -1;
48 static gint ett_nfs_fattr = -1;
49 static gint ett_nfs_sattr = -1;
50 static gint ett_nfs_mode3 = -1;
51 static gint ett_nfs_specdata3 = -1;
52 static gint ett_nfs_fh3 = -1;
53 static gint ett_nfs_nfstime3 = -1;
54 static gint ett_nfs_fattr3 = -1;
55 static gint ett_nfs_sattr3 = -1;
56 static gint ett_nfs_sattrguard3 = -1;
57 static gint ett_nfs_set_mode3 = -1;
58 static gint ett_nfs_set_uid3 = -1;
59 static gint ett_nfs_set_gid3 = -1;
60 static gint ett_nfs_set_size3 = -1;
61 static gint ett_nfs_set_atime = -1;
62 static gint ett_nfs_set_mtime = -1;
63 static gint ett_nfs_pre_op_attr = -1;
64 static gint ett_nfs_post_op_attr = -1;
65 static gint ett_nfs_wcc_attr = -1;
66 static gint ett_nfs_wcc_data = -1;
67
68
69 /***************************/
70 /* NFS Version 2, RFC 1094 */
71 /***************************/
72
73
74 /* base 32 bit type for NFS v2 */
75 int
76 dissect_unsigned_int(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
77 char* name)
78 {
79         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"unsigned int");
80         return offset;
81 }
82
83
84 /* RFC 1094, Page 12 */
85 int
86 dissect_stat(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
87 char* name, guint32* status)
88 {
89         guint32 stat;
90         char* stat_name = NULL;
91
92         const value_string nfs2_stat[] =
93         {
94                 {       0,      "OK" },
95                 {       1,      "ERR_PERM" },
96                 {       2,      "ERR_NOENT" },
97                 {       5,      "ERR_IO" },
98                 {       6,      "ERR_NX_IO" },
99                 {       13,     "ERR_ACCES" },
100                 {       17,     "ERR_EXIST" },
101                 {       19,     "ERR_NODEV" },
102                 {       20,     "ERR_NOTDIR" },
103                 {       21,     "ERR_ISDIR" },
104                 {       27,     "ERR_FBIG" },
105                 {       28,     "ERR_NOSPC" },
106                 {       30,     "ERR_ROFS" },
107                 {       63,     "ERR_NAMETOOLONG" },
108                 {       66,     "ERR_NOTEMPTY" },
109                 {       69,     "ERR_DQUOT" },
110                 {       70,     "ERR_STALE" },
111                 {       99,     "ERR_WFLUSH" },
112                 {       0,      NULL }
113         };
114
115         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
116         stat = EXTRACT_UINT(pd, offset+0);
117         stat_name = val_to_str(stat, nfs2_stat, "%u");
118         
119         if (tree) {
120                 proto_tree_add_text(tree, offset, 4,
121                         "%s: %s (%u)", name, stat_name, stat);
122         }
123
124         offset += 4;
125         *status = stat;
126         return offset;
127 }
128
129
130 /* RFC 1094, Page 15 */
131 int
132 dissect_ftype(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
133 char* name)
134 {
135         guint32 ftype;
136         char* ftype_name = NULL;
137
138         const value_string nfs2_ftype[] =
139         {
140                 {       0,      "Non-File" },
141                 {       1,      "Regular File" },
142                 {       2,      "Directory" },
143                 {       3,      "Block Special Device" },
144                 {       4,      "Character Special Device" },
145                 {       5,      "Symbolic Link" },
146                 {       0,      NULL }
147         };
148
149         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
150         ftype = EXTRACT_UINT(pd, offset+0);
151         ftype_name = val_to_str(ftype, nfs2_ftype, "%u");
152         
153         if (tree) {
154                 proto_tree_add_text(tree, offset, 4,
155                         "%s: %s (%u)", name, ftype_name, ftype);
156         }
157
158         offset += 4;
159         return offset;
160 }
161
162
163 /* RFC 1094, Page 15 */
164 int
165 dissect_fhandle(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
166 {
167         proto_item* fitem;
168         proto_tree* ftree = NULL;
169
170         if (tree) {
171                 fitem = proto_tree_add_text(tree, offset, FHSIZE,
172                         "%s", name);
173                 if (fitem)
174                         ftree = proto_item_add_subtree(fitem, ett_nfs_fhandle);
175         }
176
177         if (ftree) {
178                 proto_tree_add_text(ftree,offset+0,FHSIZE,
179                                         "file handle (opaque data)");
180         }
181
182         offset += FHSIZE;
183         return offset;
184 }
185
186
187 int
188 dissect_timeval(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
189 {
190         guint32 seconds;
191         guint32 mseconds;
192
193         proto_item* time_item;
194         proto_tree* time_tree = NULL;
195
196         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
197         seconds = EXTRACT_UINT(pd, offset+0);
198         mseconds = EXTRACT_UINT(pd, offset+4);
199         
200         if (tree) {
201                 time_item = proto_tree_add_text(tree, offset, 8,
202                         "%s: %u.%06u", name, seconds, mseconds);
203                 if (time_item)
204                         time_tree = proto_item_add_subtree(time_item, ett_nfs_timeval);
205         }
206
207         if (time_tree) {
208                 proto_tree_add_text(time_tree,offset+0,4,
209                                         "seconds: %u", seconds);
210                 proto_tree_add_text(time_tree,offset+4,4,
211                                         "micro seconds: %u", mseconds);
212         }
213         offset += 8;
214         return offset;
215 }
216
217
218 /* RFC 1094, Page 16 */
219 const value_string nfs2_mode_names[] = {
220         {       0040000,        "Directory"     },
221         {       0020000,        "Character Special Device"      },
222         {       0060000,        "Block Special Device"  },
223         {       0100000,        "Regular File"  },
224         {       0120000,        "Symbolic Link" },
225         {       0140000,        "Named Socket"  },
226 };
227
228 int
229 dissect_mode(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
230 char* name)
231 {
232         guint32 mode;
233         proto_item* mode_item = NULL;
234         proto_tree* mode_tree = NULL;
235
236         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
237         mode = EXTRACT_UINT(pd, offset+0);
238         
239         if (tree) {
240                 mode_item = proto_tree_add_text(tree, offset, 4,
241                         "%s: 0%o", name, mode);
242                 if (mode_item)
243                         mode_tree = proto_item_add_subtree(mode_item, ett_nfs_mode);
244         }
245
246         if (mode_tree) {
247                 proto_tree_add_text(mode_tree, offset, 4, "%s",
248                         decode_enumerated_bitfield(mode,  0160000, 16,
249                         nfs2_mode_names, "%s"));
250                 proto_tree_add_text(mode_tree, offset, 4, "%s",
251                 decode_boolean_bitfield(mode,   04000, 16, "Set user id on exec", "not SUID"));
252                 proto_tree_add_text(mode_tree, offset, 4, "%s",
253                 decode_boolean_bitfield(mode,   02000, 16, "Set group id on exec", "not SGID"));
254                 proto_tree_add_text(mode_tree, offset, 4, "%s",
255                 decode_boolean_bitfield(mode,   01000, 16, "Save swapped text even after use", "not save swapped text"));
256                 proto_tree_add_text(mode_tree, offset, 4, "%s",
257                 decode_boolean_bitfield(mode,    0400, 16, "Read permission for owner", "no Read permission for owner"));
258                 proto_tree_add_text(mode_tree, offset, 4, "%s",
259                 decode_boolean_bitfield(mode,    0200, 16, "Write permission for owner", "no Write permission for owner"));
260                 proto_tree_add_text(mode_tree, offset, 4, "%s",
261                 decode_boolean_bitfield(mode,    0100, 16, "Execute permission for owner", "no Execute permission for owner"));
262                 proto_tree_add_text(mode_tree, offset, 4, "%s",
263                 decode_boolean_bitfield(mode,     040, 16, "Read permission for group", "no Read permission for group"));
264                 proto_tree_add_text(mode_tree, offset, 4, "%s",
265                 decode_boolean_bitfield(mode,     020, 16, "Write permission for group", "no Write permission for group"));
266                 proto_tree_add_text(mode_tree, offset, 4, "%s",
267                 decode_boolean_bitfield(mode,     010, 16, "Execute permission for group", "no Execute permission for group"));
268                 proto_tree_add_text(mode_tree, offset, 4, "%s",
269                 decode_boolean_bitfield(mode,      04, 16, "Read permission for others", "no Read permission for others"));
270                 proto_tree_add_text(mode_tree, offset, 4, "%s",
271                 decode_boolean_bitfield(mode,      02, 16, "Write permission for others", "no Write permission for others"));
272                 proto_tree_add_text(mode_tree, offset, 4, "%s",
273                 decode_boolean_bitfield(mode,      01, 16, "Execute permission for others", "no Execute permission for others"));
274         }
275
276         offset += 4;
277         return offset;
278 }
279
280
281 /* RFC 1094, Page 15 */
282 int
283 dissect_fattr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
284 {
285         proto_item* fattr_item = NULL;
286         proto_tree* fattr_tree = NULL;
287         int old_offset = offset;
288
289         if (tree) {
290                 fattr_item = proto_tree_add_text(tree, offset,
291                         END_OF_FRAME, "%s", name);
292                 if (fattr_item)
293                         fattr_tree = proto_item_add_subtree(fattr_item, ett_nfs_fattr);
294         }
295
296         offset = dissect_ftype        (pd,offset,fd,fattr_tree,"type");
297         offset = dissect_mode         (pd,offset,fd,fattr_tree,"mode");
298         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"nlink");
299         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"uid");
300         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"gid");
301         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"size");
302         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"blocksize");
303         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"rdev");
304         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"blocks");
305         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"fsid");
306         offset = dissect_unsigned_int (pd,offset,fd,fattr_tree,"fileid");
307         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"atime");
308         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"mtime");
309         offset = dissect_timeval      (pd,offset,fd,fattr_tree,"ctime");
310
311         /* now we know, that fattr is shorter */
312         if (fattr_item) {
313                 proto_item_set_len(fattr_item, offset - old_offset);
314         }
315
316         return offset;
317 }
318
319
320 /* RFC 1094, Page 17 */
321 int
322 dissect_sattr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
323 {
324         proto_item* sattr_item = NULL;
325         proto_tree* sattr_tree = NULL;
326         int old_offset = offset;
327
328         if (tree) {
329                 sattr_item = proto_tree_add_text(tree, offset,
330                         END_OF_FRAME, "%s", name);
331                 if (sattr_item)
332                         sattr_tree = proto_item_add_subtree(sattr_item, ett_nfs_sattr);
333         }
334
335         /* some how we should indicate here, that -1 means "do not set" */
336         offset = dissect_mode         (pd,offset,fd,sattr_tree,"mode");
337         offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"uid");
338         offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"gid");
339         offset = dissect_unsigned_int (pd,offset,fd,sattr_tree,"size");
340         offset = dissect_timeval      (pd,offset,fd,sattr_tree,"atime");
341         offset = dissect_timeval      (pd,offset,fd,sattr_tree,"mtime");
342
343         /* now we know, that sattr is shorter */
344         if (sattr_item) {
345                 proto_item_set_len(sattr_item, offset - old_offset);
346         }
347
348         return offset;
349 }
350
351
352 /* generic NFS2 call dissector */
353 int
354 dissect_nfs2_any_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
355 {
356         offset = dissect_fhandle(pd, offset, fd, tree, "object");
357
358         return offset;
359 }
360
361
362 /* generic NFS2 reply dissector */
363 int
364 dissect_nfs2_any_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
365 {
366         guint32 status;
367
368         offset = dissect_stat(pd, offset, fd, tree, "status", &status);
369
370         return offset;
371 }
372
373
374 /* RFC 1094, Page 5 */
375 int
376 dissect_nfs2_getattr_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
377 {
378         offset = dissect_fhandle(pd, offset, fd, tree, "object");
379
380         return offset;
381 }
382
383
384 /* RFC 1094, Page 5 */
385 int
386 dissect_nfs2_getattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
387 {
388         guint32 status;
389
390         /* attrstat: RFC 1094, Page 17 */
391         offset = dissect_stat(pd, offset, fd, tree, "status", &status);
392         switch (status) {
393                 case 0:
394                         offset = dissect_fattr(pd, offset, fd, tree, "attributes");
395                 break;
396                 default:
397                         /* do nothing */
398                 break;
399         }
400
401         return offset;
402 }
403
404
405 /* RFC 1094, Page 6 */
406 int
407 dissect_nfs2_setattr_call(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
408 {
409         offset = dissect_fhandle(pd, offset, fd, tree, "file"      );
410         offset = dissect_sattr  (pd, offset, fd, tree, "attributes");
411
412         return offset;
413 }
414
415
416 /* RFC 1094, Page 6 */
417 int
418 dissect_nfs2_setattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
419 {
420         guint32 status;
421
422         /* attrstat: RFC 1094, Page 17 */
423         offset = dissect_stat(pd, offset, fd, tree, "status", &status);
424         switch (status) {
425                 case 0:
426                         offset = dissect_fattr(pd, offset, fd, tree, "attributes");
427                 break;
428                 default:
429                         /* do nothing */
430                 break;
431         }
432
433         return offset;
434 }
435
436
437 /* more to come here */
438
439
440 /* proc number, "proc name", dissect_request, dissect_reply */
441 /* NULL as function pointer means: take the generic one. */
442 const vsff nfs2_proc[] = {
443         { 0,    "NULL",         NULL,                           NULL },
444         { 1,    "GETATTR",      dissect_nfs2_getattr_call,      dissect_nfs2_getattr_reply },
445         { 2,    "SETATTR",      dissect_nfs2_setattr_call,      dissect_nfs2_setattr_reply },
446         { 3,    "ROOT",         NULL,                           NULL },
447         { 4,    "LOOKUP",       dissect_nfs2_any_call,          dissect_nfs2_any_reply },
448         { 5,    "READLINK",     dissect_nfs2_any_call,          dissect_nfs2_any_reply },
449         { 6,    "READ",         dissect_nfs2_any_call,          dissect_nfs2_any_reply },
450         { 7,    "WRITECACHE",   NULL,                           NULL },
451         { 8,    "WRITE",        dissect_nfs2_any_call,          dissect_nfs2_any_reply },
452         { 9,    "CREATE",       dissect_nfs2_any_call,          dissect_nfs2_any_reply },
453         { 10,   "REMOVE",       dissect_nfs2_any_call,          dissect_nfs2_any_reply },
454         { 11,   "RENAME",       dissect_nfs2_any_call,          dissect_nfs2_any_reply },
455         { 12,   "LINK",         dissect_nfs2_any_call,          dissect_nfs2_any_reply },
456         { 13,   "SYMLINK",      dissect_nfs2_any_call,          dissect_nfs2_any_reply },
457         { 14,   "MKDIR",        dissect_nfs2_any_call,          dissect_nfs2_any_reply },
458         { 15,   "RMDIR",        dissect_nfs2_any_call,          dissect_nfs2_any_reply },
459         { 16,   "READDIR",      dissect_nfs2_any_call,          dissect_nfs2_any_reply },
460         { 17,   "STATFS",       dissect_nfs2_any_call,          dissect_nfs2_any_reply },
461         { 0,    NULL,           NULL,                           NULL }
462 };
463 /* end of NFS Version 2 */
464
465
466 /***************************/
467 /* NFS Version 3, RFC 1813 */
468 /***************************/
469
470
471 /* RFC 1813, Page 15 */
472 int
473 dissect_uint64(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
474 char* name)
475 {
476         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"uint64");
477         return offset;
478 }
479
480
481 /* RFC 1813, Page 15 */
482 int
483 dissect_uint32(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
484 char* name)
485 {
486         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"uint32");
487         return offset;
488 }
489
490
491 /* RFC 1813, Page 15 */
492 int
493 dissect_fileid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
494 char* name)
495 {
496         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"fileid3");
497         return offset;
498 }
499
500
501 /* RFC 1813, Page 16 */
502 int
503 dissect_uid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
504 char* name)
505 {
506         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"uid3"); 
507         return offset;
508 }
509
510
511 /* RFC 1813, Page 16 */
512 int
513 dissect_gid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
514 char* name)
515 {
516         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"gid3"); 
517         return offset;
518 }
519
520
521 /* RFC 1813, Page 16 */
522 int
523 dissect_size3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
524 char* name)
525 {
526         offset = dissect_rpc_uint64(pd,offset,fd,tree,name,"size3"); 
527         return offset;
528 }
529
530
531 /* RFC 1813, Page 16 */
532 int
533 dissect_mode3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
534 char* name)
535 {
536         guint32 mode3;
537         proto_item* mode3_item = NULL;
538         proto_tree* mode3_tree = NULL;
539
540         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
541         mode3 = EXTRACT_UINT(pd, offset+0);
542         
543         if (tree) {
544                 mode3_item = proto_tree_add_text(tree, offset, 4,
545                         "%s: 0%o", name, mode3);
546                 if (mode3_item)
547                         mode3_tree = proto_item_add_subtree(mode3_item, ett_nfs_mode3);
548         }
549
550         /* RFC 1813, Page 23 */
551         if (mode3_tree) {
552                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
553                 decode_boolean_bitfield(mode3,   0x800, 12, "Set user id on exec", "not SUID"));
554                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
555                 decode_boolean_bitfield(mode3,   0x400, 12, "Set group id on exec", "not SGID"));
556                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
557                 decode_boolean_bitfield(mode3,   0x200, 12, "Save swapped text even after use", "not save swapped text"));
558                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
559                 decode_boolean_bitfield(mode3,   0x100, 12, "Read permission for owner", "no Read permission for owner"));
560                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
561                 decode_boolean_bitfield(mode3,    0x80, 12, "Write permission for owner", "no Write permission for owner"));
562                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
563                 decode_boolean_bitfield(mode3,    0x40, 12, "Execute permission for owner", "no Execute permission for owner"));
564                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
565                 decode_boolean_bitfield(mode3,    0x20, 12, "Read permission for group", "no Read permission for group"));
566                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
567                 decode_boolean_bitfield(mode3,    0x10, 12, "Write permission for group", "no Write permission for group"));
568                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
569                 decode_boolean_bitfield(mode3,     0x8, 12, "Execute permission for group", "no Execute permission for group"));
570                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
571                 decode_boolean_bitfield(mode3,     0x4, 12, "Read permission for others", "no Read permission for others"));
572                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
573                 decode_boolean_bitfield(mode3,     0x2, 12, "Write permission for others", "no Write permission for others"));
574                 proto_tree_add_text(mode3_tree, offset, 4, "%s",
575                 decode_boolean_bitfield(mode3,     0x1, 12, "Execute permission for others", "no Execute permission for others"));
576         }
577
578         offset += 4;
579         return offset;
580 }
581
582
583 /* RFC 1813, Page 16 */
584 int
585 dissect_count3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
586 char* name, char* type)
587 {
588         offset = dissect_rpc_uint32(pd,offset,fd,tree,name,"count");
589         return offset;
590 }
591
592
593 /* RFC 1813, Page 16 */
594 int
595 dissect_nfsstat3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
596 char* name, guint32* status)
597 {
598         guint32 nfsstat3;
599         char* nfsstat3_name = NULL;
600
601         const value_string nfs3_nfsstat3[] =
602         {
603                 {       0,      "OK" },
604                 {       1,      "ERR_PERM" },
605                 {       2,      "ERR_NOENT" },
606                 {       5,      "ERR_IO" },
607                 {       6,      "ERR_NX_IO" },
608                 {       13,     "ERR_ACCES" },
609                 {       17,     "ERR_EXIST" },
610                 {       18,     "ERR_XDEV" },
611                 {       19,     "ERR_NODEV" },
612                 {       20,     "ERR_NOTDIR" },
613                 {       21,     "ERR_ISDIR" },
614                 {       22,     "ERR_INVAL" },
615                 {       27,     "ERR_FBIG" },
616                 {       28,     "ERR_NOSPC" },
617                 {       30,     "ERR_ROFS" },
618                 {       31,     "ERR_MLINK" },
619                 {       63,     "ERR_NAMETOOLONG" },
620                 {       66,     "ERR_NOTEMPTY" },
621                 {       69,     "ERR_DQUOT" },
622                 {       70,     "ERR_STALE" },
623                 {       71,     "ERR_REMOTE" },
624                 {       10001,  "ERR_BADHANDLE" },
625 /* RFC 1813, Page 17 */
626                 {       10002,  "ERR_NOT_SYNC" },
627                 {       10003,  "ERR_BAD_COOKIE" },
628                 {       10004,  "ERR_NOTSUPP" },
629                 {       10005,  "ERR_TOOSMALL" },
630                 {       10006,  "ERR_SERVERFAULT" },
631                 {       10007,  "ERR_BADTYPE" },
632                 {       10008,  "ERR_JUKEBOX" },
633                 {       0,      NULL }
634         };
635
636         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
637         nfsstat3 = EXTRACT_UINT(pd, offset+0);
638         nfsstat3_name = val_to_str(nfsstat3, nfs3_nfsstat3, "%u");
639         
640         if (tree) {
641                 proto_tree_add_text(tree, offset, 4,
642                         "%s: %s (%u)", name, nfsstat3_name, nfsstat3);
643         }
644
645         offset += 4;
646         *status = nfsstat3;
647         return offset;
648 }
649
650
651 /* RFC 1813, Page 17, 18, 19, 20: error explanations */
652
653
654 /* RFC 1813, Page 20 */
655 int
656 dissect_ftype3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
657 char* name)
658 {
659         guint32 ftype3;
660         char* ftype3_name = NULL;
661
662         const value_string nfs3_ftype3[] =
663         {
664                 {       1,      "Regular File" },
665                 {       2,      "Directory" },
666                 {       3,      "Block Special Device" },
667                 {       4,      "Character Special Device" },
668                 {       5,      "Symbolic Link" },
669                 {       6,      "Socket" },
670                 {       7,      "Named Pipe" },
671                 {       0,      NULL }
672         };
673
674         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
675         ftype3 = EXTRACT_UINT(pd, offset+0);
676         ftype3_name = val_to_str(ftype3, nfs3_ftype3, "%u");
677         
678         if (tree) {
679                 proto_tree_add_text(tree, offset, 4,
680                         "%s: %s (%u)", name, ftype3_name, ftype3);
681         }
682
683         offset += 4;
684         return offset;
685 }
686
687
688 /* RFC 1813, Page 20 */
689 int
690 dissect_specdata3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
691 {
692         guint32 specdata1;
693         guint32 specdata2;
694
695         proto_item* specdata3_item;
696         proto_tree* specdata3_tree = NULL;
697
698         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
699         specdata1 = EXTRACT_UINT(pd, offset+0);
700         specdata2 = EXTRACT_UINT(pd, offset+4);
701         
702         if (tree) {
703                 specdata3_item = proto_tree_add_text(tree, offset, 8,
704                         "%s: %u,%u", name, specdata1, specdata2);
705                 if (specdata3_item)
706                         specdata3_tree = proto_item_add_subtree(specdata3_item,
707                                         ett_nfs_specdata3);
708         }
709
710         if (specdata3_tree) {
711                 proto_tree_add_text(specdata3_tree,offset+0,4,
712                                         "specdata1: %u", specdata1);
713                 proto_tree_add_text(specdata3_tree,offset+4,4,
714                                         "specdata2: %u", specdata2);
715         }
716
717         offset += 8;
718         return offset;
719 }
720
721
722 /* RFC 1813, Page 21 */
723 int
724 dissect_nfs_fh3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
725 {
726         guint fh3_len;
727         guint fh3_len_full;
728         guint fh3_fill;
729         proto_item* fitem;
730         proto_tree* ftree = NULL;
731
732         fh3_len = EXTRACT_UINT(pd, offset+0);
733         fh3_len_full = rpc_roundup(fh3_len);
734         fh3_fill = fh3_len_full - fh3_len;
735         
736         if (tree) {
737                 fitem = proto_tree_add_text(tree, offset, 4+fh3_len_full,
738                         "%s", name);
739                 if (fitem)
740                         ftree = proto_item_add_subtree(fitem, ett_nfs_fh3);
741         }
742
743         if (ftree) {
744                 proto_tree_add_text(ftree,offset+0,4,
745                                         "length: %u", fh3_len);
746                 proto_tree_add_text(ftree,offset+4,fh3_len,
747                                         "file handle (opaque data)");
748                 if (fh3_fill)
749                         proto_tree_add_text(ftree,offset+4+fh3_len,fh3_fill,
750                                 "fill bytes");
751         }
752         offset += 4 + fh3_len_full;
753         return offset;
754 }
755
756
757 /* RFC 1813, Page 21 */
758 int
759 dissect_nfstime3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,char* name)
760 {
761         guint32 seconds;
762         guint32 nseconds;
763
764         proto_item* time_item;
765         proto_tree* time_tree = NULL;
766
767         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
768         seconds = EXTRACT_UINT(pd, offset+0);
769         nseconds = EXTRACT_UINT(pd, offset+4);
770         
771         if (tree) {
772                 time_item = proto_tree_add_text(tree, offset, 8,
773                         "%s: %u.%09u", name, seconds, nseconds);
774                 if (time_item)
775                         time_tree = proto_item_add_subtree(time_item, ett_nfs_nfstime3);
776         }
777
778         if (time_tree) {
779                 proto_tree_add_text(time_tree,offset+0,4,
780                                         "seconds: %u", seconds);
781                 proto_tree_add_text(time_tree,offset+4,4,
782                                         "nano seconds: %u", nseconds);
783         }
784         offset += 8;
785         return offset;
786 }
787
788
789 /* RFC 1813, Page 22 */
790 int
791 dissect_fattr3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
792 {
793         proto_item* fattr3_item = NULL;
794         proto_tree* fattr3_tree = NULL;
795         int old_offset = offset;
796
797         if (tree) {
798                 fattr3_item = proto_tree_add_text(tree, offset,
799                         END_OF_FRAME, "%s", name);
800                 if (fattr3_item)
801                         fattr3_tree = proto_item_add_subtree(fattr3_item, ett_nfs_fattr3);
802         }
803
804         offset = dissect_ftype3   (pd,offset,fd,fattr3_tree,"type");
805         offset = dissect_mode3    (pd,offset,fd,fattr3_tree,"mode");
806         offset = dissect_uint32   (pd,offset,fd,fattr3_tree,"nlink");
807         offset = dissect_uid3     (pd,offset,fd,fattr3_tree,"uid");
808         offset = dissect_gid3     (pd,offset,fd,fattr3_tree,"gid");
809         offset = dissect_size3    (pd,offset,fd,fattr3_tree,"size");
810         offset = dissect_size3    (pd,offset,fd,fattr3_tree,"used");
811         offset = dissect_specdata3(pd,offset,fd,fattr3_tree,"rdev");
812         offset = dissect_uint64   (pd,offset,fd,fattr3_tree,"fsid");
813         offset = dissect_fileid3  (pd,offset,fd,fattr3_tree,"fileid");
814         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"atime");
815         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"mtime");
816         offset = dissect_nfstime3 (pd,offset,fd,fattr3_tree,"ctime");
817
818         /* now we know, that fattr3 is shorter */
819         if (fattr3_item) {
820                 proto_item_set_len(fattr3_item, offset - old_offset);
821         }
822
823         return offset;
824 }
825
826
827 const value_string value_follows[3] =
828         {
829                 { 0, "no value" },
830                 { 1, "value follows"},
831                 { 0, NULL }
832         };
833
834
835 /* RFC 1813, Page 23 */
836 int
837 dissect_post_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
838 {
839         proto_item* post_op_attr_item = NULL;
840         proto_tree* post_op_attr_tree = NULL;
841         int old_offset = offset;
842         guint32 attributes_follow;
843
844         if (tree) {
845                 post_op_attr_item = proto_tree_add_text(tree, offset,
846                         END_OF_FRAME, "%s", name);
847                 if (post_op_attr_item)
848                         post_op_attr_tree = proto_item_add_subtree(post_op_attr_item, ett_nfs_post_op_attr);
849         }
850
851         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
852         attributes_follow = EXTRACT_UINT(pd, offset+0);
853         proto_tree_add_text(post_op_attr_tree, offset, 4,
854                 "attributes_follow: %s (%u)", 
855                 val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
856         offset += 4;
857         switch (attributes_follow) {
858                 case TRUE:
859                         offset = dissect_fattr3(pd, offset, fd, post_op_attr_tree,
860                                         "attributes");
861                 break;
862                 case FALSE:
863                         /* void */
864                 break;
865         }
866         
867         /* now we know, that post_op_attr_tree is shorter */
868         if (post_op_attr_item) {
869                 proto_item_set_len(post_op_attr_item, offset - old_offset);
870         }
871
872         return offset;
873 }
874
875
876 /* RFC 1813, Page 24 */
877 int
878 dissect_wcc_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
879 {
880         proto_item* wcc_attr_item = NULL;
881         proto_tree* wcc_attr_tree = NULL;
882         int old_offset = offset;
883
884         if (tree) {
885                 wcc_attr_item = proto_tree_add_text(tree, offset,
886                         END_OF_FRAME, "%s", name);
887                 if (wcc_attr_item)
888                         wcc_attr_tree = proto_item_add_subtree(wcc_attr_item, ett_nfs_wcc_attr);
889         }
890
891         offset = dissect_size3   (pd, offset, fd, wcc_attr_tree, "size" );
892         offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "mtime");
893         offset = dissect_nfstime3(pd, offset, fd, wcc_attr_tree, "ctime");
894         
895         /* now we know, that wcc_attr_tree is shorter */
896         if (wcc_attr_item) {
897                 proto_item_set_len(wcc_attr_item, offset - old_offset);
898         }
899
900         return offset;
901 }
902
903
904 /* RFC 1813, Page 24 */
905 int
906 dissect_pre_op_attr(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
907 {
908         proto_item* pre_op_attr_item = NULL;
909         proto_tree* pre_op_attr_tree = NULL;
910         int old_offset = offset;
911         guint32 attributes_follow;
912
913         if (tree) {
914                 pre_op_attr_item = proto_tree_add_text(tree, offset,
915                         END_OF_FRAME, "%s", name);
916                 if (pre_op_attr_item)
917                         pre_op_attr_tree = proto_item_add_subtree(pre_op_attr_item, ett_nfs_pre_op_attr);
918         }
919
920         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
921         attributes_follow = EXTRACT_UINT(pd, offset+0);
922         proto_tree_add_text(pre_op_attr_tree, offset, 4,
923                 "attributes_follow: %s (%u)", 
924                 val_to_str(attributes_follow,value_follows,"Unknown"), attributes_follow);
925         offset += 4;
926         switch (attributes_follow) {
927                 case TRUE:
928                         offset = dissect_wcc_attr(pd, offset, fd, pre_op_attr_tree,
929                                         "attributes");
930                 break;
931                 case FALSE:
932                         /* void */
933                 break;
934         }
935         
936         /* now we know, that pre_op_attr_tree is shorter */
937         if (pre_op_attr_item) {
938                 proto_item_set_len(pre_op_attr_item, offset - old_offset);
939         }
940
941         return offset;
942 }
943
944
945 /* RFC 1813, Page 24 */
946 int
947 dissect_wcc_data(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
948 {
949         proto_item* wcc_data_item = NULL;
950         proto_tree* wcc_data_tree = NULL;
951         int old_offset = offset;
952
953         if (tree) {
954                 wcc_data_item = proto_tree_add_text(tree, offset,
955                         END_OF_FRAME, "%s", name);
956                 if (wcc_data_item)
957                         wcc_data_tree = proto_item_add_subtree(wcc_data_item, ett_nfs_wcc_data);
958         }
959
960         offset = dissect_pre_op_attr (pd, offset, fd, wcc_data_tree, "before");
961         offset = dissect_post_op_attr(pd, offset, fd, wcc_data_tree, "after" );
962
963         /* now we know, that wcc_data is shorter */
964         if (wcc_data_item) {
965                 proto_item_set_len(wcc_data_item, offset - old_offset);
966         }
967
968         return offset;
969 }
970
971
972 /* RFC 1813, Page 25 */
973 int
974 dissect_set_mode3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
975 {
976         proto_item* set_mode3_item = NULL;
977         proto_tree* set_mode3_tree = NULL;
978         int old_offset = offset;
979         guint32 set_it;
980         char* set_it_name;
981
982         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
983         set_it = EXTRACT_UINT(pd, offset+0);
984         set_it_name = val_to_str(set_it,value_follows,"Unknown");
985
986         if (tree) {
987                 set_mode3_item = proto_tree_add_text(tree, offset,
988                         END_OF_FRAME, "%s: %s", name, set_it_name);
989                 if (set_mode3_item)
990                         set_mode3_tree = proto_item_add_subtree(set_mode3_item, ett_nfs_set_mode3);
991         }
992
993         if (set_mode3_tree)
994                 proto_tree_add_text(set_mode3_tree, offset, 4,
995                         "set_it: %s (%u)", set_it_name, set_it);
996
997         offset += 4;
998
999         switch (set_it) {
1000                 case 1:
1001                         offset = dissect_mode3(pd, offset, fd, set_mode3_tree,
1002                                         "mode");
1003                 break;
1004                 default:
1005                         /* void */
1006                 break;
1007         }
1008         
1009         /* now we know, that set_mode3 is shorter */
1010         if (set_mode3_item) {
1011                 proto_item_set_len(set_mode3_item, offset - old_offset);
1012         }
1013
1014         return offset;
1015 }
1016
1017
1018 /* RFC 1813, Page 26 */
1019 int
1020 dissect_set_uid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1021 {
1022         proto_item* set_uid3_item = NULL;
1023         proto_tree* set_uid3_tree = NULL;
1024         int old_offset = offset;
1025         guint32 set_it;
1026         char* set_it_name;
1027
1028         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1029         set_it = EXTRACT_UINT(pd, offset+0);
1030         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1031
1032         if (tree) {
1033                 set_uid3_item = proto_tree_add_text(tree, offset,
1034                         END_OF_FRAME, "%s: %s", name, set_it_name);
1035                 if (set_uid3_item)
1036                         set_uid3_tree = proto_item_add_subtree(set_uid3_item, ett_nfs_set_uid3);
1037         }
1038
1039         if (set_uid3_tree)
1040                 proto_tree_add_text(set_uid3_tree, offset, 4,
1041                         "set_it: %s (%u)", set_it_name, set_it);
1042
1043         offset += 4;
1044
1045         switch (set_it) {
1046                 case 1:
1047                         offset = dissect_uid3(pd, offset, fd, set_uid3_tree,
1048                                         "uid");
1049                 break;
1050                 default:
1051                         /* void */
1052                 break;
1053         }
1054
1055         /* now we know, that set_uid3 is shorter */
1056         if (set_uid3_item) {
1057                 proto_item_set_len(set_uid3_item, offset - old_offset);
1058         }
1059
1060         return offset;
1061 }
1062
1063
1064 /* RFC 1813, Page 26 */
1065 int
1066 dissect_set_gid3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1067 {
1068         proto_item* set_gid3_item = NULL;
1069         proto_tree* set_gid3_tree = NULL;
1070         int old_offset = offset;
1071         guint32 set_it;
1072         char* set_it_name;
1073
1074         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1075         set_it = EXTRACT_UINT(pd, offset+0);
1076         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1077
1078         if (tree) {
1079                 set_gid3_item = proto_tree_add_text(tree, offset,
1080                         END_OF_FRAME, "%s: %s", name, set_it_name);
1081                 if (set_gid3_item)
1082                         set_gid3_tree = proto_item_add_subtree(set_gid3_item, ett_nfs_set_gid3);
1083         }
1084
1085         if (set_gid3_tree)
1086                 proto_tree_add_text(set_gid3_tree, offset, 4,
1087                         "set_it: %s (%u)", set_it_name, set_it);
1088
1089         offset += 4;
1090
1091         switch (set_it) {
1092                 case 1:
1093                         offset = dissect_gid3(pd, offset, fd, set_gid3_tree,
1094                                         "gid");
1095                 break;
1096                 default:
1097                         /* void */
1098                 break;
1099         }
1100
1101         /* now we know, that set_gid3 is shorter */
1102         if (set_gid3_item) {
1103                 proto_item_set_len(set_gid3_item, offset - old_offset);
1104         }
1105
1106         return offset;
1107 }
1108
1109
1110 /* RFC 1813, Page 26 */
1111 int
1112 dissect_set_size3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1113 {
1114         proto_item* set_size3_item = NULL;
1115         proto_tree* set_size3_tree = NULL;
1116         int old_offset = offset;
1117         guint32 set_it;
1118         char* set_it_name;
1119
1120         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1121         set_it = EXTRACT_UINT(pd, offset+0);
1122         set_it_name = val_to_str(set_it,value_follows,"Unknown");
1123
1124         if (tree) {
1125                 set_size3_item = proto_tree_add_text(tree, offset,
1126                         END_OF_FRAME, "%s: %s", name, set_it_name);
1127                 if (set_size3_item)
1128                         set_size3_tree = proto_item_add_subtree(set_size3_item, ett_nfs_set_size3);
1129         }
1130
1131         if (set_size3_tree)
1132                 proto_tree_add_text(set_size3_tree, offset, 4,
1133                         "set_it: %s (%u)", set_it_name, set_it);
1134
1135         offset += 4;
1136
1137         switch (set_it) {
1138                 case 1:
1139                         offset = dissect_size3(pd, offset, fd, set_size3_tree,
1140                                         "size");
1141                 break;
1142                 default:
1143                         /* void */
1144                 break;
1145         }
1146
1147         /* now we know, that set_size3 is shorter */
1148         if (set_size3_item) {
1149                 proto_item_set_len(set_size3_item, offset - old_offset);
1150         }
1151
1152         return offset;
1153 }
1154
1155
1156 /* RFC 1813, Page 25 */
1157 #define DONT_CHANGE 0
1158 #define SET_TO_SERVER_TIME 1
1159 #define SET_TO_CLIENT_TIME 2
1160
1161 const value_string time_how[] =
1162         {
1163                 { DONT_CHANGE,  "don't change" },
1164                 { SET_TO_SERVER_TIME, "set to server time" },
1165                 { SET_TO_CLIENT_TIME, "set to client time" },
1166                 { 0, NULL }
1167         };
1168
1169
1170 /* RFC 1813, Page 26 */
1171 int
1172 dissect_set_atime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1173 {
1174         proto_item* set_atime_item = NULL;
1175         proto_tree* set_atime_tree = NULL;
1176         int old_offset = offset;
1177         guint32 set_it;
1178         char* set_it_name;
1179
1180         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1181         set_it = EXTRACT_UINT(pd, offset+0);
1182         set_it_name = val_to_str(set_it,time_how,"Unknown");
1183
1184         if (tree) {
1185                 set_atime_item = proto_tree_add_text(tree, offset,
1186                         END_OF_FRAME, "%s: %s",
1187                         name, set_it_name, set_it);
1188                 if (set_atime_item)
1189                         set_atime_tree = proto_item_add_subtree(set_atime_item, ett_nfs_set_atime);
1190         }
1191
1192         if (set_atime_tree)
1193                 proto_tree_add_text(set_atime_tree, offset, 4,
1194                         "set_it: %s (%u)", set_it_name, set_it);
1195
1196         offset += 4;
1197
1198         switch (set_it) {
1199                 case SET_TO_CLIENT_TIME:
1200                         if (set_atime_item)
1201                         offset = dissect_nfstime3(pd, offset, fd, set_atime_tree,
1202                                         "atime");
1203                 break;
1204                 default:
1205                         /* void */
1206                 break;
1207         }
1208
1209         /* now we know, that set_atime is shorter */
1210         if (set_atime_item) {
1211                 proto_item_set_len(set_atime_item, offset - old_offset);
1212         }
1213
1214         return offset;
1215 }
1216
1217
1218 /* RFC 1813, Page 26 */
1219 int
1220 dissect_set_mtime(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1221 {
1222         proto_item* set_mtime_item = NULL;
1223         proto_tree* set_mtime_tree = NULL;
1224         int old_offset = offset;
1225         guint32 set_it;
1226         char* set_it_name;
1227
1228         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1229         set_it = EXTRACT_UINT(pd, offset+0);
1230         set_it_name = val_to_str(set_it,time_how,"Unknown");
1231
1232         if (tree) {
1233                 set_mtime_item = proto_tree_add_text(tree, offset,
1234                         END_OF_FRAME, "%s: %s",
1235                         name, set_it_name, set_it);
1236                 if (set_mtime_item)
1237                         set_mtime_tree = proto_item_add_subtree(set_mtime_item, ett_nfs_set_mtime);
1238         }
1239
1240         if (set_mtime_tree)
1241                 proto_tree_add_text(set_mtime_tree, offset, 4,
1242                                 "set_it: %s (%u)", set_it_name, set_it);
1243
1244         offset += 4;
1245
1246         switch (set_it) {
1247                 case SET_TO_CLIENT_TIME:
1248                         if (set_mtime_item)
1249                         offset = dissect_nfstime3(pd, offset, fd, set_mtime_tree,
1250                                         "atime");
1251                 break;
1252                 default:
1253                         /* void */
1254                 break;
1255         }
1256
1257         /* now we know, that set_mtime is shorter */
1258         if (set_mtime_item) {
1259                 proto_item_set_len(set_mtime_item, offset - old_offset);
1260         }
1261
1262         return offset;
1263 }
1264
1265
1266 /* RFC 1813, Page 26 */
1267 int
1268 dissect_sattr3(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
1269 {
1270         proto_item* sattr3_item = NULL;
1271         proto_tree* sattr3_tree = NULL;
1272         int old_offset = offset;
1273
1274         if (tree) {
1275                 sattr3_item = proto_tree_add_text(tree, offset,
1276                         END_OF_FRAME, "%s", name);
1277                 if (sattr3_item)
1278                         sattr3_tree = proto_item_add_subtree(sattr3_item, ett_nfs_sattr3);
1279         }
1280
1281         offset = dissect_set_mode3(pd, offset, fd, sattr3_tree, "mode");
1282         offset = dissect_set_uid3 (pd, offset, fd, sattr3_tree, "uid");
1283         offset = dissect_set_gid3 (pd, offset, fd, sattr3_tree, "gid");
1284         offset = dissect_set_size3(pd, offset, fd, sattr3_tree, "size");
1285         offset = dissect_set_atime(pd, offset, fd, sattr3_tree, "atime");
1286         offset = dissect_set_mtime(pd, offset, fd, sattr3_tree, "mtime");
1287
1288         /* now we know, that sattr3 is shorter */
1289         if (sattr3_item) {
1290                 proto_item_set_len(sattr3_item, offset - old_offset);
1291         }
1292
1293         return offset;
1294 }
1295
1296
1297 /* generic NFS3 call dissector */
1298 int
1299 dissect_nfs3_any_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1300 {
1301         offset = dissect_nfs_fh3(pd, offset, fd, tree, "object");
1302         return offset;
1303 }
1304
1305
1306 /* generic NFS3 reply dissector */
1307 int
1308 dissect_nfs3_any_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1309 {
1310         guint32 status;
1311
1312         offset = dissect_nfsstat3(pd, offset, fd, tree, "status", &status);
1313
1314         return offset;
1315
1316 }
1317
1318
1319 /* RFC 1813, Page 32 */
1320 int
1321 dissect_nfs3_getattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1322 {
1323         offset = dissect_nfs_fh3(pd, offset, fd, tree, "object");
1324         return offset;
1325 }
1326
1327
1328 /* RFC 1813, Page 32 */
1329 int
1330 dissect_nfs3_getattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1331 {
1332         guint32 status;
1333
1334         offset = dissect_nfsstat3(pd, offset, fd, tree, "status", &status);
1335         switch (status) {
1336                 case 0:
1337                         offset = dissect_fattr3(pd, offset, fd, tree, "obj_attributes");
1338                 break;
1339                 default:
1340                         /* void */
1341                 break;
1342         }
1343                 
1344         return offset;
1345 }
1346
1347
1348 /* RFC 1813, Page 33 */
1349 int
1350 dissect_sattrguard3(const u_char* pd, int offset, frame_data* fd, proto_tree* tree, char *name)
1351 {
1352         proto_item* sattrguard3_item = NULL;
1353         proto_tree* sattrguard3_tree = NULL;
1354         int old_offset = offset;
1355         guint32 check;
1356         char* check_name;
1357
1358         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
1359         check = EXTRACT_UINT(pd, offset+0);
1360         check_name = val_to_str(check,value_follows,"Unknown");
1361
1362         if (tree) {
1363                 sattrguard3_item = proto_tree_add_text(tree, offset,
1364                         END_OF_FRAME, "%s: %s", name, check_name);
1365                 if (sattrguard3_item)
1366                         sattrguard3_tree = proto_item_add_subtree(sattrguard3_item, ett_nfs_sattrguard3);
1367         }
1368
1369         if (sattrguard3_tree)
1370                 proto_tree_add_text(sattrguard3_tree, offset, 4,
1371                         "check: %s (%u)", check_name, check);
1372
1373         offset += 4;
1374
1375         switch (check) {
1376                 case TRUE:
1377                         offset = dissect_nfstime3(pd, offset, fd, sattrguard3_tree,
1378                                         "obj_ctime");
1379                 break;
1380                 case FALSE:
1381                         /* void */
1382                 break;
1383         }
1384
1385         /* now we know, that sattrguard3 is shorter */
1386         if (sattrguard3_item) {
1387                 proto_item_set_len(sattrguard3_item, offset - old_offset);
1388         }
1389
1390         return offset;
1391 }
1392
1393
1394 /* RFC 1813, Page 33 */
1395 int
1396 dissect_nfs3_setattr_call(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1397 {
1398         offset = dissect_nfs_fh3    (pd, offset, fd, tree, "object");
1399         offset = dissect_sattr3     (pd, offset, fd, tree, "new_attributes");
1400         offset = dissect_sattrguard3(pd, offset, fd, tree, "guard");
1401         return offset;
1402 }
1403
1404
1405 /* RFC 1813, Page 33 */
1406 int
1407 dissect_nfs3_setattr_reply(const u_char* pd, int offset, frame_data* fd, proto_tree* tree)
1408 {
1409         guint32 status;
1410
1411         offset = dissect_nfsstat3(pd, offset, fd, tree, "status", &status);
1412         switch (status) {
1413                 case 0:
1414                         offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
1415                 break;
1416                 default:
1417                         offset = dissect_wcc_data(pd, offset, fd, tree, "obj_wcc");
1418                 break;
1419         }
1420                 
1421         return offset;
1422 }
1423
1424
1425 /* proc number, "proc name", dissect_request, dissect_reply */
1426 /* NULL as function pointer means: take the generic one. */
1427 const vsff nfs3_proc[] = {
1428         { 0,    "NULL",         NULL,                           NULL },
1429         { 1,    "GETATTR",      dissect_nfs3_getattr_call,      dissect_nfs3_getattr_reply },
1430         { 2,    "SETATTR",      dissect_nfs3_setattr_call,      dissect_nfs3_setattr_reply },
1431         { 3,    "LOOKUP",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1432         { 4,    "ACCESS",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1433         { 5,    "READLINK",     dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1434         { 6,    "READ",         dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1435         { 7,    "WRITE",        dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1436         { 8,    "CREATE",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1437         { 9,    "MKDIR",        dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1438         { 10,   "SYMLINK",      dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1439         { 11,   "MKNOD",        dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1440         { 12,   "REMOVE",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1441         { 13,   "RMDIR",        dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1442         { 14,   "RENAME",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1443         { 15,   "LINK",         dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1444         { 16,   "READDIR",      dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1445         { 17,   "READDIRPLUS",  dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1446         { 18,   "FSSTAT",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1447         { 19,   "FSINFO",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1448         { 20,   "PATHCONF",     dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1449         { 21,   "COMMIT",       dissect_nfs3_any_call,          dissect_nfs3_any_reply },
1450         { 0,    NULL,           NULL,                           NULL }
1451 };
1452 /* end of NFS Version 3 */
1453
1454
1455 void
1456 proto_register_nfs(void)
1457 {
1458         static gint *ett[] = {
1459                 &ett_nfs,
1460                 &ett_nfs_fhandle,
1461                 &ett_nfs_timeval,
1462                 &ett_nfs_mode,
1463                 &ett_nfs_fattr,
1464                 &ett_nfs_sattr,
1465                 &ett_nfs_mode3,
1466                 &ett_nfs_specdata3,
1467                 &ett_nfs_fh3,
1468                 &ett_nfs_nfstime3,
1469                 &ett_nfs_fattr3,
1470                 &ett_nfs_sattr3,
1471                 &ett_nfs_sattrguard3,
1472                 &ett_nfs_set_mode3,
1473                 &ett_nfs_set_uid3,
1474                 &ett_nfs_set_gid3,
1475                 &ett_nfs_set_size3,
1476                 &ett_nfs_set_atime,
1477                 &ett_nfs_set_mtime,
1478                 &ett_nfs_pre_op_attr,
1479                 &ett_nfs_post_op_attr,
1480                 &ett_nfs_wcc_attr,
1481                 &ett_nfs_wcc_data,
1482         };
1483         proto_nfs = proto_register_protocol("Network File System", "nfs");
1484         proto_register_subtree_array(ett, array_length(ett));
1485
1486         /* Register the protocol as RPC */
1487         rpc_init_prog(proto_nfs, NFS_PROGRAM, ett_nfs);
1488         /* Register the procedure tables */
1489         rpc_init_proc_table(NFS_PROGRAM, 2, nfs2_proc);
1490         rpc_init_proc_table(NFS_PROGRAM, 3, nfs3_proc);
1491 }