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 }