db0b2fab9571ba8efa3e1866de3d5427662e8d07
[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[20];
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 }