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