comparison netinstall/tar.cc @ 448:3078fd1074e8 r21-2-39

Import from CVS: tag r21-2-39
author cvs
date Mon, 13 Aug 2007 11:38:25 +0200
parents
children 3d3049ae1304
comparison
equal deleted inserted replaced
447:4fc5f13f3bd3 448:3078fd1074e8
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
119 int
120 tar_ftell ()
121 {
122 return gztell (g);
123 }
124
125 static void
126 skip_file ()
127 {
128 while (file_length > 0)
129 {
130 gzread (g, buf, 512);
131 file_length -= 512;
132 }
133 }
134
135 char *
136 tar_next_file ()
137 {
138 int r, n;
139 char *c;
140 r = gzread (g, &tar_header, 512);
141
142 /* See if we're at end of file */
143 if (r != 512)
144 return 0;
145
146 /* See if the header is all zeros (i.e. last block) */
147 n = 0;
148 for (r = 512/sizeof (int); r; r--)
149 n |= ((int *)&tar_header)[r-1];
150 if (n == 0)
151 return 0;
152
153 if (!have_longname && tar_header.typeflag != 'L')
154 {
155 memcpy (file_name, tar_header.name, 100);
156 file_name[100] = 0;
157 }
158
159 sscanf (tar_header.size, "%o", &file_length);
160
161 vp2 (_tar_vfile, "%c %9d %s\n", tar_header.typeflag, file_length, file_name);
162
163 switch (tar_header.typeflag)
164 {
165 case 'L': /* GNU tar long name extension */
166 if (file_length > _MAX_PATH)
167 {
168 skip_file ();
169 fprintf (stderr, "error: long file name exceeds %d characters\n",
170 _MAX_PATH);
171 err ++;
172 gzread (g, &tar_header, 512);
173 sscanf (tar_header.size, "%o", &file_length);
174 skip_file ();
175 return tar_next_file ();
176 }
177 c = file_name;
178 while (file_length > 0)
179 {
180 int need = file_length > 512 ? 512 : file_length;
181 if (gzread (g, buf, 512) < 512)
182 return 0;
183 memcpy (c, buf, need);
184 c += need;
185 file_length -= need;
186 }
187 *c = 0;
188 have_longname = 1;
189 return tar_next_file ();
190
191 case '3': /* char */
192 case '4': /* block */
193 case '6': /* fifo */
194 fprintf (stderr, "warning: not extracting special file %s\n",
195 file_name);
196 err ++;
197 return tar_next_file ();
198
199 case '0': /* regular file */
200 case 0: /* regular file also */
201 case '2': /* symbolic link */
202 case '5': /* directory */
203 case '7': /* contiguous file */
204 return file_name;
205
206 case '1': /* hard link, we just copy */
207 return file_name;
208
209 default:
210 fprintf (stderr, "error: unknown (or unsupported) file type `%c'\n",
211 tar_header.typeflag);
212 err ++;
213 skip_file ();
214 return tar_next_file ();
215 }
216 }
217
218 static void
219 fix_time_stamp (char *path)
220 {
221 int mtime;
222 #if defined(CYGWIN) || defined(MINGW)
223 long long ftimev;
224 #else
225 __int64 ftimev;
226 #endif
227 FILETIME ftime;
228 HANDLE h;
229
230 sscanf (tar_header.mtime, "%o", &mtime);
231 ftimev = mtime * NSPERSEC + FACTOR;
232 ftime.dwHighDateTime = ftimev >> 32;
233 ftime.dwLowDateTime = ftimev;
234 h = CreateFileA (path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
235 0, OPEN_EXISTING,
236 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
237 if (h)
238 {
239 SetFileTime (h, 0, 0, &ftime);
240 CloseHandle (h);
241 }
242 }
243
244 static FILE *
245 common_fopen (char *path)
246 {
247 FILE *out;
248 out = fopen (path, "wb");
249 if (!out)
250 {
251 /* maybe we need to create a directory */
252 if (mkdir_p (0, path))
253 {
254 skip_file ();
255 return 0;
256 }
257 out = fopen (path, "wb");
258 }
259 if (!out)
260 {
261 fprintf (stderr, "unable to write to file %s\n", path);
262 perror ("The error was");
263 skip_file ();
264 return 0;
265 }
266 return out;
267 }
268
269 static void
270 prepare_for_file (char *path)
271 {
272 DWORD w;
273 mkdir_p (0, path);
274
275 w = GetFileAttributes (path);
276 if (w != 0xffffffff && w & FILE_ATTRIBUTE_DIRECTORY)
277 {
278 char *tmp = (char *) malloc (strlen (path) + 10);
279 int i = 0;
280 do {
281 i++;
282 sprintf (tmp, "%s.old-%d", path, i);
283 } while (GetFileAttributes (tmp) != 0xffffffff);
284 fprintf (stderr, "warning: moving directory \"%s\" out of the way.\n", path);
285 MoveFile (path, tmp);
286 free (tmp);
287 }
288
289 DeleteFileA (path);
290 }
291
292 int
293 tar_read_file (char *path)
294 {
295 FILE *out, *copy;
296 HANDLE h;
297 DWORD w;
298 int got;
299 tar_map_result_type *tmr;
300
301 switch (tar_header.typeflag)
302 {
303 case '0': /* regular files */
304 case 0:
305 case '7':
306 vp (_tar_vfile, "F %s\n", path);
307 prepare_for_file (path);
308 out = common_fopen (path);
309 if (!out)
310 return 1;
311
312 while (file_length > 0)
313 {
314 int put;
315 int want = file_length > 512 ? 512 : file_length;
316 got = gzread (g, buf, 512);
317 if (got < 512)
318 {
319 fprintf (stderr, "tar: unexpected end of file reading %s\n", path);
320 fclose (out);
321 remove (path);
322 return 1;
323 }
324 put = fwrite (buf, 1, want, out);
325 if (put < want)
326 {
327 fprintf (stderr, "tar: out of disk space writing %s\n", path);
328 fclose (out);
329 remove (path);
330 skip_file ();
331 return 1;
332 }
333 file_length -= want;
334 }
335 fclose (out);
336
337 fix_time_stamp (path);
338
339 /* we need this to do hard links below */
340 tmr = (tar_map_result_type *) malloc (sizeof (tar_map_result_type));
341 tmr->next = tar_map_result;
342 tmr->stored_name = xstrdup (file_name);
343 tmr->mapped_name = xstrdup (path);
344 tar_map_result = tmr;
345
346 return 0;
347
348 case '1': /* hard links; we just copy */
349 for (tmr = tar_map_result; tmr; tmr=tmr->next)
350 if (strcmp (tmr->stored_name, tar_header.linkname) == 0)
351 break;
352 if (!tmr)
353 {
354 fprintf (stderr, "tar: can't find %s to link %s to\n",
355 tar_header.linkname, path);
356 return 1;
357 }
358 vp (_tar_vfile, "H %s <- %s\n", path, tmr->mapped_name);
359 prepare_for_file (path);
360 copy = fopen (tmr->mapped_name, "rb");
361 if (!copy)
362 {
363 fprintf (stderr, "tar: unable to read %s\n", tmr->mapped_name);
364 return 1;
365 }
366 out = common_fopen (path);
367 if (!out)
368 return 1;
369
370 while ((got = fread (buf, 1, 512, copy)) > 0)
371 {
372 int put = fwrite (buf, 1, got, out);
373 if (put < got)
374 {
375 fprintf (stderr, "tar: out of disk space writing %s\n", path);
376 fclose (out);
377 fclose (copy);
378 remove (path);
379 return 1;
380 }
381 }
382 fclose (out);
383 fclose (copy);
384
385 fix_time_stamp (path);
386 return 0;
387
388 case '5': /* directories */
389 vp (_tar_vfile, "D %s\n", path);
390 while (path[0] && path[strlen (path)-1] == '/')
391 path[strlen (path) - 1] = 0;
392 return mkdir_p (1, path);
393
394
395 case '2': /* symbolic links */
396 vp (_tar_vfile, "L %s -> %s\n", path, tar_header.linkname);
397 prepare_for_file (path);
398 h = CreateFileA (path, GENERIC_WRITE, 0, 0, CREATE_NEW,
399 FILE_ATTRIBUTE_NORMAL, 0);
400 if (h == INVALID_HANDLE_VALUE)
401 {
402 fprintf (stderr, "error: unable to create symlink \"%s\" -> \"%s\"\n",
403 path, tar_header.linkname);
404 return 1;
405 }
406 strcpy (buf, SYMLINK_COOKIE);
407 strcat (buf, tar_header.linkname);
408 if (WriteFile (h, buf, strlen (buf) + 1, &w, NULL))
409 {
410 CloseHandle (h);
411 SetFileAttributesA (path, FILE_ATTRIBUTE_SYSTEM);
412 return 0;
413 }
414 CloseHandle (h);
415 fprintf (stderr, "error: unable to write symlink \"%s\"\n", path);
416 DeleteFileA (path);
417 return 1;
418 }
419
420 return 0;
421 }
422
423 int
424 tar_close ()
425 {
426 #if 0
427 while (tar_map_result)
428 {
429 tar_map_result_type *t = tar_map_result->next;
430 free (tar_map_result->stored_name);
431 free (tar_map_result->mapped_name);
432 free (tar_map_result);
433 tar_map_result = t;
434 }
435 #endif
436 tar_map_result = 0;
437
438 if (gzclose (g))
439 err ++;
440 return err; /* includes errors for skipped files, etc */
441 }
442
443 typedef struct {
444 char *from;
445 int from_len;
446 char *to;
447 int to_len;
448 } map_type;
449
450 static map_type *map;
451 static int nmaps;
452
453 int
454 tar_auto (char *pathname, char **maplist)
455 {
456 char *c;
457 int errcount = 0;
458 int i, j;
459 map_type mtemp;
460 char newname[_MAX_PATH+512];
461 static char twiddles[] = "|\b/\b-\b\\\b";
462 int t = 0;
463
464 for (nmaps=0; maplist[nmaps*2]; nmaps++) ;
465 map = (map_type *) malloc ((nmaps+1) * sizeof (map_type));
466 for (nmaps=0; maplist[nmaps*2]; nmaps++)
467 {
468 map[nmaps].from = maplist[nmaps*2];
469 map[nmaps].from_len = strlen (maplist[nmaps*2]);
470 map[nmaps].to = maplist[nmaps*2+1];
471 map[nmaps].to_len = strlen (maplist[nmaps*2+1]);
472 }
473 /* bubble sort - expect the maps to be short */
474 for (i=0; i<nmaps-1; i++)
475 for (j=i+1; j<nmaps; j++)
476 if (map[i].from_len < map[j].from_len)
477 {
478 mtemp = map[i];
479 map[i] = map[j];
480 map[j] = mtemp;
481 }
482
483 if ((tar_open (pathname)))
484 return 1;
485 while (c = tar_next_file ())
486 {
487 int l = strlen (c);
488 for (i=0; i<nmaps; i++)
489 if (l >= map[i].from_len
490 && strncmp (c, map[i].from, map[i].from_len) == 0)
491 {
492 strcpy (newname, map[i].to);
493 strcpy (newname+map[i].to_len, c + map[i].from_len);
494 c = newname;
495 break;
496 }
497
498 t = (t+2) % 8;
499 fwrite (twiddles+t, 1, 2, stderr);
500
501 if (tar_read_file (c))
502 errcount ++;
503 }
504 if (tar_close ())
505 errcount ++;
506
507 fwrite (" \b", 1, 2, stderr);
508
509 vp2 (_tar_vfile, "tar_auto returns %d\n", errcount);
510 return errcount;
511 }