448
|
1 /*
|
|
2 * Copyright (c) 2000, Red Hat, Inc.
|
|
3 *
|
|
4 * This program is free software; you can redistribute it and/or modify
|
|
5 * it under the terms of the GNU General Public License as published by
|
|
6 * the Free Software Foundation; either version 2 of the License, or
|
|
7 * (at your option) any later version.
|
|
8 *
|
|
9 * A copy of the GNU General Public License can be found at
|
|
10 * http://www.gnu.org/
|
|
11 *
|
|
12 * Written by DJ Delorie <dj@cygnus.com>
|
|
13 *
|
|
14 */
|
|
15
|
|
16 /* Built-in tar functionality. See tar.h for usage. */
|
|
17
|
|
18 #include <stdio.h>
|
|
19 #include <stdlib.h>
|
|
20 #include <sys/types.h>
|
|
21 #include <sys/stat.h>
|
|
22
|
|
23 #include "win32.h"
|
|
24 #include <zlib.h>
|
|
25 #include "tar.h"
|
|
26 #include "mkdir.h"
|
|
27 #include "log.h"
|
|
28
|
|
29 #include "port.h"
|
|
30
|
|
31 #if defined(CYGWIN) || defined(MINGW)
|
|
32 #define FACTOR (0x19db1ded53ea710LL)
|
|
33 #define NSPERSEC 10000000LL
|
|
34 #else
|
|
35 __int64 FACTOR=0x19db1ded53ea710L;
|
|
36 __int64 NSPERSEC=10000000L;
|
|
37 #endif
|
|
38 #define SYMLINK_COOKIE "!<symlink>"
|
|
39
|
|
40 typedef struct {
|
|
41 char name[100]; /* 0 */
|
|
42 char mode[8]; /* 100 */
|
|
43 char uid[8]; /* 108 */
|
|
44 char gid[8]; /* 116 */
|
|
45 char size[12]; /* 124 */
|
|
46 char mtime[12]; /* 136 */
|
|
47 char chksum[8]; /* 148 */
|
|
48 char typeflag; /* 156 */
|
|
49 char linkname[100]; /* 157 */
|
|
50 char magic[6]; /* 257 */
|
|
51 char version[2]; /* 263 */
|
|
52 char uname[32]; /* 265 */
|
|
53 char gname[32]; /* 297 */
|
|
54 char devmajor[8]; /* 329 */
|
|
55 char devminor[8]; /* 337 */
|
|
56 char prefix[155]; /* 345 */
|
|
57 char junk[12]; /* 500 */
|
|
58 } tar_header_type;
|
|
59
|
|
60 typedef struct tar_map_result_type_s {
|
|
61 struct tar_map_result_type_s *next;
|
|
62 char *stored_name;
|
|
63 char *mapped_name;
|
|
64 } tar_map_result_type;
|
|
65
|
|
66 static tar_map_result_type *tar_map_result = 0;
|
|
67
|
|
68 static int err;
|
|
69
|
|
70 static char file_name[_MAX_PATH+512];
|
|
71 static char have_longname = 0;
|
|
72 static int file_length;
|
|
73
|
|
74 static tar_header_type tar_header;
|
|
75 static char buf[512];
|
|
76
|
|
77 static int _tar_file_size = 0;
|
|
78 int _tar_verbose = 0;
|
|
79 FILE * _tar_vfile = 0;
|
|
80 #define vp if (_tar_verbose) fprintf
|
|
81 #define vp2 if (_tar_verbose>1) fprintf
|
|
82
|
|
83 static gzFile g = 0;
|
|
84
|
|
85 static char *
|
|
86 xstrdup (char *c)
|
|
87 {
|
|
88 char *r = (char *) malloc (strlen (c) + 1);
|
|
89 if (!r)
|
|
90 exit_setup (1);
|
|
91 strcpy (r, c);
|
|
92 return r;
|
|
93 }
|
|
94
|
|
95 int
|
|
96 tar_open (char *pathname)
|
|
97 {
|
|
98 struct stat s;
|
|
99 if (_tar_vfile == 0)
|
|
100 _tar_vfile = stderr;
|
|
101
|
|
102 vp2 (_tar_vfile, "tar: open `%s'\n", pathname);
|
|
103 if (stat (pathname, &s) < 0)
|
|
104 return 1;
|
|
105 _tar_file_size = s.st_size;
|
|
106
|
|
107 g = gzopen (pathname, "rb");
|
|
108 if (sizeof (tar_header) != 512)
|
|
109 {
|
|
110 /* drastic, but important */
|
|
111 fprintf (stderr, "compilation error: tar header struct not 512"
|
|
112 " bytes (it's %d)\n", sizeof (tar_header));
|
|
113 exit_setup (1);
|
|
114 }
|
|
115 err = 0;
|
|
116 return g ? 0 : 1;
|
|
117 }
|
|
118
|
452
|
119 /* For some reason the cygwin version uses a function that is not in
|
|
120 the original source. We duplicate it here - although this does mean
|
|
121 revealing some internals. */
|
|
122 extern "C" {
|
|
123 z_off_t ZEXPORT tar_gzctell (gzFile file);
|
|
124 typedef struct gz_stream {
|
|
125 z_stream stream;
|
|
126 int z_err; /* error code for last stream operation */
|
|
127 int z_eof; /* set if end of input file */
|
|
128 FILE *file; /* .gz file */
|
|
129 Byte *inbuf; /* input buffer */
|
|
130 Byte *outbuf; /* output buffer */
|
|
131 uLong crc; /* crc32 of uncompressed data */
|
|
132 char *msg; /* error message */
|
|
133 char *path; /* path name for debugging only */
|
|
134 int transparent; /* 1 if input file is not a .gz file */
|
|
135 char mode; /* 'w' or 'r' */
|
|
136 long startpos; /* start of compressed data in file (header skipped) */
|
|
137 } gz_stream;
|
|
138 };
|
|
139
|
|
140 z_off_t ZEXPORT tar_gzctell (gzFile file)
|
|
141 {
|
|
142 gz_stream *s = (gz_stream *)file;
|
|
143 return ftell(s->file);
|
|
144 }
|
|
145
|
448
|
146 int
|
|
147 tar_ftell ()
|
|
148 {
|
452
|
149 return tar_gzctell (g);
|
448
|
150 }
|
|
151
|
|
152 static void
|
|
153 skip_file ()
|
|
154 {
|
|
155 while (file_length > 0)
|
|
156 {
|
|
157 gzread (g, buf, 512);
|
|
158 file_length -= 512;
|
|
159 }
|
|
160 }
|
|
161
|
|
162 char *
|
|
163 tar_next_file ()
|
|
164 {
|
|
165 int r, n;
|
|
166 char *c;
|
|
167 r = gzread (g, &tar_header, 512);
|
|
168
|
|
169 /* See if we're at end of file */
|
|
170 if (r != 512)
|
|
171 return 0;
|
|
172
|
|
173 /* See if the header is all zeros (i.e. last block) */
|
|
174 n = 0;
|
|
175 for (r = 512/sizeof (int); r; r--)
|
|
176 n |= ((int *)&tar_header)[r-1];
|
|
177 if (n == 0)
|
|
178 return 0;
|
|
179
|
|
180 if (!have_longname && tar_header.typeflag != 'L')
|
|
181 {
|
|
182 memcpy (file_name, tar_header.name, 100);
|
|
183 file_name[100] = 0;
|
|
184 }
|
|
185
|
|
186 sscanf (tar_header.size, "%o", &file_length);
|
|
187
|
|
188 vp2 (_tar_vfile, "%c %9d %s\n", tar_header.typeflag, file_length, file_name);
|
|
189
|
|
190 switch (tar_header.typeflag)
|
|
191 {
|
|
192 case 'L': /* GNU tar long name extension */
|
|
193 if (file_length > _MAX_PATH)
|
|
194 {
|
|
195 skip_file ();
|
|
196 fprintf (stderr, "error: long file name exceeds %d characters\n",
|
|
197 _MAX_PATH);
|
|
198 err ++;
|
|
199 gzread (g, &tar_header, 512);
|
|
200 sscanf (tar_header.size, "%o", &file_length);
|
|
201 skip_file ();
|
|
202 return tar_next_file ();
|
|
203 }
|
|
204 c = file_name;
|
|
205 while (file_length > 0)
|
|
206 {
|
|
207 int need = file_length > 512 ? 512 : file_length;
|
|
208 if (gzread (g, buf, 512) < 512)
|
|
209 return 0;
|
|
210 memcpy (c, buf, need);
|
|
211 c += need;
|
|
212 file_length -= need;
|
|
213 }
|
|
214 *c = 0;
|
|
215 have_longname = 1;
|
|
216 return tar_next_file ();
|
|
217
|
|
218 case '3': /* char */
|
|
219 case '4': /* block */
|
|
220 case '6': /* fifo */
|
|
221 fprintf (stderr, "warning: not extracting special file %s\n",
|
|
222 file_name);
|
|
223 err ++;
|
|
224 return tar_next_file ();
|
|
225
|
|
226 case '0': /* regular file */
|
|
227 case 0: /* regular file also */
|
|
228 case '2': /* symbolic link */
|
|
229 case '5': /* directory */
|
|
230 case '7': /* contiguous file */
|
|
231 return file_name;
|
|
232
|
|
233 case '1': /* hard link, we just copy */
|
|
234 return file_name;
|
|
235
|
|
236 default:
|
|
237 fprintf (stderr, "error: unknown (or unsupported) file type `%c'\n",
|
|
238 tar_header.typeflag);
|
|
239 err ++;
|
|
240 skip_file ();
|
|
241 return tar_next_file ();
|
|
242 }
|
|
243 }
|
|
244
|
|
245 static void
|
|
246 fix_time_stamp (char *path)
|
|
247 {
|
|
248 int mtime;
|
|
249 #if defined(CYGWIN) || defined(MINGW)
|
|
250 long long ftimev;
|
|
251 #else
|
|
252 __int64 ftimev;
|
|
253 #endif
|
|
254 FILETIME ftime;
|
|
255 HANDLE h;
|
|
256
|
|
257 sscanf (tar_header.mtime, "%o", &mtime);
|
|
258 ftimev = mtime * NSPERSEC + FACTOR;
|
|
259 ftime.dwHighDateTime = ftimev >> 32;
|
|
260 ftime.dwLowDateTime = ftimev;
|
|
261 h = CreateFileA (path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
262 0, OPEN_EXISTING,
|
|
263 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
264 if (h)
|
|
265 {
|
|
266 SetFileTime (h, 0, 0, &ftime);
|
|
267 CloseHandle (h);
|
|
268 }
|
|
269 }
|
|
270
|
|
271 static FILE *
|
|
272 common_fopen (char *path)
|
|
273 {
|
|
274 FILE *out;
|
|
275 out = fopen (path, "wb");
|
|
276 if (!out)
|
|
277 {
|
|
278 /* maybe we need to create a directory */
|
|
279 if (mkdir_p (0, path))
|
|
280 {
|
|
281 skip_file ();
|
|
282 return 0;
|
|
283 }
|
|
284 out = fopen (path, "wb");
|
|
285 }
|
|
286 if (!out)
|
|
287 {
|
|
288 fprintf (stderr, "unable to write to file %s\n", path);
|
|
289 perror ("The error was");
|
|
290 skip_file ();
|
|
291 return 0;
|
|
292 }
|
|
293 return out;
|
|
294 }
|
|
295
|
|
296 static void
|
|
297 prepare_for_file (char *path)
|
|
298 {
|
|
299 DWORD w;
|
|
300 mkdir_p (0, path);
|
|
301
|
|
302 w = GetFileAttributes (path);
|
|
303 if (w != 0xffffffff && w & FILE_ATTRIBUTE_DIRECTORY)
|
|
304 {
|
|
305 char *tmp = (char *) malloc (strlen (path) + 10);
|
|
306 int i = 0;
|
|
307 do {
|
|
308 i++;
|
|
309 sprintf (tmp, "%s.old-%d", path, i);
|
|
310 } while (GetFileAttributes (tmp) != 0xffffffff);
|
|
311 fprintf (stderr, "warning: moving directory \"%s\" out of the way.\n", path);
|
|
312 MoveFile (path, tmp);
|
|
313 free (tmp);
|
|
314 }
|
|
315
|
|
316 DeleteFileA (path);
|
|
317 }
|
|
318
|
|
319 int
|
|
320 tar_read_file (char *path)
|
|
321 {
|
|
322 FILE *out, *copy;
|
|
323 HANDLE h;
|
|
324 DWORD w;
|
|
325 int got;
|
|
326 tar_map_result_type *tmr;
|
|
327
|
|
328 switch (tar_header.typeflag)
|
|
329 {
|
|
330 case '0': /* regular files */
|
|
331 case 0:
|
|
332 case '7':
|
|
333 vp (_tar_vfile, "F %s\n", path);
|
|
334 prepare_for_file (path);
|
|
335 out = common_fopen (path);
|
|
336 if (!out)
|
|
337 return 1;
|
|
338
|
|
339 while (file_length > 0)
|
|
340 {
|
|
341 int put;
|
|
342 int want = file_length > 512 ? 512 : file_length;
|
|
343 got = gzread (g, buf, 512);
|
|
344 if (got < 512)
|
|
345 {
|
|
346 fprintf (stderr, "tar: unexpected end of file reading %s\n", path);
|
|
347 fclose (out);
|
|
348 remove (path);
|
|
349 return 1;
|
|
350 }
|
|
351 put = fwrite (buf, 1, want, out);
|
|
352 if (put < want)
|
|
353 {
|
|
354 fprintf (stderr, "tar: out of disk space writing %s\n", path);
|
|
355 fclose (out);
|
|
356 remove (path);
|
|
357 skip_file ();
|
|
358 return 1;
|
|
359 }
|
|
360 file_length -= want;
|
|
361 }
|
|
362 fclose (out);
|
|
363
|
|
364 fix_time_stamp (path);
|
|
365
|
|
366 /* we need this to do hard links below */
|
|
367 tmr = (tar_map_result_type *) malloc (sizeof (tar_map_result_type));
|
|
368 tmr->next = tar_map_result;
|
|
369 tmr->stored_name = xstrdup (file_name);
|
|
370 tmr->mapped_name = xstrdup (path);
|
|
371 tar_map_result = tmr;
|
|
372
|
|
373 return 0;
|
|
374
|
|
375 case '1': /* hard links; we just copy */
|
|
376 for (tmr = tar_map_result; tmr; tmr=tmr->next)
|
|
377 if (strcmp (tmr->stored_name, tar_header.linkname) == 0)
|
|
378 break;
|
|
379 if (!tmr)
|
|
380 {
|
|
381 fprintf (stderr, "tar: can't find %s to link %s to\n",
|
|
382 tar_header.linkname, path);
|
|
383 return 1;
|
|
384 }
|
|
385 vp (_tar_vfile, "H %s <- %s\n", path, tmr->mapped_name);
|
|
386 prepare_for_file (path);
|
|
387 copy = fopen (tmr->mapped_name, "rb");
|
|
388 if (!copy)
|
|
389 {
|
|
390 fprintf (stderr, "tar: unable to read %s\n", tmr->mapped_name);
|
|
391 return 1;
|
|
392 }
|
|
393 out = common_fopen (path);
|
|
394 if (!out)
|
|
395 return 1;
|
|
396
|
|
397 while ((got = fread (buf, 1, 512, copy)) > 0)
|
|
398 {
|
|
399 int put = fwrite (buf, 1, got, out);
|
|
400 if (put < got)
|
|
401 {
|
|
402 fprintf (stderr, "tar: out of disk space writing %s\n", path);
|
|
403 fclose (out);
|
|
404 fclose (copy);
|
|
405 remove (path);
|
|
406 return 1;
|
|
407 }
|
|
408 }
|
|
409 fclose (out);
|
|
410 fclose (copy);
|
|
411
|
|
412 fix_time_stamp (path);
|
|
413 return 0;
|
|
414
|
|
415 case '5': /* directories */
|
|
416 vp (_tar_vfile, "D %s\n", path);
|
|
417 while (path[0] && path[strlen (path)-1] == '/')
|
|
418 path[strlen (path) - 1] = 0;
|
|
419 return mkdir_p (1, path);
|
|
420
|
|
421
|
|
422 case '2': /* symbolic links */
|
|
423 vp (_tar_vfile, "L %s -> %s\n", path, tar_header.linkname);
|
|
424 prepare_for_file (path);
|
|
425 h = CreateFileA (path, GENERIC_WRITE, 0, 0, CREATE_NEW,
|
|
426 FILE_ATTRIBUTE_NORMAL, 0);
|
|
427 if (h == INVALID_HANDLE_VALUE)
|
|
428 {
|
|
429 fprintf (stderr, "error: unable to create symlink \"%s\" -> \"%s\"\n",
|
|
430 path, tar_header.linkname);
|
|
431 return 1;
|
|
432 }
|
|
433 strcpy (buf, SYMLINK_COOKIE);
|
|
434 strcat (buf, tar_header.linkname);
|
|
435 if (WriteFile (h, buf, strlen (buf) + 1, &w, NULL))
|
|
436 {
|
|
437 CloseHandle (h);
|
|
438 SetFileAttributesA (path, FILE_ATTRIBUTE_SYSTEM);
|
|
439 return 0;
|
|
440 }
|
|
441 CloseHandle (h);
|
|
442 fprintf (stderr, "error: unable to write symlink \"%s\"\n", path);
|
|
443 DeleteFileA (path);
|
|
444 return 1;
|
|
445 }
|
|
446
|
|
447 return 0;
|
|
448 }
|
|
449
|
|
450 int
|
|
451 tar_close ()
|
|
452 {
|
|
453 #if 0
|
|
454 while (tar_map_result)
|
|
455 {
|
|
456 tar_map_result_type *t = tar_map_result->next;
|
|
457 free (tar_map_result->stored_name);
|
|
458 free (tar_map_result->mapped_name);
|
|
459 free (tar_map_result);
|
|
460 tar_map_result = t;
|
|
461 }
|
|
462 #endif
|
|
463 tar_map_result = 0;
|
|
464
|
|
465 if (gzclose (g))
|
|
466 err ++;
|
|
467 return err; /* includes errors for skipped files, etc */
|
|
468 }
|
|
469
|
|
470 typedef struct {
|
|
471 char *from;
|
|
472 int from_len;
|
|
473 char *to;
|
|
474 int to_len;
|
|
475 } map_type;
|
|
476
|
|
477 static map_type *map;
|
|
478 static int nmaps;
|
|
479
|
|
480 int
|
|
481 tar_auto (char *pathname, char **maplist)
|
|
482 {
|
|
483 char *c;
|
|
484 int errcount = 0;
|
|
485 int i, j;
|
|
486 map_type mtemp;
|
|
487 char newname[_MAX_PATH+512];
|
|
488 static char twiddles[] = "|\b/\b-\b\\\b";
|
|
489 int t = 0;
|
|
490
|
|
491 for (nmaps=0; maplist[nmaps*2]; nmaps++) ;
|
|
492 map = (map_type *) malloc ((nmaps+1) * sizeof (map_type));
|
|
493 for (nmaps=0; maplist[nmaps*2]; nmaps++)
|
|
494 {
|
|
495 map[nmaps].from = maplist[nmaps*2];
|
|
496 map[nmaps].from_len = strlen (maplist[nmaps*2]);
|
|
497 map[nmaps].to = maplist[nmaps*2+1];
|
|
498 map[nmaps].to_len = strlen (maplist[nmaps*2+1]);
|
|
499 }
|
|
500 /* bubble sort - expect the maps to be short */
|
|
501 for (i=0; i<nmaps-1; i++)
|
|
502 for (j=i+1; j<nmaps; j++)
|
|
503 if (map[i].from_len < map[j].from_len)
|
|
504 {
|
|
505 mtemp = map[i];
|
|
506 map[i] = map[j];
|
|
507 map[j] = mtemp;
|
|
508 }
|
|
509
|
|
510 if ((tar_open (pathname)))
|
|
511 return 1;
|
452
|
512 while ((c = tar_next_file ()))
|
448
|
513 {
|
|
514 int l = strlen (c);
|
|
515 for (i=0; i<nmaps; i++)
|
|
516 if (l >= map[i].from_len
|
|
517 && strncmp (c, map[i].from, map[i].from_len) == 0)
|
|
518 {
|
|
519 strcpy (newname, map[i].to);
|
|
520 strcpy (newname+map[i].to_len, c + map[i].from_len);
|
|
521 c = newname;
|
|
522 break;
|
|
523 }
|
|
524
|
|
525 t = (t+2) % 8;
|
|
526 fwrite (twiddles+t, 1, 2, stderr);
|
|
527
|
|
528 if (tar_read_file (c))
|
|
529 errcount ++;
|
|
530 }
|
|
531 if (tar_close ())
|
|
532 errcount ++;
|
|
533
|
|
534 fwrite (" \b", 1, 2, stderr);
|
|
535
|
|
536 vp2 (_tar_vfile, "tar_auto returns %d\n", errcount);
|
|
537 return errcount;
|
|
538 }
|