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