rxperf: Move into the tools directory
[openafs.git] / src / tools / dumpscan / parsevnode.c
1 /*
2  * CMUCS AFStools
3  * dumpscan - routines for scanning and manipulating AFS volume dumps
4  *
5  * Copyright (c) 1998 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie Mellon
26  * the rights to redistribute these changes.
27  */
28
29 /* parsevnode.c - Parse a VNode */
30
31 #include <sys/types.h>
32 #include <netinet/in.h>
33 #include <errno.h>
34
35 #include "dumpscan.h"
36 #include "dumpscan_errs.h"
37 #include "dumpfmt.h"
38 #include "internal.h"
39
40 #include <afs/acl.h>
41 #include <afs/prs_fs.h>
42
43 static afs_uint32 LastGoodVNode = 0;
44 static afs_uint32 store_vnode(XFILE *, unsigned char *, tagged_field *,
45                               afs_uint32, tag_parse_info *, void *, void *);
46 static afs_uint32 parse_acl(XFILE *, unsigned char *, tagged_field *,
47                             afs_uint32, tag_parse_info *, void *, void *);
48 static afs_uint32 parse_vdata(XFILE *, unsigned char *, tagged_field *,
49                               afs_uint32, tag_parse_info *, void *, void *);
50
51 /** Field list for vnodes **/
52 static tagged_field vnode_fields[] = {
53     {VTAG_TYPE, DKIND_BYTE, " VNode type:   ", store_vnode, 0, 0},
54     {VTAG_NLINKS, DKIND_INT16, " Link count:   ", store_vnode, 0, 0},
55     {VTAG_DVERS, DKIND_INT32, " Version:      ", store_vnode, 0, 0},
56     {VTAG_CLIENT_DATE, DKIND_TIME, " Server Date:  ", store_vnode, 0, 0},
57     {VTAG_AUTHOR, DKIND_INT32, " Author:       ", store_vnode, 0, 0},
58     {VTAG_OWNER, DKIND_INT32, " Owner:        ", store_vnode, 0, 0},
59     {VTAG_GROUP, DKIND_INT32, " Group:        ", store_vnode, 0, 0},
60     {VTAG_MODE, DKIND_INT16, " UNIX mode:    ", store_vnode, 0, 0},
61     {VTAG_PARENT, DKIND_INT32, " Parent:       ", store_vnode, 0, 0},
62     {VTAG_SERVER_DATE, DKIND_TIME, " Client Date:  ", store_vnode, 0, 0},
63     {VTAG_ACL, DKIND_SPECIAL, " xxxxxxxx ACL: ", parse_acl, 0, 0},
64     {VTAG_DATA, DKIND_SPECIAL, " Contents:     ", parse_vdata, 0, 0},
65     {0, 0, 0, 0, 0, 0}
66 };
67
68
69 static afs_uint32
70 resync_vnode(XFILE * X, dump_parser * p, afs_vnode * v, int start, int limit)
71 {
72     dt_uint64 where, expected_where;
73     afs_uint32 r;
74     int i;
75
76     if ((r = xftell(X, &expected_where)))
77         return r;
78     cp64(where, expected_where);
79
80     r = match_next_vnode(X, p, &where, v->vnode);
81     if (r && r != DSERR_FMT)
82         return r;
83     if (r)
84         for (i = -start; i < limit; i++) {
85             add64_32(where, expected_where, i);
86             r = match_next_vnode(X, p, &where, v->vnode);
87             if (!r)
88                 break;
89             if (r != DSERR_FMT)
90                 return r;
91         }
92     if (r) {
93         if (p->cb_error)
94             (p->cb_error) (r, 1, p->err_refcon,
95                            "Unable to resync after vnode %d [%s = 0x%s]",
96                            v->vnode, decimate_int64(&expected_where, 0),
97                            hexify_int64(&expected_where, 0));
98         return r;
99     }
100     if (ne64(where, expected_where) && p->cb_error) {
101         (p->cb_error) (DSERR_FMT, 0, p->err_refcon,
102                        "Vnode after %d not in expected location", v->vnode);
103         (p->cb_error) (DSERR_FMT, 0, p->err_refcon,
104                        "Expected location: %s = 0x%s",
105                        decimate_int64(&expected_where, 0),
106                        hexify_int64(&expected_where, 0));
107         (p->cb_error) (DSERR_FMT, 0, p->err_refcon,
108                        "Actual location: %s = 0x%s", decimate_int64(&where,
109                                                                     0),
110                        hexify_int64(&where, 0));
111     }
112     return xfseek(X, &where);
113 }
114
115
116 /* Parse a VNode, including any tagged attributes and data, and call the
117  * appropriate callback, if one is defined.
118  */
119 afs_uint32
120 parse_vnode(XFILE * X, unsigned char *tag, tagged_field * field,
121             afs_uint32 value, tag_parse_info * pi, void *g_refcon,
122             void *l_refcon)
123 {
124     dump_parser *p = (dump_parser *) g_refcon;
125     afs_uint32(*cb) (afs_vnode *, XFILE *, void *);
126     dt_uint64 where, offset2k;
127     afs_vnode v;
128     afs_uint32 r;
129
130
131     if ((r = xftell(X, &where)))
132         return r;
133     memset(&v, 0, sizeof(v));
134     sub64_32(v.offset, where, 1);
135     if ((r = ReadInt32(X, &v.vnode)))
136         return r;
137     if ((r = ReadInt32(X, &v.vuniq)))
138         return r;
139
140     mk64(offset2k, 0, 2048);
141     if (!LastGoodVNode
142         || ((p->flags & DSFLAG_SEEK) && v.vnode == 1
143             && lt64(v.offset, offset2k)))
144         LastGoodVNode = -1;
145
146     if (p->print_flags & DSPRINT_ITEM) {
147         printf("%s %d/%d [%s = 0x%s]\n", field->label, v.vnode, v.vuniq,
148                decimate_int64(&where, 0), hexify_int64(&where, 0));
149     }
150
151     r = ParseTaggedData(X, vnode_fields, tag, pi, g_refcon, (void *)&v);
152
153     /* Try to resync, if requested */
154     if (!r && (p->repair_flags & DSFIX_VFSYNC)) {
155         afs_uint32 drop;
156         dt_uint64 xwhere;
157
158         if ((r = xftell(X, &where)))
159             return r;
160         sub64_32(xwhere, where, 1);
161
162         /* Are we at the start of a valid vnode (or dump end)? */
163         r = match_next_vnode(X, p, &xwhere, v.vnode);
164         if (r && r != DSERR_FMT)
165             return r;
166         if (r) {                /* Nope. */
167             /* Was _this_ a valid vnode?  If so, we can keep it and search for
168              * the next one.  Otherwise, we throw it out, and start the search
169              * at the starting point of this vnode.
170              */
171             drop = r = match_next_vnode(X, p, &v.offset, LastGoodVNode);
172             if (r && r != DSERR_FMT)
173                 return r;
174             if (!r) {
175                 add64_32(where, v.offset, 1);
176                 if ((r = xfseek(X, &v.offset)))
177                     return r;
178             } else {
179                 if ((r = xfseek(X, &xwhere)))
180                     return r;
181             }
182             if ((r = resync_vnode(X, p, &v, 0, 1024)))
183                 return r;
184             if ((r = ReadByte(X, tag)))
185                 return r;
186             if (drop) {
187                 if (p->cb_error)
188                     (p->cb_error) (DSERR_FMT, 0, p->err_refcon,
189                                    "Dropping vnode %d", v.vnode);
190                 return 0;
191             }
192         } else {
193             if ((r = xfseek(X, &where)))
194                 return r;
195         }
196     }
197     LastGoodVNode = v.vnode;
198
199     if (!r) {
200         if (v.field_mask & F_VNODE_TYPE)
201             switch (v.type) {
202             case vFile:
203                 cb = p->cb_vnode_file;
204                 break;
205             case vDirectory:
206                 cb = p->cb_vnode_dir;
207                 break;
208             case vSymlink:
209                 cb = p->cb_vnode_link;
210                 break;
211             default:
212                 cb = p->cb_vnode_wierd;
213                 break;
214         } else
215             cb = p->cb_vnode_empty;
216         if (cb) {
217             dt_uint64 where;
218
219             if ((r = xftell(X, &where)))
220                 return r;
221             r = (cb) (&v, X, p->refcon);
222             if (p->flags & DSFLAG_SEEK) {
223                 if (!r)
224                     r = xfseek(X, &where);
225                 else
226                     xfseek(X, &where);
227             }
228         }
229     }
230     return r;
231 }
232
233
234 /* Store data in a vnode */
235 static afs_uint32
236 store_vnode(XFILE * X, unsigned char *tag, tagged_field * field,
237             afs_uint32 value, tag_parse_info * pi, void *g_refcon,
238             void *l_refcon)
239 {
240     dump_parser *p = (dump_parser *) g_refcon;
241     afs_vnode *v = (afs_vnode *) l_refcon;
242     time_t when;
243     afs_uint32 r = 0;
244
245     switch (field->tag) {
246     case VTAG_TYPE:
247         v->field_mask |= F_VNODE_TYPE;
248         v->type = value;
249         if (p->print_flags & DSPRINT_VNODE) {
250             switch (value) {
251             case vFile:
252                 printf("%sFile (%d)\n", field->label, value);
253                 break;
254             case vDirectory:
255                 printf("%sDirectory (%d)\n", field->label, value);
256                 break;
257             case vSymlink:
258                 printf("%sSymbolic Link (%d)\n", field->label, value);
259                 break;
260             default:
261                 printf("%s??? (%d)\n", field->label, value);
262             }
263             return r;
264         }
265         break;
266
267     case VTAG_NLINKS:
268         v->field_mask |= F_VNODE_NLINKS;
269         v->nlinks = value;
270         break;
271
272     case VTAG_DVERS:
273         v->field_mask |= F_VNODE_DVERS;
274         v->datavers = value;
275         break;
276
277     case VTAG_CLIENT_DATE:
278         v->field_mask |= F_VNODE_CDATE;
279         v->client_date = value;
280         break;
281
282     case VTAG_SERVER_DATE:
283         v->field_mask |= F_VNODE_SDATE;
284         v->server_date = value;
285         break;
286
287     case VTAG_AUTHOR:
288         v->field_mask |= F_VNODE_AUTHOR;
289         v->author = value;
290         break;
291
292     case VTAG_OWNER:
293         v->field_mask |= F_VNODE_OWNER;
294         v->owner = value;
295         break;
296
297     case VTAG_GROUP:
298         v->field_mask |= F_VNODE_GROUP;
299         v->group = value;
300         break;
301
302     case VTAG_MODE:
303         v->field_mask |= F_VNODE_MODE;
304         v->mode = value;
305         break;
306
307     case VTAG_PARENT:
308         v->field_mask |= F_VNODE_PARENT;
309         v->parent = value;
310         break;
311     }
312
313     if (p->print_flags & DSPRINT_VNODE)
314         switch (field->kind) {
315         case DKIND_BYTE:
316         case DKIND_INT16:
317         case DKIND_INT32:
318             printf("%s%d\n", field->label, value);
319             break;
320         case DKIND_HEX8:
321             printf("%s0x%02x\n", field->label, value);
322             break;
323         case DKIND_HEX16:
324             printf("%s0x%04x\n", field->label, value);
325             break;
326         case DKIND_HEX32:
327             printf("%s0x%08x\n", field->label, value);
328             break;
329         case DKIND_CHAR:
330             printf("%s%c\n", field->label, value);
331             break;
332         case DKIND_STRING:
333             printf("%s%s\n", field->label, tag);
334             break;
335         case DKIND_FLAG:
336             printf("%s%s\n", field->label, value ? "true" : "false");
337             break;
338         case DKIND_TIME:
339             when = value;
340             printf("%s%s", field->label, ctime(&when));
341             break;
342         }
343     return r;
344 }
345
346
347 static char *
348 rights2str(afs_uint32 rights)
349 {
350     static char str[16];
351     char *p = str;
352
353     if (rights & PRSFS_READ)
354         *p++ = 'r';
355     if (rights & PRSFS_LOOKUP)
356         *p++ = 'l';
357     if (rights & PRSFS_INSERT)
358         *p++ = 'i';
359     if (rights & PRSFS_DELETE)
360         *p++ = 'd';
361     if (rights & PRSFS_WRITE)
362         *p++ = 'w';
363     if (rights & PRSFS_LOCK)
364         *p++ = 'k';
365     if (rights & PRSFS_ADMINISTER)
366         *p++ = 'a';
367     if (rights & PRSFS_USR0)
368         *p++ = 'A';
369     if (rights & PRSFS_USR1)
370         *p++ = 'B';
371     if (rights & PRSFS_USR2)
372         *p++ = 'C';
373     if (rights & PRSFS_USR3)
374         *p++ = 'D';
375     if (rights & PRSFS_USR4)
376         *p++ = 'E';
377     if (rights & PRSFS_USR5)
378         *p++ = 'F';
379     if (rights & PRSFS_USR6)
380         *p++ = 'G';
381     if (rights & PRSFS_USR7)
382         *p++ = 'H';
383
384     *p = 0;
385     if (!str[0])
386         strcpy(str, "none");
387     return str;
388 }
389
390
391 /* Parse and store the ACL data from a directory vnode */
392 static afs_uint32
393 parse_acl(XFILE * X, unsigned char *tag, tagged_field * field,
394           afs_uint32 value, tag_parse_info * pi, void *g_refcon,
395           void *l_refcon)
396 {
397     struct acl_accessList *acl;
398     dump_parser *p = (dump_parser *) g_refcon;
399     afs_vnode *v = (afs_vnode *) l_refcon;
400     afs_uint32 r, i, n;
401
402     if ((r = xfread(X, v->acl, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE)))
403         return r;
404
405     v->field_mask |= F_VNODE_ACL;
406     if (p->print_flags & DSPRINT_ACL) {
407         acl = (struct acl_accessList *)(v->acl);
408         n = ntohl(acl->positive);
409         if (n) {
410             printf(" Positive ACL: %d entries\n", n);
411             for (i = 0; i < n; i++)
412                 printf("              %9d  %s\n", ntohl(acl->entries[i].id),
413                        rights2str(ntohl(acl->entries[i].rights)));
414         }
415         n = ntohl(acl->negative);
416         if (n) {
417             printf(" Negative ACL: %d entries\n", n);
418             for (i = ntohl(acl->positive); i < ntohl(acl->total); i++)
419                 printf("              %9d  %s\n", ntohl(acl->entries[i].id),
420                        rights2str(ntohl(acl->entries[i].rights)));
421         }
422     }
423     return ReadByte(X, tag);
424 }
425
426
427 /* Parse or skip over the vnode data */
428 static afs_uint32
429 parse_vdata(XFILE * X, unsigned char *tag, tagged_field * field,
430             afs_uint32 value, tag_parse_info * pi, void *g_refcon,
431             void *l_refcon)
432 {
433     dump_parser *p = (dump_parser *) g_refcon;
434     afs_vnode *v = (afs_vnode *) l_refcon;
435     static char *symlink_buf = 0;
436     static int symlink_size = 0;
437     afs_uint32 r;
438
439     if ((r = ReadInt32(X, &v->size)))
440         return r;
441     v->field_mask |= F_VNODE_SIZE;
442
443     if (v->size) {
444         v->field_mask |= F_VNODE_DATA;
445         if ((r = xftell(X, &v->d_offset)))
446             return r;
447         if (p->print_flags & DSPRINT_VNODE)
448             printf("%s%d (0x%08x) bytes at %s (0x%s)\n", field->label,
449                    v->size, v->size, decimate_int64(&v->d_offset, 0),
450                    hexify_int64(&v->d_offset, 0));
451
452         switch (v->type) {
453         case vSymlink:
454             if (v->size > symlink_size) {
455                 if (symlink_buf)
456                     symlink_buf = (char *)realloc(symlink_buf, v->size + 1);
457                 else
458                     symlink_buf = (char *)malloc(v->size + 1);
459                 symlink_size = symlink_buf ? v->size : 0;
460             }
461             if (symlink_buf) {
462                 if ((r = xfread(X, symlink_buf, v->size)))
463                     return r;
464                 symlink_buf[v->size] = 0;
465                 if (p->print_flags & DSPRINT_VNODE)
466                     printf(" Target:       %s\n", symlink_buf);
467             } else {
468                 /* Call the callback here, because it's non-fatal */
469                 if (p->cb_error)
470                     (p->cb_error) (ENOMEM, 0, p->err_refcon,
471                                    "Out of memory reading symlink");
472                 if ((r = xfskip(X, v->size)))
473                     return r;
474             }
475             break;
476
477         case vDirectory:
478             if (p->cb_dirent || (p->print_flags & DSPRINT_DIR)) {
479                 if ((r = parse_directory(X, p, v, v->size, 0)))
480                     return r;
481                 break;
482             }
483
484         default:
485             if ((r = xfskip(X, v->size)))
486                 return r;
487         }
488     } else if (p->print_flags & DSPRINT_VNODE) {
489         printf("%sEmpty\n", field->label);
490     }
491     if (p->repair_flags & DSFIX_VDSYNC) {
492         r = resync_vnode(X, p, v, 10, 15);
493         if (r)
494             return r;
495     }
496     return ReadByte(X, tag);
497 }