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