Initial revision
[chise/xemacs-chise.git] / netinstall / tar.cc
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 }