Bump the version to 0.9.10.
[obnox/wireshark/wip.git] / packet-mount.c
1 /* packet-mount.c
2  * Routines for mount dissection
3  *
4  * $Id: packet-mount.c,v 1.37 2002/11/14 02:31:26 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * Copied from packet-smb.c
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31
32 #include <string.h>
33
34 #include "packet-rpc.h"
35 #include "packet-mount.h"
36 #include "packet-nfs.h"
37
38
39 static int proto_mount = -1;
40 static int proto_sgi_mount = -1;
41 static int hf_mount_procedure_v1 = -1;
42 static int hf_mount_procedure_v2 = -1;
43 static int hf_mount_procedure_v3 = -1;
44 static int hf_sgi_mount_procedure_v1 = -1;
45 static int hf_mount_path = -1;
46 static int hf_mount3_status = -1;
47 static int hf_mount_mountlist_hostname = -1;
48 static int hf_mount_mountlist_directory = -1;
49 static int hf_mount_mountlist = -1;
50 static int hf_mount_groups_group = -1;
51 static int hf_mount_groups = -1;
52 static int hf_mount_exportlist_directory = -1;
53 static int hf_mount_exportlist = -1;
54 static int hf_mount_has_options = -1;
55 static int hf_mount_options = -1;
56 static int hf_mount_pathconf_link_max = -1;
57 static int hf_mount_pathconf_max_canon = -1;
58 static int hf_mount_pathconf_max_input = -1;
59 static int hf_mount_pathconf_name_max = -1;
60 static int hf_mount_pathconf_path_max = -1;
61 static int hf_mount_pathconf_pipe_buf = -1;
62 static int hf_mount_pathconf_vdisable = -1;
63 static int hf_mount_pathconf_mask = -1;
64 static int hf_mount_pathconf_error_all = -1;
65 static int hf_mount_pathconf_error_link_max = -1;
66 static int hf_mount_pathconf_error_max_canon = -1;
67 static int hf_mount_pathconf_error_max_input = -1;
68 static int hf_mount_pathconf_error_name_max = -1;
69 static int hf_mount_pathconf_error_path_max = -1;
70 static int hf_mount_pathconf_error_pipe_buf = -1;
71 static int hf_mount_pathconf_chown_restricted = -1;
72 static int hf_mount_pathconf_no_trunc = -1;
73 static int hf_mount_pathconf_error_vdisable = -1;
74 static int hf_mount_statvfs_bsize = -1;
75 static int hf_mount_statvfs_frsize = -1;
76 static int hf_mount_statvfs_blocks = -1;
77 static int hf_mount_statvfs_bfree = -1;
78 static int hf_mount_statvfs_bavail = -1;
79 static int hf_mount_statvfs_files = -1;
80 static int hf_mount_statvfs_ffree = -1;
81 static int hf_mount_statvfs_favail = -1;
82 static int hf_mount_statvfs_fsid = -1;
83 static int hf_mount_statvfs_basetype = -1;
84 static int hf_mount_statvfs_flag = -1;
85 static int hf_mount_statvfs_flag_rdonly = -1;
86 static int hf_mount_statvfs_flag_nosuid = -1;
87 static int hf_mount_statvfs_flag_notrunc = -1;
88 static int hf_mount_statvfs_flag_nodev = -1;
89 static int hf_mount_statvfs_flag_grpid = -1;
90 static int hf_mount_statvfs_flag_local = -1;
91 static int hf_mount_statvfs_namemax = -1;
92 static int hf_mount_statvfs_fstr = -1;
93 static int hf_mount_flavors = -1;
94 static int hf_mount_flavor = -1;
95
96 static gint ett_mount = -1;
97 static gint ett_mount_mountlist = -1;
98 static gint ett_mount_groups = -1;
99 static gint ett_mount_exportlist = -1;
100 static gint ett_mount_pathconf_mask = -1;
101 static gint ett_mount_statvfs_flag = -1;
102
103 #define MAX_GROUP_NAME_LIST 128
104 static char group_name_list[MAX_GROUP_NAME_LIST];
105 static int  group_names_len;
106
107 /* RFC 1094, Page 24 */
108 /* This function dissects fhstatus for v1 and v2 of the mount protocol.
109  * Formally, hf_mount3_status only define the status codes returned by version
110  * 3 of the protocol.
111  * Though not formally defined in the standard, we use the same
112  * value-to-string mappings as version 3 since we belive that this mapping
113  * is consistant with most v1 and v2 implementations.
114  */
115 static int
116 dissect_fhstatus(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
117 {
118         gint32 status;
119
120         status=tvb_get_ntohl(tvb,offset);
121         offset = dissect_rpc_uint32(tvb,tree,hf_mount3_status,offset);
122
123         switch (status) {
124                 case 0:
125                         offset = dissect_fhandle(tvb,offset,pinfo,tree,"fhandle");
126                 break;
127                 default:
128                         /* void */
129                 break;
130         }
131
132         return offset;
133 }
134
135
136 static int
137 dissect_mount_dirpath_call(tvbuff_t *tvb, int offset, packet_info *pinfo,
138                 proto_tree *tree)
139 {
140         if((!pinfo->fd->flags.visited) && nfs_file_name_snooping){
141                 rpc_call_info_value *civ=pinfo->private_data;
142
143                 if(civ->request && (civ->proc==1)){
144                         unsigned char *host, *name;
145                         unsigned const char *dir;
146                         int len;
147
148                         host=ip_to_str(pinfo->dst.data);
149                         len=tvb_get_ntohl(tvb, offset);
150
151                         dir=tvb_get_ptr(tvb, offset+4, len);
152                         if(dir){
153                                 unsigned char *ptr;
154                                 name=g_malloc(strlen(host)+1+len+1+200);
155                                 ptr=name;
156                                 memcpy(ptr, host, strlen(host));
157                                 ptr+=strlen(host);
158                                 *ptr++=':';
159                                 memcpy(ptr, dir, len);
160                                 ptr+=len;
161                                 *ptr=0;
162
163                                 nfs_name_snoop_add_name(civ->xid, tvb, -1, strlen(name), 0, 0, name);
164                         }
165                 }
166         }
167
168         if ( tree )
169         {
170                 offset = dissect_rpc_string(tvb,tree,hf_mount_path,offset,NULL);
171         }
172
173         return offset;
174 }
175
176
177 /* RFC 1094, Page 25,26 */
178 static int
179 dissect_mount1_mnt_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
180 {
181         offset = dissect_fhstatus(tvb,offset,pinfo,tree);
182
183         return offset;
184 }
185
186
187
188 /* RFC 1094, Page 26 */
189 /* RFC 1813, Page 110 */
190 static int
191 dissect_mountlist(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
192 {
193         proto_item* lock_item = NULL;
194         proto_tree* lock_tree = NULL;
195         int old_offset = offset;
196         char* hostname;
197         char* directory;
198
199         if (tree) {
200                 lock_item = proto_tree_add_item(tree, hf_mount_mountlist, tvb,
201                                         offset, -1, FALSE);
202                 if (lock_item)
203                         lock_tree = proto_item_add_subtree(lock_item, ett_mount_mountlist);
204         }
205
206         offset = dissect_rpc_string(tvb, lock_tree,
207                         hf_mount_mountlist_hostname, offset, &hostname);
208         offset = dissect_rpc_string(tvb, lock_tree,
209                         hf_mount_mountlist_directory, offset, &directory);
210
211         if (lock_item) {
212                 /* now we have a nicer string */
213                 proto_item_set_text(lock_item, "Mount List Entry: %s:%s", hostname, directory);
214                 /* now we know, that mountlist is shorter */
215                 proto_item_set_len(lock_item, offset - old_offset);
216         }
217         g_free(hostname);
218         g_free(directory);
219
220         return offset;
221 }
222
223
224 /* RFC 1094, Page 26 */
225 /* RFC 1813, Page 110 */
226 static int
227 dissect_mount_dump_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
228 {
229         offset = dissect_rpc_list(tvb, pinfo, tree, offset, dissect_mountlist);
230
231         return offset;
232 }
233
234
235
236 /* RFC 1094, Page 26 */
237 /* RFC 1813, Page 110 */
238 static int
239 dissect_group(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
240 {
241         int len,str_len;
242         len=tvb_get_ntohl(tvb,offset);
243         str_len=tvb_get_nstringz(tvb,offset+4,
244                 MAX_GROUP_NAME_LIST-5-group_names_len,
245                 group_name_list+group_names_len);
246         if((group_names_len>=(MAX_GROUP_NAME_LIST-5))||(str_len<0)){
247                 strcpy(group_name_list+(MAX_GROUP_NAME_LIST-5),"...");
248                 group_names_len=MAX_GROUP_NAME_LIST-1;
249         } else {
250                 group_names_len+=str_len;
251                 group_name_list[group_names_len++]=' ';
252         }
253         group_name_list[group_names_len]=0;
254
255         offset = dissect_rpc_string(tvb, tree,
256                         hf_mount_groups_group, offset, NULL);
257
258         return offset;
259 }
260
261
262 /* RFC 1094, Page 26 */
263 /* RFC 1813, Page 113 */
264 static int
265 dissect_exportlist(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
266 {
267         proto_item* exportlist_item = NULL;
268         proto_tree* exportlist_tree = NULL;
269         int old_offset = offset;
270         int groups_offset;
271         proto_item* groups_item = NULL;
272         proto_item* groups_tree = NULL;
273         char* directory;
274
275         group_name_list[0]=0;
276         group_names_len=0;
277         if (tree) {
278                 exportlist_item = proto_tree_add_item(tree, hf_mount_exportlist, tvb,
279                                         offset, -1, FALSE);
280                 if (exportlist_item)
281                         exportlist_tree = proto_item_add_subtree(exportlist_item, ett_mount_exportlist);
282         }
283
284         offset = dissect_rpc_string(tvb, exportlist_tree,
285                         hf_mount_exportlist_directory, offset, &directory);
286         groups_offset = offset;
287
288         if (tree) {
289                 groups_item = proto_tree_add_item(exportlist_tree, hf_mount_groups, tvb,
290                                         offset, -1, FALSE);
291                 if (groups_item)
292                         groups_tree = proto_item_add_subtree(groups_item, ett_mount_groups);
293         }
294
295         offset = dissect_rpc_list(tvb, pinfo, groups_tree, offset, dissect_group);
296         if (groups_item) {
297                 /* mark empty lists */
298                 if (offset - groups_offset == 4) {
299                         proto_item_set_text(groups_item, "Groups: empty");
300                 }
301
302                 /* now we know, that groups is shorter */
303                 proto_item_set_len(groups_item, offset - groups_offset);
304         }
305
306         if (exportlist_item) {
307                 /* now we have a nicer string */
308                 proto_item_set_text(exportlist_item, "Export List Entry: %s -> %s", directory,group_name_list);
309                 /* now we know, that exportlist is shorter */
310                 proto_item_set_len(exportlist_item, offset - old_offset);
311         }
312         g_free(directory);
313
314         return offset;
315 }
316
317
318 /* RFC 1094, Page 26 */
319 /* RFC 1813, Page 113 */
320 static int
321 dissect_mount_export_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
322 {
323         offset = dissect_rpc_list(tvb, pinfo, tree, offset, dissect_exportlist);
324
325         return offset;
326 }
327
328
329 #define OFFS_MASK       32      /* offset of the "pc_mask" field */
330
331 #define PC_ERROR_ALL            0x0001
332 #define PC_ERROR_LINK_MAX       0x0002
333 #define PC_ERROR_MAX_CANON      0x0004
334 #define PC_ERROR_MAX_INPUT      0x0008
335 #define PC_ERROR_NAME_MAX       0x0010
336 #define PC_ERROR_PATH_MAX       0x0020
337 #define PC_ERROR_PIPE_BUF       0x0040
338 #define PC_CHOWN_RESTRICTED     0x0080
339 #define PC_NO_TRUNC             0x0100
340 #define PC_ERROR_VDISABLE       0x0200
341
342 static const true_false_string tos_error_all = {
343   "All info invalid",
344   "Some or all info valid"
345 };
346
347 static const true_false_string tos_error_link_max = {
348   "LINK_MAX invalid",
349   "LINK_MAX valid"
350 };
351
352 static const true_false_string tos_error_max_canon = {
353   "MAX_CANON invalid",
354   "MAX_CANON valid"
355 };
356
357 static const true_false_string tos_error_max_input = {
358   "MAX_INPUT invalid",
359   "MAX_INPUT valid"
360 };
361
362 static const true_false_string tos_error_name_max = {
363   "NAME_MAX invalid",
364   "NAME_MAX valid"
365 };
366
367 static const true_false_string tos_error_path_max = {
368   "PATH_MAX invalid",
369   "PATH_MAX valid"
370 };
371
372 static const true_false_string tos_error_pipe_buf = {
373   "PIPE_BUF invalid",
374   "PIPE_BUF valid"
375 };
376
377 static const true_false_string tos_chown_restricted = {
378   "Only a privileged user can change the ownership of a file",
379   "Users may give away their own files"
380 };
381
382 static const true_false_string tos_no_trunc = {
383   "File names that are too long will get an error",
384   "File names that are too long will be truncated"
385 };
386
387 static const true_false_string tos_error_vdisable = {
388   "VDISABLE invalid",
389   "VDISABLE valid"
390 };
391
392
393 static int
394 dissect_mount_pathconf_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
395 {
396         int saved_offset;
397         guint32 pc_mask;
398         proto_item *lock_item;
399         proto_tree *lock_tree;
400
401         saved_offset=offset;
402         /*
403          * Extract the mask first, so we know which other fields the
404          * server was able to return to us.
405          */
406         pc_mask = tvb_get_ntohl(tvb, offset+OFFS_MASK) & 0xffff;
407         if (!(pc_mask & (PC_ERROR_LINK_MAX|PC_ERROR_ALL))) {
408                 if (tree) {
409                         dissect_rpc_uint32(tvb,tree,hf_mount_pathconf_link_max,offset);
410                 }
411         }
412         offset += 4;
413
414         if (!(pc_mask & (PC_ERROR_MAX_CANON|PC_ERROR_ALL))) {
415                 if (tree) {
416                         proto_tree_add_item(tree,
417                                 hf_mount_pathconf_max_canon,tvb,offset+2,2,
418                                 tvb_get_ntohs(tvb,offset)&0xffff);
419                 }
420         }
421         offset += 4;
422
423         if (!(pc_mask & (PC_ERROR_MAX_INPUT|PC_ERROR_ALL))) {
424                 if (tree) {
425                         proto_tree_add_item(tree,
426                                 hf_mount_pathconf_max_input,tvb,offset+2,2,
427                                 tvb_get_ntohs(tvb,offset)&0xffff);
428                 }
429         }
430         offset += 4;
431
432         if (!(pc_mask & (PC_ERROR_NAME_MAX|PC_ERROR_ALL))) {
433                 if (tree) {
434                         proto_tree_add_item(tree,
435                                 hf_mount_pathconf_name_max,tvb,offset+2,2,
436                                 tvb_get_ntohs(tvb,offset)&0xffff);
437                 }
438         }
439         offset += 4;
440
441         if (!(pc_mask & (PC_ERROR_PATH_MAX|PC_ERROR_ALL))) {
442                 if (tree) {
443                         proto_tree_add_item(tree,
444                                 hf_mount_pathconf_path_max,tvb,offset+2,2,
445                                 tvb_get_ntohs(tvb,offset)&0xffff);
446                 }
447         }
448         offset += 4;
449
450         if (!(pc_mask & (PC_ERROR_PIPE_BUF|PC_ERROR_ALL))) {
451                 if (tree) {
452                         proto_tree_add_item(tree,
453                                 hf_mount_pathconf_pipe_buf,tvb,offset+2,2,
454                                 tvb_get_ntohs(tvb,offset)&0xffff);
455                 }
456         }
457         offset += 4;
458
459         offset += 4;    /* skip "pc_xxx" pad field */
460
461         if (!(pc_mask & (PC_ERROR_VDISABLE|PC_ERROR_ALL))) {
462                 if (tree) {
463                         proto_tree_add_item(tree,
464                                 hf_mount_pathconf_vdisable,tvb,offset+3,1,
465                                 tvb_get_ntohs(tvb,offset)&0xffff);
466                 }
467         }
468         offset += 4;
469
470
471         if (tree) {
472                 lock_item = proto_tree_add_item(tree, hf_mount_pathconf_mask, tvb,
473                                         offset+2, 2, FALSE);
474
475                 lock_tree = proto_item_add_subtree(lock_item, ett_mount_pathconf_mask);
476                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_all, tvb,
477                     offset + 2, 2, pc_mask);
478
479                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_link_max, tvb,
480                     offset + 2, 2, pc_mask);
481                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_max_canon, tvb,
482                     offset + 2, 2, pc_mask);
483                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_max_input, tvb,
484                     offset + 2, 2, pc_mask);
485                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_name_max, tvb,
486                     offset + 2, 2, pc_mask);
487                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_path_max, tvb,
488                     offset + 2, 2, pc_mask);
489                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_pipe_buf, tvb,
490                     offset + 2, 2, pc_mask);
491                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_chown_restricted, tvb,
492                     offset + 2, 2, pc_mask);
493                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_no_trunc, tvb,
494                     offset + 2, 2, pc_mask);
495                 proto_tree_add_boolean(lock_tree, hf_mount_pathconf_error_vdisable, tvb,
496                     offset + 2, 2, pc_mask);
497         }
498
499         offset += 8;
500         return offset;
501 }
502
503 /* RFC 1813, Page 107 */
504 static const value_string mount3_mountstat3[] =
505 {
506         {       0,      "OK" },
507         {       1,      "ERR_PERM" },
508         {       2,      "ERR_NOENT" },
509         {       5,      "ERR_IO" },
510         {       13,     "ERR_ACCESS" },
511         {       20,     "ERR_NOTDIR" },
512         {       22,     "ERR_INVAL" },
513         {       63,     "ERR_NAMETOOLONG" },
514         {       10004,  "ERR_NOTSUPP" },
515         {       10006,  "ERR_SERVERFAULT" },
516         {       0,      NULL }
517 };
518
519
520 /* RFC 1813, Page 107 */
521 static int
522 dissect_mountstat3(tvbuff_t *tvb, proto_tree *tree, int offset, int hfindex, guint32 *status)
523 {
524         guint32 mountstat3;
525
526         mountstat3 = tvb_get_ntohl(tvb, offset);
527
528         offset = dissect_rpc_uint32(tvb,tree,hfindex,offset);
529         *status = mountstat3;
530         return offset;
531 }
532
533 /* RFC 1831, Page 109 */
534 static int
535 dissect_mount3_mnt_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
536 {
537         guint32 status;
538         guint32 auth_flavors;
539         guint32 auth_flavor;
540         guint32 auth_flavor_i;
541
542         offset = dissect_mountstat3(tvb,tree,offset,hf_mount3_status,&status);
543
544         switch (status) {
545                 case 0:
546                         offset = dissect_nfs_fh3(tvb,offset,pinfo,tree,"fhandle");
547
548                         auth_flavors = tvb_get_ntohl(tvb, offset);
549                         proto_tree_add_uint(tree,hf_mount_flavors, tvb,
550                                 offset, 4, auth_flavors);
551                         offset += 4;
552                         for (auth_flavor_i = 0 ; auth_flavor_i < auth_flavors ; auth_flavor_i++) {
553                                 auth_flavor = tvb_get_ntohl(tvb, offset);
554                                 proto_tree_add_uint(tree,hf_mount_flavor, tvb,
555                                         offset, 4, auth_flavor);
556                                 offset += 4;
557                         }
558                 break;
559                 default:
560                         /* void */
561                 break;
562         }
563
564         return offset;
565 }
566
567 static int
568 dissect_sgi_exportlist(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
569 {
570         proto_item* exportlist_item = NULL;
571         proto_tree* exportlist_tree = NULL;
572         int old_offset = offset;
573         int options_offset;
574         char* directory, *options;
575
576         if (tree) {
577                 exportlist_item = proto_tree_add_item(tree, hf_mount_exportlist,
578                                         tvb, offset, -1, FALSE);
579                 if (exportlist_item)
580                         exportlist_tree = proto_item_add_subtree(exportlist_item,
581                                                 ett_mount_exportlist);
582         }
583
584         offset = dissect_rpc_string(tvb, exportlist_tree,
585                         hf_mount_exportlist_directory, offset, &directory);
586
587         offset = dissect_rpc_bool(tvb, exportlist_tree,
588                         hf_mount_has_options, offset);
589         options_offset = offset;
590
591         offset = dissect_rpc_string(tvb, exportlist_tree, hf_mount_options,
592                          offset, &options);
593
594         if (exportlist_item) {
595                 /* now we have a nicer string */
596                 proto_item_set_text(exportlist_item,
597                         "Export List Entry: %s %s", directory,
598                         options);
599                 /* now we know, that exportlist is shorter */
600                 proto_item_set_len(exportlist_item, offset - old_offset);
601         }
602         g_free(directory);
603         g_free(options);
604
605         return offset;
606 }
607
608 static int
609 dissect_mount_exportlist_reply(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
610 {
611         offset = dissect_rpc_list(tvb, pinfo, tree, offset, dissect_sgi_exportlist);
612
613         return offset;
614 }
615
616 #define ST_RDONLY       0x00000001
617 #define ST_NOSUID       0x00000002
618 #define ST_NOTRUNC      0x00000004
619 #define ST_NODEV        0x20000000
620 #define ST_GRPID        0x40000000
621 #define ST_LOCAL        0x80000000
622
623 static const true_false_string tos_st_rdonly = {
624         "Read-only file system",
625         "Read/Write file system"
626 };
627
628 static const true_false_string tos_st_nosuid = {
629         "Does not support setuid/setgid semantics",
630         "Supports setuid/setgid semantics"
631 };
632
633 static const true_false_string tos_st_notrunc = {
634         "Does not trunctate filenames longer than NAME_MAX",
635         "Truncates filenames longer than NAME_MAX"
636 };
637
638 static const true_false_string tos_st_nodev = {
639         "Disallows opening of device files",
640         "Allows opening of device files"
641 };
642
643 static const true_false_string tos_st_grpid = {
644         "Group ID assigned from directory",
645         "Group ID not assigned from directory"
646 };
647
648 static const true_false_string tos_st_local = {
649         "File system is local",
650         "File system is not local"
651 };
652
653 static int
654 dissect_mount_statvfs_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
655 {
656         proto_item *flag_item;
657         proto_tree *flag_tree;
658         guint32 statvfs_flags;
659
660         statvfs_flags = tvb_get_ntohl(tvb, offset+52);
661         if (tree) {
662                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_bsize, offset);
663         }
664         offset += 4;
665         if (tree) {
666                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_frsize, offset);
667         }
668         offset += 4;
669         if (tree) {
670                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_blocks, offset);
671         }
672         offset += 4;
673         if (tree) {
674                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_bfree, offset);
675         }
676         offset += 4;
677         if (tree) {
678                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_bavail, offset);
679         }
680         offset += 4;
681         if (tree) {
682                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_files, offset);
683         }
684         offset += 4;
685         if (tree) {
686                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_ffree, offset);
687         }
688         offset += 4;
689         if (tree) {
690                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_favail, offset);
691         }
692         offset += 4;
693         if (tree) {
694                 dissect_rpc_bytes(tvb, tree, hf_mount_statvfs_basetype, offset,
695                         16, TRUE, NULL);
696         }
697         offset += 16;
698         if (tree) {
699                 dissect_rpc_bytes(tvb, tree, hf_mount_statvfs_fstr, offset,
700                         32, FALSE, NULL);
701         }
702         offset += 32;
703         if (tree) {
704                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_fsid, offset);
705         }
706         offset += 4;
707
708         if (tree) {
709                 flag_item = proto_tree_add_item(tree, hf_mount_statvfs_flag,
710                                 tvb, offset, 4, FALSE);
711                 if (flag_item) {
712                         flag_tree = proto_item_add_subtree(flag_item,
713                                         ett_mount_statvfs_flag);
714                         proto_tree_add_boolean(flag_tree,
715                                 hf_mount_statvfs_flag_rdonly, tvb, offset, 4,
716                                 statvfs_flags);
717                         proto_tree_add_boolean(flag_tree,
718                                 hf_mount_statvfs_flag_nosuid, tvb, offset, 4,
719                                 statvfs_flags);
720                         proto_tree_add_boolean(flag_tree,
721                                 hf_mount_statvfs_flag_notrunc, tvb, offset, 4,
722                                 statvfs_flags);
723                         proto_tree_add_boolean(flag_tree,
724                                 hf_mount_statvfs_flag_nodev, tvb, offset, 4,
725                                 statvfs_flags);
726                         proto_tree_add_boolean(flag_tree,
727                                 hf_mount_statvfs_flag_grpid, tvb, offset, 4,
728                                 statvfs_flags);
729                         proto_tree_add_boolean(flag_tree,
730                                 hf_mount_statvfs_flag_local, tvb, offset, 4,
731                                 statvfs_flags);
732                 }
733         }
734
735         offset += 4;
736         if (tree) {
737                 dissect_rpc_uint32(tvb, tree, hf_mount_statvfs_namemax, offset);
738         }
739         offset += 4;
740
741         return offset;
742 }
743
744 /* proc number, "proc name", dissect_request, dissect_reply */
745 /* NULL as function pointer means: type of arguments is "void". */
746
747 /* Mount protocol version 1, RFC 1094 */
748 static const vsff mount1_proc[] = {
749     { 0, "NULL", NULL, NULL },
750     { MOUNTPROC_MNT,        "MNT",
751                 dissect_mount_dirpath_call, dissect_mount1_mnt_reply },
752     { MOUNTPROC_DUMP,       "DUMP",
753                 NULL, dissect_mount_dump_reply },
754     { MOUNTPROC_UMNT,      "UMNT",
755                 dissect_mount_dirpath_call, NULL },
756     { MOUNTPROC_UMNTALL,   "UMNTALL",
757                 NULL, NULL },
758     { MOUNTPROC_EXPORT,    "EXPORT",
759                 NULL, dissect_mount_export_reply },
760     { MOUNTPROC_EXPORTALL, "EXPORTALL",
761                 NULL, dissect_mount_export_reply },
762     { 0, NULL, NULL, NULL }
763 };
764 static const value_string mount1_proc_vals[] = {
765     { 0, "NULL" },
766     { MOUNTPROC_MNT,       "MNT" },
767     { MOUNTPROC_DUMP,      "DUMP" },
768     { MOUNTPROC_UMNT,      "UMNT" },
769     { MOUNTPROC_UMNTALL,   "UMNTALL" },
770     { MOUNTPROC_EXPORT,    "EXPORT" },
771     { MOUNTPROC_EXPORTALL, "EXPORTALL" },
772     { 0, NULL }
773 };
774 /* end of mount version 1 */
775
776
777 /* Mount protocol version 2, private communication from somebody at Sun;
778    mount V2 is V1 plus MOUNTPROC_PATHCONF to fetch information for the
779    POSIX "pathconf()" call. */
780 static const vsff mount2_proc[] = {
781     { 0, "NULL", NULL, NULL },
782     { MOUNTPROC_MNT,        "MNT",
783                 dissect_mount_dirpath_call, dissect_mount1_mnt_reply },
784     { MOUNTPROC_DUMP,       "DUMP",
785                 NULL, dissect_mount_dump_reply },
786     { MOUNTPROC_UMNT,      "UMNT",
787                 dissect_mount_dirpath_call, NULL },
788     { MOUNTPROC_UMNTALL,   "UMNTALL",
789                 NULL, NULL },
790     { MOUNTPROC_EXPORT,    "EXPORT",
791                 NULL, dissect_mount_export_reply },
792     { MOUNTPROC_EXPORTALL, "EXPORTALL",
793                 NULL, dissect_mount_export_reply },
794     { MOUNTPROC_PATHCONF,  "PATHCONF",
795                 dissect_mount_dirpath_call, dissect_mount_pathconf_reply },
796     { 0, NULL, NULL, NULL }
797 };
798 static const value_string mount2_proc_vals[] = {
799     { 0, "NULL" },
800     { MOUNTPROC_MNT,       "MNT" },
801     { MOUNTPROC_DUMP,      "DUMP" },
802     { MOUNTPROC_UMNT,      "UMNT" },
803     { MOUNTPROC_UMNTALL,   "UMNTALL" },
804     { MOUNTPROC_EXPORT,    "EXPORT" },
805     { MOUNTPROC_EXPORTALL, "EXPORTALL" },
806     { MOUNTPROC_PATHCONF,  "PATHCONF" },
807     { 0, NULL }
808 };
809 /* end of mount version 2 */
810
811
812 /* Mount protocol version 3, RFC 1813 */
813 static const vsff mount3_proc[] = {
814         { 0, "NULL", NULL, NULL },
815         { MOUNTPROC_MNT, "MNT",
816                 dissect_mount_dirpath_call, dissect_mount3_mnt_reply },
817         { MOUNTPROC_DUMP, "DUMP",
818                 NULL, dissect_mount_dump_reply },
819         { MOUNTPROC_UMNT, "UMNT",
820                 dissect_mount_dirpath_call, NULL },
821         { MOUNTPROC_UMNTALL, "UMNTALL",
822                 NULL, NULL },
823         { MOUNTPROC_EXPORT, "EXPORT",
824                 NULL, dissect_mount_export_reply },
825         { 0, NULL, NULL, NULL }
826 };
827 static const value_string mount3_proc_vals[] = {
828         { 0, "NULL" },
829         { MOUNTPROC_MNT, "MNT" },
830         { MOUNTPROC_DUMP, "DUMP" },
831         { MOUNTPROC_UMNT, "UMNT" },
832         { MOUNTPROC_UMNTALL, "UMNTALL" },
833         { MOUNTPROC_EXPORT, "EXPORT" },
834         { 0, NULL }
835 };
836 /* end of Mount protocol version 3 */
837
838 /* SGI mount protocol version 1; actually the same as v1 plus
839    MOUNTPROC_EXPORTLIST and MOUNTPROC_STATVFS */
840
841 static const vsff sgi_mount1_proc[] = {
842     { 0, "NULL", NULL, NULL },
843     { MOUNTPROC_MNT,        "MNT",      
844                 dissect_mount_dirpath_call, dissect_mount1_mnt_reply },
845     { MOUNTPROC_DUMP,       "DUMP",
846                 NULL, dissect_mount_dump_reply },
847     { MOUNTPROC_UMNT,      "UMNT",        
848                 dissect_mount_dirpath_call, NULL },
849     { MOUNTPROC_UMNTALL,   "UMNTALL",
850                 NULL, NULL },
851     { MOUNTPROC_EXPORT,    "EXPORT",
852                 NULL, dissect_mount_export_reply },
853     { MOUNTPROC_EXPORTALL, "EXPORTALL",
854                 NULL, dissect_mount_export_reply },
855     { MOUNTPROC_EXPORTLIST,"EXPORTLIST",
856                 NULL, dissect_mount_exportlist_reply },
857     { MOUNTPROC_STATVFS,   "STATVFS",
858                 dissect_mount_dirpath_call, dissect_mount_statvfs_reply },
859     { 0, NULL, NULL, NULL }
860 };
861 static const value_string sgi_mount1_proc_vals[] = {
862     { 0, "NULL" },
863     { MOUNTPROC_MNT,        "MNT" },
864     { MOUNTPROC_DUMP,       "DUMP" },
865     { MOUNTPROC_UMNT,       "UMNT" },
866     { MOUNTPROC_UMNTALL,    "UMNTALL" },
867     { MOUNTPROC_EXPORT,     "EXPORT" },
868     { MOUNTPROC_EXPORTALL,  "EXPORTALL" },
869     { MOUNTPROC_EXPORTLIST, "EXPORTLIST" },
870     { MOUNTPROC_STATVFS,    "STATVFS" },
871     { 0, NULL }
872 };
873 /* end of SGI mount protocol version 1 */
874
875 void
876 proto_register_mount(void)
877 {
878         static hf_register_info hf[] = {
879                 { &hf_mount_procedure_v1, {
880                         "V1 Procedure", "mount.procedure_v1", FT_UINT32, BASE_DEC,
881                         VALS(mount1_proc_vals), 0, "V1 Procedure", HFILL }},
882                 { &hf_mount_procedure_v2, {
883                         "V2 Procedure", "mount.procedure_v2", FT_UINT32, BASE_DEC,
884                         VALS(mount2_proc_vals), 0, "V2 Procedure", HFILL }},
885                 { &hf_mount_procedure_v3, {
886                         "V3 Procedure", "mount.procedure_v3", FT_UINT32, BASE_DEC,
887                         VALS(mount3_proc_vals), 0, "V3 Procedure", HFILL }},
888                 { &hf_sgi_mount_procedure_v1, {
889                         "SGI V1 procedure", "mount.procedure_sgi_v1", FT_UINT32, BASE_DEC,
890                         VALS(sgi_mount1_proc_vals), 0, "SGI V1 Procedure", HFILL }},
891                 { &hf_mount_path, {
892                         "Path", "mount.path", FT_STRING, BASE_DEC,
893                         NULL, 0, "Path", HFILL }},
894                 { &hf_mount3_status, {
895                         "Status", "mount.status", FT_UINT32, BASE_DEC,
896                         VALS(mount3_mountstat3), 0, "Status", HFILL }},
897                 { &hf_mount_mountlist_hostname, {
898                         "Hostname", "mount.dump.hostname", FT_STRING, BASE_DEC,
899                         NULL, 0, "Hostname", HFILL }},
900                 { &hf_mount_mountlist_directory, {
901                         "Directory", "mount.dump.directory", FT_STRING, BASE_DEC,
902                         NULL, 0, "Directory", HFILL }},
903                 { &hf_mount_mountlist, {
904                         "Mount List Entry", "mount.dump.entry", FT_NONE, 0,
905                         NULL, 0, "Mount List Entry", HFILL }},
906                 { &hf_mount_groups_group, {
907                         "Group", "mount.export.group", FT_STRING, BASE_DEC,
908                         NULL, 0, "Group", HFILL }},
909                 { &hf_mount_groups, {
910                         "Groups", "mount.export.groups", FT_NONE, 0,
911                         NULL, 0, "Groups", HFILL }},
912                 { &hf_mount_has_options, {
913                         "Has options", "mount.export.has_options", FT_UINT32,
914                          BASE_DEC, NULL, 0, "Has options", HFILL }},
915                 { &hf_mount_options, {
916                         "Options", "mount.export.options", FT_STRING, BASE_DEC,
917                         NULL, 0, "Options", HFILL }},
918                 { &hf_mount_exportlist_directory, {
919                         "Directory", "mount.export.directory", FT_STRING, BASE_DEC,
920                         NULL, 0, "Directory", HFILL }},
921                 { &hf_mount_exportlist, {
922                         "Export List Entry", "mount.export.entry", FT_NONE, 0,
923                         NULL, 0, "Export List Entry", HFILL }},
924                 { &hf_mount_pathconf_link_max, {
925                         "Maximum number of links to a file", "mount.pathconf.link_max",
926                         FT_UINT32, BASE_DEC,
927                         NULL, 0, "Maximum number of links allowed to a file", HFILL }},
928                 { &hf_mount_pathconf_max_canon, {
929                         "Maximum terminal input line length", "mount.pathconf.max_canon",
930                         FT_UINT16, BASE_DEC,
931                         NULL, 0, "Max tty input line length", HFILL }},
932                 { &hf_mount_pathconf_max_input, {
933                         "Terminal input buffer size", "mount.pathconf.max_input",
934                         FT_UINT16, BASE_DEC,
935                         NULL, 0, "Terminal input buffer size", HFILL }},
936                 { &hf_mount_pathconf_name_max, {
937                         "Maximum file name length", "mount.pathconf.name_max",
938                         FT_UINT16, BASE_DEC,
939                         NULL, 0, "Maximum file name length", HFILL }},
940                 { &hf_mount_pathconf_path_max, {
941                         "Maximum path name length", "mount.pathconf.path_max",
942                         FT_UINT16, BASE_DEC,
943                         NULL, 0, "Maximum path name length", HFILL }},
944                 { &hf_mount_pathconf_pipe_buf, {
945                         "Pipe buffer size", "mount.pathconf.pipe_buf",
946                         FT_UINT16, BASE_DEC,
947                         NULL, 0, "Maximum amount of data that can be written atomically to a pipe", HFILL }},
948                 { &hf_mount_pathconf_vdisable, {
949                         "VDISABLE character", "mount.pathconf.vdisable_char",
950                         FT_UINT8, BASE_HEX,
951                         NULL, 0, "Character value to disable a terminal special character", HFILL }},
952                 { &hf_mount_pathconf_mask, {
953                         "Reply error/status bits", "mount.pathconf.mask",
954                         FT_UINT16, BASE_HEX,
955                         NULL, 0, "Bit mask with error and status bits", HFILL }},
956                 { &hf_mount_pathconf_error_all, {
957                         "ERROR_ALL",    "mount.pathconf.mask.error_all",
958                         FT_BOOLEAN, 16, TFS(&tos_error_all),
959                         PC_ERROR_ALL, "", HFILL }},
960                 { &hf_mount_pathconf_error_link_max, {
961                         "ERROR_LINK_MAX", "mount.pathconf.mask.error_link_max",
962                         FT_BOOLEAN, 16, TFS(&tos_error_link_max),
963                         PC_ERROR_LINK_MAX, "", HFILL }},
964                 { &hf_mount_pathconf_error_max_canon, {
965                         "ERROR_MAX_CANON", "mount.pathconf.mask.error_max_canon",
966                         FT_BOOLEAN, 16, TFS(&tos_error_max_canon),
967                         PC_ERROR_MAX_CANON, "", HFILL }},
968                 { &hf_mount_pathconf_error_max_input, {
969                         "ERROR_MAX_INPUT", "mount.pathconf.mask.error_max_input",
970                         FT_BOOLEAN, 16, TFS(&tos_error_max_input),
971                         PC_ERROR_MAX_INPUT, "", HFILL }},
972                 { &hf_mount_pathconf_error_name_max, {
973                         "ERROR_NAME_MAX", "mount.pathconf.mask.error_name_max",
974                         FT_BOOLEAN, 16, TFS(&tos_error_name_max),
975                         PC_ERROR_NAME_MAX, "", HFILL }},
976                 { &hf_mount_pathconf_error_path_max, {
977                         "ERROR_PATH_MAX", "mount.pathconf.mask.error_path_max",
978                         FT_BOOLEAN, 16, TFS(&tos_error_path_max),
979                         PC_ERROR_PATH_MAX, "", HFILL }},
980                 { &hf_mount_pathconf_error_pipe_buf, {
981                         "ERROR_PIPE_BUF", "mount.pathconf.mask.error_pipe_buf",
982                         FT_BOOLEAN, 16, TFS(&tos_error_pipe_buf),
983                         PC_ERROR_PIPE_BUF, "", HFILL }},
984                 { &hf_mount_pathconf_chown_restricted, {
985                         "CHOWN_RESTRICTED", "mount.pathconf.mask.chown_restricted",
986                         FT_BOOLEAN, 16, TFS(&tos_chown_restricted),
987                         PC_CHOWN_RESTRICTED, "", HFILL }},
988                 { &hf_mount_pathconf_no_trunc, {
989                         "NO_TRUNC", "mount.pathconf.mask.no_trunc",
990                         FT_BOOLEAN, 16, TFS(&tos_no_trunc),
991                         PC_NO_TRUNC, "", HFILL }},
992                 { &hf_mount_pathconf_error_vdisable, {
993                         "ERROR_VDISABLE", "mount.pathconf.mask.error_vdisable",
994                         FT_BOOLEAN, 16, TFS(&tos_error_vdisable),
995                         PC_ERROR_VDISABLE, "", HFILL }},
996                 { &hf_mount_statvfs_bsize, {
997                         "Block size", "mount.statvfs.f_bsize",
998                         FT_UINT32, BASE_DEC, NULL, 0,
999                         "File system block size", HFILL }},
1000                 { &hf_mount_statvfs_frsize, {
1001                         "Fragment size", "mount.statvfs.f_frsize",
1002                         FT_UINT32, BASE_DEC, NULL, 0,
1003                         "File system fragment size", HFILL }},
1004                 { &hf_mount_statvfs_blocks, {
1005                         "Blocks", "mount.statvfs.f_blocks",
1006                         FT_UINT32, BASE_DEC, NULL, 0,
1007                         "Total fragment sized blocks", HFILL }},
1008                 { &hf_mount_statvfs_bfree, {
1009                         "Blocks Free", "mount.statvfs.f_bfree",
1010                         FT_UINT32, BASE_DEC, NULL, 0,
1011                         "Free fragment sized blocks", HFILL }},
1012                 { &hf_mount_statvfs_bavail, {
1013                         "Blocks Available", "mount.statvfs.f_bavail",
1014                         FT_UINT32, BASE_DEC, NULL, 0,
1015                         "Available fragment sized blocks", HFILL }},
1016                 { &hf_mount_statvfs_files, {
1017                         "Files", "mount.statvfs.f_files",
1018                         FT_UINT32, BASE_DEC, NULL, 0,
1019                         "Total files/inodes", HFILL }},
1020                 { &hf_mount_statvfs_ffree, {
1021                         "Files Free", "mount.statvfs.f_ffree",
1022                         FT_UINT32, BASE_DEC, NULL, 0,
1023                         "Free files/inodes", HFILL }},
1024                 { &hf_mount_statvfs_favail, {
1025                         "Files Available", "mount.statvfs.f_favail",
1026                         FT_UINT32, BASE_DEC, NULL, 0,
1027                         "Available files/inodes",  HFILL }},
1028                 { &hf_mount_statvfs_fsid, {
1029                         "File system ID", "mount.statvfs.f_fsid",
1030                         FT_UINT32, BASE_DEC, NULL, 0,
1031                         "File system identifier", HFILL }},
1032                 { &hf_mount_statvfs_basetype, {
1033                         "Type", "mount.statvfs.f_basetype",
1034                         FT_STRING, BASE_DEC, NULL, 0,
1035                         "File system type", HFILL }},
1036                 { &hf_mount_statvfs_flag, {
1037                         "Flags", "mount.statvfs.f_flag",
1038                         FT_UINT32, BASE_HEX, NULL, 0,
1039                         "Flags bit-mask", HFILL }},
1040                 { &hf_mount_statvfs_flag_rdonly, {
1041                         "ST_RDONLY", "mount.statvfs.f_flag.st_rdonly",
1042                         FT_BOOLEAN, 32, TFS(&tos_st_rdonly), ST_RDONLY,
1043                         "", HFILL }},
1044                 { &hf_mount_statvfs_flag_nosuid, {
1045                         "ST_NOSUID", "mount.statvfs.f_flag.st_nosuid",
1046                         FT_BOOLEAN, 32, TFS(&tos_st_nosuid), ST_NOSUID,
1047                         "", HFILL }},
1048                 { &hf_mount_statvfs_flag_notrunc, {
1049                         "ST_NOTRUNC", "mount.statvfs.f_flag.st_notrunc",
1050                         FT_BOOLEAN, 32, TFS(&tos_st_notrunc), ST_NOTRUNC,
1051                         "", HFILL }},
1052                 { &hf_mount_statvfs_flag_nodev, {
1053                         "ST_NODEV", "mount.statvfs.f_flag.st_nodev",
1054                          FT_BOOLEAN, 32, TFS(&tos_st_nodev), ST_NODEV,
1055                         "", HFILL }},
1056                 { &hf_mount_statvfs_flag_grpid, {
1057                         "ST_GRPID", "mount.statvfs.f_flag.st_grpid",
1058                         FT_BOOLEAN, 32, TFS(&tos_st_grpid), ST_GRPID,
1059                         "", HFILL }},
1060                 { &hf_mount_statvfs_flag_local, {
1061                         "ST_LOCAL", "mount.statvfs.f_flag.st_local",
1062                         FT_BOOLEAN, 32, TFS(&tos_st_local), ST_LOCAL,
1063                         "", HFILL }},
1064                 { &hf_mount_statvfs_namemax, {
1065                         "Maximum file name length", "mount.statvfs.f_namemax",
1066                         FT_UINT32, BASE_DEC, NULL, 0,
1067                         "Maximum file name length", HFILL }},
1068                 { &hf_mount_statvfs_fstr, {
1069                         "File system specific string", "mount.statvfs.f_fstr",
1070                         FT_BYTES, BASE_HEX, NULL, 0,
1071                         "File system specific string", HFILL }},
1072                 { &hf_mount_flavors, {
1073                         "Flavors", "mount.flavors", FT_UINT32, BASE_DEC,
1074                         NULL, 0, "Flavors", HFILL }},
1075                 { &hf_mount_flavor, {
1076                         "Flavor", "mount.flavor", FT_UINT32, BASE_DEC,
1077                         VALS(rpc_auth_flavor), 0, "Flavor", HFILL }},
1078         };
1079         static gint *ett[] = {
1080                 &ett_mount,
1081                 &ett_mount_mountlist,
1082                 &ett_mount_groups,
1083                 &ett_mount_exportlist,
1084                 &ett_mount_pathconf_mask,
1085                 &ett_mount_statvfs_flag,
1086         };
1087
1088         proto_mount = proto_register_protocol("Mount Service", "MOUNT",
1089             "mount");
1090         proto_sgi_mount = proto_register_protocol("SGI Mount Service",
1091             "SGI MOUNT", "sgimount");
1092         proto_register_field_array(proto_mount, hf, array_length(hf));
1093         proto_register_subtree_array(ett, array_length(ett));
1094 }
1095
1096 void
1097 proto_reg_handoff_mount(void)
1098 {
1099         /* Register the protocol as RPC */
1100         rpc_init_prog(proto_mount, MOUNT_PROGRAM, ett_mount);
1101         rpc_init_prog(proto_sgi_mount, SGI_MOUNT_PROGRAM, ett_mount);
1102         /* Register the procedure tables */
1103         rpc_init_proc_table(MOUNT_PROGRAM, 1, mount1_proc, hf_mount_procedure_v1);
1104         rpc_init_proc_table(MOUNT_PROGRAM, 2, mount2_proc, hf_mount_procedure_v2);
1105         rpc_init_proc_table(MOUNT_PROGRAM, 3, mount3_proc, hf_mount_procedure_v3);
1106         rpc_init_proc_table(SGI_MOUNT_PROGRAM, 1, sgi_mount1_proc, hf_sgi_mount_procedure_v1);
1107 }