Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / tools / dumpscan / afsdump_extract.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 /* afsdump_extract.c - Extract files from an AFS dump */
30
31 #include <afsconfig.h>
32 #include <afs/param.h>
33
34 #include <roken.h>
35
36 #include <afs/com_err.h>
37 #include <afs/cellconfig.h>
38 #include <afs/vlserver.h>
39 #include <afs/afsint.h>
40 #include <afs/volser.h>
41 #include <rx/rxkad.h>
42
43 #include "dumpscan.h"
44 #include "dumpscan_errs.h"
45 #include "xf_errs.h"
46
47 #define COPYBUFSIZE (256*1024)
48
49 extern int optind;
50 extern char *optarg;
51
52 char *argv0;
53 static char **file_names;
54 static int *file_vnums, name_count, vnum_count;
55
56 static char *input_path, *target;
57 static int quiet, verbose, error_count, dirs_done, extract_all;
58 static int nomode, use_realpath, use_vnum;
59 static int do_acls, do_headers;
60
61 static path_hashinfo phi;
62 static dump_parser dp;
63
64 /* Print a usage message and exit */
65 static void
66 usage(int status, char *msg)
67 {
68 if (msg)
69 fprintf(stderr, "%s: %s\n", argv0, msg);
70 fprintf(stderr, "Usage: %s [options] dumpfile [dest [files...]]\n",
71 argv0);
72 fprintf(stderr, " -A Save ACL's\n");
73 fprintf(stderr, " -H Save headers\n");
74 fprintf(stderr, " -h Print this help message\n");
75 fprintf(stderr, " -i Use vnode numbers\n");
76 fprintf(stderr, " -n Don't actually create files\n");
77 fprintf(stderr, " -p Use real pathnames internally\n");
78 fprintf(stderr, " -q Quiet mode (don't print errors)\n");
79 fprintf(stderr, " -v Verbose mode\n");
80 fprintf(stderr, "The destination directory defaults to .\n");
81 fprintf(stderr, "Files may be vnode numbers or volume-relative paths;\n");
82 fprintf(stderr, "If vnode numbers are used, files will be extracted\n");
83 fprintf(stderr,
84 "a name generated from the vnode number and uniqifier.\n");
85 fprintf(stderr, "If paths are used, -p is implied and files will be\n");
86 fprintf(stderr, "into correctly-named files.\n");
87 exit(status);
88 }
89
90
91 /* Parse the command-line options */
92 static void
93 parse_options(int argc, char **argv)
94 {
95 int c, i, i_name, i_vnum;
96
97 /* Set the program name */
98 if ((argv0 = strrchr(argv[0], '/')))
99 argv0++;
100 else
101 argv0 = argv[0];
102
103 /* Initialize options */
104 input_path = 0;
105 quiet = verbose = nomode = 0;
106 use_realpath = use_vnum = do_acls = do_headers = extract_all = 0;
107
108 /* Initialize other stuff */
109 error_count = 0;
110
111 /* Parse the options */
112 while ((c = getopt(argc, argv, "AHhinpqv")) != EOF) {
113 switch (c) {
114 case 'A':
115 do_acls = 1;
116 continue;
117 case 'H':
118 do_headers = 1;
119 continue;
120 case 'i':
121 use_vnum = 1;
122 continue;
123 case 'n':
124 nomode = 1;
125 continue;
126 case 'p':
127 use_realpath = 1;
128 continue;
129 case 'q':
130 quiet = 1;
131 continue;
132 case 'v':
133 verbose = 1;
134 continue;
135 case 'h':
136 usage(0, 0);
137 default:
138 usage(1, "Invalid option!");
139 }
140 }
141
142 if (quiet && verbose)
143 usage(1, "Can't specify both -q and -v");
144
145 /* Parse non-option arguments */
146 if (argc - optind < 1)
147 usage(1, "Dumpfile name required!");
148 input_path = argv[optind];
149
150 if (argc - optind < 2)
151 target = ".";
152 target = argv[optind + 1];
153
154 vnum_count = name_count = 0;
155 if (argc - optind < 3)
156 extract_all = 1;
157 else {
158 argv += optind + 2;
159 argc -= optind + 2;
160 for (i = 0; i < argc; i++) {
161 if (argv[i][0] == '/')
162 name_count++;
163 else
164 vnum_count++;
165 }
166 file_names = malloc(name_count + sizeof(char *));
167 file_vnums = malloc(vnum_count + sizeof(afs_uint32));
168 if (name_count)
169 use_realpath = 1;
170
171 i_name = i_vnum = 0;
172 for (i = 0; i < argc; i++) {
173 if (argv[i][0] == '/')
174 file_names[i_name++] = argv[i];
175 else
176 file_vnums[i_vnum++] = strtol(argv[i], 0, 0);
177 }
178 }
179 }
180
181
182 static int
183 mkdirp(char *path)
184 {
185 char *x = path, slash;
186 struct stat statbuf;
187
188 for (;;) {
189 while (*x && *x != '/')
190 x++;
191 slash = *x;
192 *x = 0;
193
194 if (stat(path, &statbuf)) {
195 if (errno == ENOENT) {
196 if (verbose)
197 printf("> mkdir %s\n", path);
198 if (!mkdir(path, 0755))
199 errno = 0;
200 }
201 }
202 if (!slash)
203 break;
204 *x++ = '/';
205 if (errno)
206 return errno;
207 }
208
209 return 0;
210 }
211
212
213 static char *
214 modestr(int mode)
215 {
216 static char str[10];
217 int i;
218
219 strcpy(str, "rwxrwxrwx");
220 for (i = 0; i < 9; i++) {
221 if (!(mode & (1 << i)))
222 str[8 - i] = '-';
223 }
224 if (mode & 01000)
225 str[8] = (str[8] == '-') ? 'T' : 't';
226 if (mode & 02000)
227 str[5] = (str[5] == '-') ? 'S' : 's';
228 if (mode & 04000)
229 str[2] = (str[2] == '-') ? 'S' : 's';
230 return str;
231 }
232
233
234 static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
235 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
236 };
237 static char *
238 datestr(time_t date)
239 {
240 static char str[13];
241 time_t clock = time(0);
242 struct tm *now, *then;
243 int diff;
244
245 now = localtime(&clock);
246 then = localtime(&date);
247
248 diff = now->tm_mon - then->tm_mon;
249 if (then->tm_year == now->tm_year - 1)
250 diff += 12;
251 if (then->tm_year == now->tm_year + 1)
252 diff -= 12;
253
254 if (diff < 5 || diff > 5)
255 sprintf(str, "%3s %2d %4d", month[then->tm_mon], then->tm_mday,
256 then->tm_year + 1900);
257 else
258 sprintf(str, "%3s %2d %2d:%2d", month[then->tm_mon], then->tm_mday,
259 then->tm_hour, then->tm_min);
260 return str;
261 }
262
263
264 /* Should we use this vnode?
265 * Return 0 if no, non-0 if yes
266 */
267 static int
268 usevnode(XFILE * X, afs_uint32 vnum, char *vnodepath)
269 {
270 int vl, vpl, r, i;
271
272 /* Special case */
273 if (extract_all || !strcmp(vnodepath, "/"))
274 return 1;
275
276 for (i = 0; i < vnum_count; i++)
277 if (vnum == file_vnums[i])
278 return 2;
279
280 vl = strlen(vnodepath);
281 /*fprintf(stderr, "++ checking %s\n", vnodepath);*/
282 for (i = 0; i < name_count; i++) {
283 vpl = strlen(file_names[i]);
284 /* fprintf(stderr, " %s\n", file_names[i]);*/
285
286 if (vl > vpl) {
287 r = !strncmp(file_names[i], vnodepath, vpl)
288 && vnodepath[vpl] == '/';
289 } else if (vl < vpl) {
290 r = !strncmp(file_names[i], vnodepath, vl)
291 && file_names[i][vl] == '/';
292 } else {
293 r = !strcmp(file_names[i], vnodepath);
294 }
295 if (r)
296 return 1;
297 }
298 return 0;
299 }
300
301
302 static int
303 copyfile(XFILE * in, XFILE * out, int size)
304 {
305 static char buf[COPYBUFSIZE];
306 int nr, r;
307
308 while (size) {
309 nr = (size > COPYBUFSIZE) ? COPYBUFSIZE : size;
310 if ((r = xfread(in, buf, nr)))
311 return r;
312 if ((r = xfwrite(out, buf, nr)))
313 return r;
314 size -= nr;
315 }
316 return 0;
317 }
318
319
320 /* A callback to count and print errors */
321 static afs_uint32
322 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...)
323 {
324 va_list alist;
325
326 error_count++;
327 if (!quiet) {
328 va_start(alist, msg);
329 afs_com_err_va(argv0, code, msg, alist);
330 va_end(alist);
331 }
332 return 0;
333 }
334
335
336 static afs_uint32
337 dumphdr_cb(afs_dump_header * hdr, XFILE * X, void *refcon)
338 {
339 return 0;
340 }
341
342
343 static afs_uint32
344 volhdr_cb(afs_vol_header * hdr, XFILE * X, void *refcon)
345 {
346 return 0;
347 }
348
349
350 static afs_uint32
351 directory_cb(afs_vnode * v, XFILE * X, void *refcon)
352 {
353 char *vnodepath;
354 int r, use = 0;
355
356 /* Should we even use this? */
357 if (!use_vnum) {
358 if ((r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)))
359 return r;
360 if (!(use = usevnode(X, v->vnode, vnodepath))) {
361 free(vnodepath);
362 return 0;
363 }
364 }
365
366 /* Print it out */
367 if (verbose) {
368 if (use_vnum)
369 printf("d%s %3d %-11d %11d %s #%d:%d\n", modestr(v->mode),
370 v->nlinks, v->owner, v->size, datestr(v->server_date),
371 v->vnode, v->vuniq);
372 else
373 printf("d%s %3d %-11d %11d %s %s\n", modestr(v->mode), v->nlinks,
374 v->owner, v->size, datestr(v->server_date), vnodepath);
375 } else if (!quiet && !use_vnum)
376 printf("%s\n", vnodepath);
377
378 /* Make the directory, if needed */
379 if (!nomode && !use_vnum && use != 2) {
380 if (strcmp(vnodepath, "/")
381 && (r = mkdirp(vnodepath + 1))) {
382 free(vnodepath);
383 return r;
384 }
385 if (do_acls) {
386 /* XXX do ACL's later */
387 }
388 }
389 if (!use_vnum)
390 free(vnodepath);
391 return 0;
392 }
393
394
395 static afs_uint32
396 file_cb(afs_vnode * v, XFILE * X, void *refcon)
397 {
398 char *vnodepath, vnpx[30];
399 dt_uint64 where;
400 XFILE OX;
401 int r, use = 0;
402
403 if (!dirs_done) {
404 dirs_done = 1;
405 if (verbose)
406 printf("* Extracting files...\n");
407 }
408
409 /* Should we even use this? */
410 if (!use_vnum) {
411 if ((r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)))
412 return r;
413 if (!(use = usevnode(X, v->vnode, vnodepath))) {
414 free(vnodepath);
415 return 0;
416 }
417 if (use == 2) {
418 free(vnodepath);
419 sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
420 vnodepath = vnpx;
421 }
422 } else {
423 sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
424 vnodepath = vnpx;
425 }
426
427 /* Print it out */
428 if (verbose) {
429 printf("-%s %3d %-11d %11d %s %s\n", modestr(v->mode), v->nlinks,
430 v->owner, v->size, datestr(v->server_date), vnodepath);
431 } else if (!quiet) {
432 printf("%s\n", vnodepath);
433 }
434
435 if (!nomode) {
436 if ((r = xftell(X, &where))
437 || (r = xfseek(X, &v->d_offset))
438 || (r =
439 xfopen_path(&OX, O_RDWR | O_CREAT | O_TRUNC, vnodepath + 1,
440 0644))) {
441 if (vnodepath != vnpx)
442 free(vnodepath);
443 return r;
444 }
445 r = copyfile(X, &OX, v->size);
446 xfclose(&OX);
447 xfseek(X, &where);
448 } else
449 r = 0;
450
451 if (vnodepath != vnpx)
452 free(vnodepath);
453 return r;
454 }
455
456
457 static afs_uint32
458 symlink_cb(afs_vnode * v, XFILE * X, void *refcon)
459 {
460 char *vnodepath, *linktarget, vnpx[30];
461 dt_uint64 where;
462 int r, use = 0;
463
464 if (!dirs_done) {
465 dirs_done = 1;
466 if (verbose)
467 printf("* Extracting files...\n");
468 }
469
470 /* Should we even use this? */
471 if (!use_vnum) {
472 if ((r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)))
473 return r;
474 if (!(use = usevnode(X, v->vnode, vnodepath))) {
475 free(vnodepath);
476 return 0;
477 }
478 if (use == 2) {
479 free(vnodepath);
480 sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
481 vnodepath = vnpx;
482 }
483 } else {
484 sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq);
485 vnodepath = vnpx;
486 }
487
488 if (!(linktarget = malloc(v->size + 1))) {
489 if (vnodepath != vnpx)
490 free(vnodepath);
491 return DSERR_MEM;
492 }
493 if ((r = xftell(X, &where))
494 || (r = xfseek(X, &v->d_offset))
495 || (r = xfread(X, linktarget, v->size))) {
496 if (vnodepath != vnpx)
497 free(vnodepath);
498 free(linktarget);
499 return r;
500 }
501 xfseek(X, &where);
502 linktarget[v->size] = 0;
503
504 /* Print it out */
505 if (verbose)
506 printf("l%s %3d %-11d %11d %s %s -> %s\n", modestr(v->mode),
507 v->nlinks, v->owner, v->size, datestr(v->server_date),
508 vnodepath, linktarget);
509 else if (!quiet)
510 printf("%s\n", vnodepath);
511
512 r = 0;
513 if (!nomode) {
514 if (symlink(linktarget, vnodepath + 1))
515 r = errno;
516 }
517
518 free(linktarget);
519 if (vnodepath != vnpx)
520 free(vnodepath);
521 return r;
522 }
523
524
525 static afs_uint32
526 lose_cb(afs_vnode * v, XFILE * F, void *refcon)
527 {
528 if (!dirs_done) {
529 dirs_done = 1;
530 if (verbose)
531 printf("* Extracting files...\n");
532 }
533
534 return 0;
535 }
536
537
538 /* Main program */
539 int
540 main(int argc, char **argv)
541 {
542 XFILE input_file;
543 afs_uint32 r;
544
545 parse_options(argc, argv);
546 initialize_acfg_error_table();
547 initialize_AVds_error_table();
548 initialize_rxk_error_table();
549 initialize_u_error_table();
550 initialize_vl_error_table();
551 initialize_vols_error_table();
552 initialize_xFil_error_table();
553 r = xfopen(&input_file, O_RDONLY, input_path);
554 if (r) {
555 afs_com_err(argv0, r, "opening %s", input_path);
556 exit(2);
557 }
558
559 memset(&dp, 0, sizeof(dp));
560 dp.cb_error = my_error_cb;
561 if (input_file.is_seekable)
562 dp.flags |= DSFLAG_SEEK;
563 dirs_done = 0;
564
565 if (!use_vnum) {
566 dt_uint64 where;
567
568 memset(&phi, 0, sizeof(phi));
569 phi.p = &dp;
570
571 if (verbose)
572 printf("* Building pathname info...\n");
573 if ((r = xftell(&input_file, &where))
574 || (r = Path_PreScan(&input_file, &phi, 1))
575 || (r = xfseek(&input_file, &where))) {
576 afs_com_err(argv0, r, "- path initialization failed");
577 xfclose(&input_file);
578 exit(1);
579 }
580 }
581
582 dp.cb_vnode_dir = directory_cb;
583 dp.cb_vnode_file = file_cb;
584 dp.cb_vnode_link = symlink_cb;
585 dp.cb_vnode_empty = lose_cb;
586 dp.cb_vnode_wierd = lose_cb;
587 if (do_headers) {
588 dp.cb_dumphdr = dumphdr_cb;
589 dp.cb_volhdr = volhdr_cb;
590 }
591
592 if (!nomode) {
593 mkdir(target, 0755);
594 if (chdir(target)) {
595 fprintf(stderr, "chdir %s failed: %s\n", target, strerror(errno));
596 exit(1);
597 }
598 }
599 r = ParseDumpFile(&input_file, &dp);
600
601 if (verbose && error_count)
602 fprintf(stderr, "*** %d errors\n", error_count);
603 if (r && !quiet)
604 fprintf(stderr, "*** FAILED: %s\n", afs_error_message(r));
605 exit(0);
606 }