Import Upstream version 1.8.5
[hcoop/debian/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 = 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 }