Use calloc, rather than malloc/memset
[openafs.git] / src / tools / dumpscan / pathname.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 /* pathname.c - Pathname lookup and traversal */
30
31 #include <errno.h>
32 #include <string.h>
33
34 #include "dumpscan.h"
35 #include "dumpscan_errs.h"
36
37 /* Hash function for a vnode */
38 #define BUCKET_SIZE 32
39 #define vnode_hash(phi,vnode) ((vnode) & ((1 << (phi)->hash_size) - 1))
40
41
42 static vhash_ent *
43 get_vhash_ent(path_hashinfo * phi, afs_uint32 vnode, int make)
44 {
45     int key = vnode_hash(phi, vnode);
46     vhash_ent *vhe;
47
48     for (vhe = phi->hash_table[key]; vhe && vhe->vnode != vnode;
49          vhe = vhe->next);
50     if (make && !vhe) {
51         vhe = calloc(1, sizeof(vhash_ent));
52         if (vhe) {
53             vhe->vnode = vnode;
54             vhe->next = phi->hash_table[key];
55             phi->hash_table[key] = vhe;
56         }
57     }
58     return vhe;
59 }
60
61
62 static afs_uint32
63 volhdr_cb(afs_vol_header * hdr, XFILE * X, void *refcon)
64 {
65     path_hashinfo *phi = (path_hashinfo *) refcon;
66     int nfiles, hsize;
67
68     if (hdr->field_mask & F_VOLHDR_NFILES) {
69         nfiles = phi->n_vnodes = hdr->nfiles;
70         for (phi->hash_size = 1; nfiles > BUCKET_SIZE;
71              phi->hash_size++, nfiles >>= 1);
72         hsize = (1 << phi->hash_size);
73         phi->hash_table = calloc(hsize ,sizeof(vhash_ent *));
74         if (!phi->hash_table)
75             return ENOMEM;
76         return 0;
77     } else {
78         if (phi->p->cb_error)
79             (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
80                                 "File count missing from volume header");
81         return DSERR_FMT;
82     }
83 }
84
85
86 static afs_uint32
87 vnode_keep(afs_vnode * v, XFILE * X, void *refcon)
88 {
89     path_hashinfo *phi = (path_hashinfo *) refcon;
90     vhash_ent *vhe;
91
92     if (!phi->hash_table) {
93         if (phi->p->cb_error)
94             (phi->p->cb_error) (DSERR_FMT, 1, phi->p->refcon,
95                                 "No volume header in dump???");
96         return DSERR_FMT;
97     }
98     vhe = get_vhash_ent(phi, v->vnode, 1);
99     if (!vhe)
100         return ENOMEM;
101     cp64(vhe->v_offset, v->offset);
102     if (v->field_mask & F_VNODE_PARENT)
103         vhe->parent = v->parent;
104     if (v->field_mask & F_VNODE_DATA) {
105         cp64(vhe->d_offset, v->d_offset);
106         vhe->d_size = v->size;
107     }
108     if ((v->field_mask & F_VNODE_TYPE) && v->type == vDirectory)
109         phi->n_dirs++;
110     else
111         phi->n_files++;
112     return 0;
113 }
114
115
116 static afs_uint32
117 vnode_stop(afs_vnode * v, XFILE * X, void *refcon)
118 {
119     path_hashinfo *phi = (path_hashinfo *) refcon;
120     int r;
121
122     /* If the file is seekable, try to position so we can pick up later... */
123     if (phi->p->flags & DSFLAG_SEEK)
124       if ((r = xfseek(X, &v->offset)))
125             return r;
126     return DSERR_DONE;
127 }
128
129
130 static afs_uint32
131 dirent_cb(afs_vnode * v, afs_dir_entry * de, XFILE * X, void *refcon)
132 {
133     path_hashinfo *phi = (path_hashinfo *) refcon;
134     vhash_ent *vhe;
135
136     if (!phi->hash_table) {
137         if (phi->p->cb_error)
138             (phi->p->cb_error) (DSERR_FMT, 1, phi->p->refcon,
139                                 "No volume header in dump???");
140         return DSERR_FMT;
141     }
142     if (!strcmp(de->name, ".") || !strcmp(de->name, ".."))
143         return 0;
144     vhe = get_vhash_ent(phi, de->vnode, 1);
145     if (!vhe)
146         return ENOMEM;
147     vhe->parent = v->vnode;
148     return 0;
149 }
150
151
152 /* Prescan the vnodes in a dump file, collecting information that will
153  * be useful in generating and following pathnames.
154  */
155 afs_uint32
156 Path_PreScan(XFILE * X, path_hashinfo * phi, int full)
157 {
158     dump_parser my_p, *p = phi->p;
159
160     memset(phi, 0, sizeof(path_hashinfo));
161     phi->p = p;
162     memset(&my_p, 0, sizeof(my_p));
163     my_p.refcon = (void *)phi;
164     my_p.cb_volhdr = volhdr_cb;
165     my_p.cb_vnode_dir = vnode_keep;
166     if (full) {
167         my_p.cb_vnode_file = vnode_keep;
168         my_p.cb_vnode_link = vnode_keep;
169         my_p.cb_vnode_empty = vnode_keep;
170         my_p.cb_vnode_wierd = vnode_keep;
171     } else {
172         my_p.cb_vnode_file = vnode_stop;
173         my_p.cb_vnode_link = vnode_stop;
174         my_p.cb_vnode_empty = vnode_stop;
175         my_p.cb_vnode_wierd = vnode_stop;
176     }
177     my_p.err_refcon = p->err_refcon;
178     my_p.cb_error = p->cb_error;
179     my_p.cb_dirent = dirent_cb;
180     my_p.flags = p->flags;
181     my_p.print_flags = p->print_flags;
182     my_p.repair_flags = p->repair_flags;
183
184     return ParseDumpFile(X, &my_p);
185 }
186
187
188 /* Free the hash table in a path_hashinfo */
189 void
190 Path_FreeHashTable(path_hashinfo * phi)
191 {
192     int i, size;
193     vhash_ent *vhe, *next_vhe;
194
195     if (phi->hash_table) {
196         size = (1 << phi->hash_size);
197         for (i = 0; i < size; i++)
198             for (vhe = phi->hash_table[i]; vhe; vhe = next_vhe) {
199                 next_vhe = vhe->next;
200                 free(vhe);
201             }
202         free(phi->hash_table);
203     }
204 }
205
206
207 /* Follow a pathname to the vnode it represents */
208 afs_uint32
209 Path_Follow(XFILE * X, path_hashinfo * phi, char *path, vhash_ent * his_vhe)
210 {
211     vhash_ent *vhe;
212     char *name;
213     afs_uint32 r, vnum = 1;
214
215     if (*path == '/')
216         path++;
217     name = strtok(path, "/");
218
219     for (name = strtok(path, "/"); name; name = strtok(0, "/")) {
220         if (!(vnum & 1)) {
221             if (phi->p->cb_error)
222                 (phi->p->cb_error) (ENOTDIR, 1, phi->p->err_refcon,
223                                     "Not a directory vnode");
224             return ENOTDIR;
225         }
226         vhe = get_vhash_ent(phi, vnum, 0);
227         if (!vhe) {
228             if (phi->p->cb_error)
229                 (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
230                                     "Vnode %d not found in hash table", vnum);
231             return DSERR_FMT;
232         }
233         if (zero64(vhe->d_offset)) {
234             if (phi->p->cb_error)
235                 (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
236                                     "Directory vnode %d is incomplete", vnum);
237             return DSERR_FMT;
238         }
239         if ((r = xfseek(X, &vhe->d_offset))) {
240             if (phi->p->cb_error)
241                 (phi->p->cb_error) (r, 1, phi->p->err_refcon,
242                                     "Unable to seek to directory %d", vnum);
243             return r;
244         }
245         vnum = 0;
246         r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnum, 0);
247         if (r)
248             return r;
249         if (!vnum) {
250             if (phi->p->cb_error)
251                 (phi->p->cb_error) (ENOENT, 1, phi->p->err_refcon,
252                                     "No such vnode");
253             return ENOENT;
254         }
255     }
256     vhe = get_vhash_ent(phi, vnum, 0);
257     if (!vhe) {
258         if (phi->p->cb_error)
259             (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
260                                 "Vnode %d not found in hash table", vnum);
261         return DSERR_FMT;
262     }
263     if (his_vhe)
264         *his_vhe = *vhe;
265     return 0;
266 }
267
268
269 afs_uint32
270 Path_Build(XFILE * X, path_hashinfo * phi, afs_uint32 vnode, char **his_path,
271            int fast)
272 {
273     vhash_ent *vhe;
274     char *name, *path = 0, fastbuf[12];
275     char *x, *y;
276     afs_uint32 parent, r;
277     int nl, pl = 0;
278
279     if (vnode == 1) {
280         *his_path = strdup("/");
281         if (!his_path) {
282             if (phi->p->cb_error)
283                 (phi->p->cb_error) (ENOMEM, 1, phi->p->err_refcon,
284                                     "No memory for pathname of vnode 1");
285             return ENOMEM;
286         }
287         return 0;
288     }
289
290     *his_path = 0;
291     vhe = get_vhash_ent(phi, vnode, 0);
292     if (!vhe) {
293         if (phi->p->cb_error)
294             (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
295                                 "Vnode %d not found in hash table", vnode);
296         return DSERR_FMT;
297     }
298     while (vnode != 1) {
299         /* Find the parent */
300         if (!vhe->parent) {
301             if (phi->p->cb_error)
302                 (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
303                                     "Vnode %d has no parent?", vnode);
304             if (path)
305                 free(path);
306             return DSERR_FMT;
307         }
308         parent = vhe->parent;
309         vhe = get_vhash_ent(phi, parent, 0);
310         if (phi->p->print_flags & DSPRINT_DEBUG)
311             fprintf(stderr, "Searching for vnode %d in parent %d\n", vnode,
312                     parent);
313         if (!vhe) {
314             if (phi->p->cb_error)
315                 (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
316                                     "Vnode %d not found in hash table",
317                                     parent);
318             if (path)
319                 free(path);
320             return DSERR_FMT;
321         }
322
323         if (fast) {
324             /* Make up a path component from the vnode number */
325             sprintf(fastbuf, "%d", vnode);
326             name = fastbuf;
327         } else {
328             /* Do a reverse-lookup in the parent directory */
329             if (zero64(vhe->d_offset)) {
330                 if (phi->p->cb_error)
331                     (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
332                                         "Directory vnode %d is incomplete",
333                                         parent);
334                 if (path)
335                     free(path);
336                 return DSERR_FMT;
337             }
338             if ((r = xfseek(X, &vhe->d_offset))) {
339                 if (phi->p->cb_error)
340                     (phi->p->cb_error) (errno, 1, phi->p->err_refcon,
341                                         "Unable to seek to directory %d",
342                                         parent);
343                 if (path)
344                     free(path);
345                 return r;
346             }
347
348             name = 0;
349             r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnode, 0);
350             if (r)
351                 return r;
352             if (!name) {
353                 if (phi->p->cb_error)
354                     (phi->p->cb_error) (DSERR_FMT, 1, phi->p->err_refcon,
355                                         "No entry for vnode %d in directory %d",
356                                         vnode, parent);
357                 if (path)
358                     free(path);
359                 return ENOENT;
360             }
361         }
362
363         nl = strlen(name);
364         if (path) {
365             path = (char *)realloc(path, nl + pl + 2);
366             if (!path) {
367                 if (phi->p->cb_error)
368                     (phi->p->cb_error) (ENOMEM, 1, phi->p->err_refcon,
369                                         "No memory for pathname of vnode 1");
370                 return ENOMEM;
371             }
372             x = path + pl;
373             y = x + nl + 1;
374             while (x >= path)
375                 *y-- = *x--;
376             path[0] = '/';
377             for (x = name, y = path + 1; *x;)
378                 *y++ = *x++;
379             pl += nl + 1;
380         } else {
381             path = (char *)malloc(nl + 2);
382             if (!path) {
383                 if (phi->p->cb_error)
384                     (phi->p->cb_error) (ENOMEM, 1, phi->p->err_refcon,
385                                         "No memory for pathname of vnode 1");
386                 return ENOMEM;
387             }
388             path[0] = '/';
389             strcpy(path + 1, name);
390             pl = nl + 1;
391         }
392         if (!fast)
393             free(name);
394         vnode = parent;
395     }
396     *his_path = path;
397     return 0;
398 }