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