Mercurial > hg > xemacs-beta
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 } |