Version 1.20.
[debian/tar] / src / gnu.c
1 /* GNU dump extensions to tar.
2    Copyright (C) 1988, 1992, 1993 Free Software Foundation
3
4 This file is part of GNU Tar.
5
6 GNU Tar is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Tar is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Tar; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #ifndef STDC_HEADERS
25 extern int errno;
26 #endif
27 #include <time.h>
28 time_t time ();
29
30 #include "tar.h"
31 #include "port.h"
32
33 #ifndef S_ISLNK
34 #define lstat stat
35 #endif
36
37 extern time_t new_time;
38 extern FILE *msg_file;
39
40 void addname ();
41 int check_exclude ();
42 extern PTR ck_malloc ();
43 extern PTR ck_realloc ();
44 int confirm ();
45 extern PTR init_buffer ();
46 extern char *get_buffer ();
47 int is_dot_or_dotdot ();
48 extern void add_buffer ();
49 extern void flush_buffer ();
50 void name_gather ();
51 int recursively_delete ();
52 void skip_file ();
53 char *un_quote_string ();
54
55 extern char *new_name ();
56
57 static void add_dir_name ();
58
59 struct dirname
60   {
61     struct dirname *next;
62     char *name;
63     char *dir_text;
64     int dev;
65     int ino;
66     int allnew;
67   };
68 static struct dirname *dir_list;
69 static time_t this_time;
70
71 void
72 add_dir (name, dev, ino, text)
73      char *name;
74      char *text;
75      dev_t dev;
76      ino_t ino;
77 {
78   struct dirname *dp;
79
80   dp = (struct dirname *) ck_malloc (sizeof (struct dirname));
81   if (!dp)
82     abort ();
83   dp->next = dir_list;
84   dir_list = dp;
85   dp->dev = dev;
86   dp->ino = ino;
87   dp->name = ck_malloc (strlen (name) + 1);
88   strcpy (dp->name, name);
89   dp->dir_text = text;
90   dp->allnew = 0;
91 }
92
93 void
94 read_dir_file ()
95 {
96   int dev;
97   int ino;
98   char *strp;
99   FILE *fp;
100   char buf[512];
101   static char *path = 0;
102
103   if (path == 0)
104     path = ck_malloc (PATH_MAX);
105   time (&this_time);
106   if (gnu_dumpfile[0] != '/')
107     {
108 #if defined(__MSDOS__) || defined(HAVE_GETCWD) || defined(_POSIX_VERSION)
109       if (!getcwd (path, PATH_MAX))
110         {
111           msg ("Couldn't get current directory.");
112           exit (EX_SYSTEM);
113         }
114 #else
115       char *getwd ();
116
117       if (!getwd (path))
118         {
119           msg ("Couldn't get current directory: %s", path);
120           exit (EX_SYSTEM);
121         }
122 #endif
123       /* If this doesn't fit, we're in serious trouble */
124       strcat (path, "/");
125       strcat (path, gnu_dumpfile);
126       gnu_dumpfile = path;
127     }
128   fp = fopen (gnu_dumpfile, "r");
129   if (fp == 0 && errno != ENOENT)
130     {
131       msg_perror ("Can't open %s", gnu_dumpfile);
132       return;
133     }
134   if (!fp)
135     return;
136   fgets (buf, sizeof (buf), fp);
137   if (!f_new_files)
138     {
139       f_new_files++;
140       new_time = atol (buf);
141     }
142   while (fgets (buf, sizeof (buf), fp))
143     {
144       strp = &buf[strlen (buf)];
145       if (strp[-1] == '\n')
146         strp[-1] = '\0';
147       strp = buf;
148       dev = atol (strp);
149       while (isdigit (*strp))
150         strp++;
151       ino = atol (strp);
152       while (isspace (*strp))
153         strp++;
154       while (isdigit (*strp))
155         strp++;
156       strp++;
157       add_dir (un_quote_string (strp), dev, ino, (char *) 0);
158     }
159   fclose (fp);
160 }
161
162 void
163 write_dir_file ()
164 {
165   FILE *fp;
166   struct dirname *dp;
167   char *str;
168   extern char *quote_copy_string ();
169
170   fp = fopen (gnu_dumpfile, "w");
171   if (fp == 0)
172     {
173       msg_perror ("Can't write to %s", gnu_dumpfile);
174       return;
175     }
176   fprintf (fp, "%lu\n", this_time);
177   for (dp = dir_list; dp; dp = dp->next)
178     {
179       if (!dp->dir_text)
180         continue;
181       str = quote_copy_string (dp->name);
182       if (str)
183         {
184           fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, str);
185           free (str);
186         }
187       else
188         fprintf (fp, "%u %u %s\n", dp->dev, dp->ino, dp->name);
189     }
190   fclose (fp);
191 }
192
193 struct dirname *
194 get_dir (name)
195      char *name;
196 {
197   struct dirname *dp;
198
199   for (dp = dir_list; dp; dp = dp->next)
200     {
201       if (!strcmp (dp->name, name))
202         return dp;
203     }
204   return 0;
205 }
206
207
208 /* Collect all the names from argv[] (or whatever), then expand them into
209    a directory tree, and put all the directories at the beginning. */
210 void
211 collect_and_sort_names ()
212 {
213   struct name *n, *n_next;
214   int num_names;
215   struct stat statbuf;
216   int name_cmp ();
217   char *merge_sort ();
218
219   name_gather ();
220
221   if (gnu_dumpfile)
222     read_dir_file ();
223   if (!namelist)
224     addname (".");
225   for (n = namelist; n; n = n_next)
226     {
227       n_next = n->next;
228       if (n->found || n->dir_contents)
229         continue;
230       if (n->regexp)            /* FIXME just skip regexps for now */
231         continue;
232       if (n->change_dir)
233         if (chdir (n->change_dir) < 0)
234           {
235             msg_perror ("can't chdir to %s", n->change_dir);
236             continue;
237           }
238
239 #ifdef AIX
240       if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK))
241 #else
242       if (lstat (n->name, &statbuf) < 0)
243 #endif /* AIX */
244         {
245           msg_perror ("can't stat %s", n->name);
246           continue;
247         }
248       if (S_ISDIR (statbuf.st_mode))
249         {
250           n->found++;
251           add_dir_name (n->name, statbuf.st_dev);
252         }
253     }
254
255   num_names = 0;
256   for (n = namelist; n; n = n->next)
257     num_names++;
258   namelist = (struct name *) merge_sort ((PTR) namelist, num_names, (char *) (&(namelist->next)) - (char *) namelist, name_cmp);
259
260   for (n = namelist; n; n = n->next)
261     {
262       n->found = 0;
263     }
264   if (gnu_dumpfile)
265     write_dir_file ();
266 }
267
268 int
269 name_cmp (n1, n2)
270      struct name *n1, *n2;
271 {
272   if (n1->found)
273     {
274       if (n2->found)
275         return strcmp (n1->name, n2->name);
276       else
277         return -1;
278     }
279   else if (n2->found)
280     return 1;
281   else
282     return strcmp (n1->name, n2->name);
283 }
284
285 int
286 dirent_cmp (p1, p2)
287      const PTR p1;
288      const PTR p2;
289 {
290   char *frst, *scnd;
291
292   frst = (*(char **) p1) + 1;
293   scnd = (*(char **) p2) + 1;
294
295   return strcmp (frst, scnd);
296 }
297
298 char *
299 get_dir_contents (p, device)
300      char *p;
301      int device;
302 {
303   DIR *dirp;
304   register struct dirent *d;
305   char *new_buf;
306   char *namebuf;
307   int bufsiz;
308   int len;
309   PTR the_buffer;
310   char *buf;
311   size_t n_strs;
312   /*    int n_size;*/
313   char *p_buf;
314   char **vec, **p_vec;
315
316   extern int errno;
317
318   errno = 0;
319   dirp = opendir (p);
320   bufsiz = strlen (p) + NAMSIZ;
321   namebuf = ck_malloc (bufsiz + 2);
322   if (!dirp)
323     {
324       if (errno)
325         msg_perror ("can't open directory %s", p);
326       else
327         msg ("error opening directory %s", p);
328       new_buf = NULL;
329     }
330   else
331     {
332       struct dirname *dp;
333       int all_children;
334
335       dp = get_dir (p);
336       all_children = dp ? dp->allnew : 0;
337       (void) strcpy (namebuf, p);
338       if (p[strlen (p) - 1] != '/')
339         (void) strcat (namebuf, "/");
340       len = strlen (namebuf);
341
342       the_buffer = init_buffer ();
343       while (d = readdir (dirp))
344         {
345           struct stat hs;
346
347           /* Skip . and .. */
348           if (is_dot_or_dotdot (d->d_name))
349             continue;
350           if (NLENGTH (d) + len >= bufsiz)
351             {
352               bufsiz += NAMSIZ;
353               namebuf = ck_realloc (namebuf, bufsiz + 2);
354             }
355           (void) strcpy (namebuf + len, d->d_name);
356 #ifdef AIX
357           if (0 != f_follow_links ?
358               statx (namebuf, &hs, STATSIZE, STX_HIDDEN) :
359               statx (namebuf, &hs, STATSIZE, STX_HIDDEN | STX_LINK))
360 #else
361           if (0 != f_follow_links ? stat (namebuf, &hs) : lstat (namebuf, &hs))
362 #endif
363             {
364               msg_perror ("can't stat %s", namebuf);
365               continue;
366             }
367           if ((f_local_filesys && device != hs.st_dev)
368               || (f_exclude && check_exclude (namebuf)))
369             add_buffer (the_buffer, "N", 1);
370 #ifdef AIX
371           else if (S_ISHIDDEN (hs.st_mode))
372             {
373               add_buffer (the_buffer, "D", 1);
374               strcat (d->d_name, "A");
375               d->d_namlen++;
376             }
377 #endif /* AIX */
378           else if (S_ISDIR (hs.st_mode))
379             {
380               if (dp = get_dir (namebuf))
381                 {
382                   if (dp->dev != hs.st_dev
383                       || dp->ino != hs.st_ino)
384                     {
385                       if (f_verbose)
386                         msg ("directory %s has been renamed.", namebuf);
387                       dp->allnew = 1;
388                       dp->dev = hs.st_dev;
389                       dp->ino = hs.st_ino;
390                     }
391                   dp->dir_text = "";
392                 }
393               else
394                 {
395                   if (f_verbose)
396                     msg ("Directory %s is new", namebuf);
397                   add_dir (namebuf, hs.st_dev, hs.st_ino, "");
398                   dp = get_dir (namebuf);
399                   dp->allnew = 1;
400                 }
401               if (all_children)
402                 dp->allnew = 1;
403
404               add_buffer (the_buffer, "D", 1);
405             }
406           else if (!all_children
407                    && f_new_files
408                    && new_time > hs.st_mtime
409                    && (f_new_files > 1
410                        || new_time > hs.st_ctime))
411             add_buffer (the_buffer, "N", 1);
412           else
413             add_buffer (the_buffer, "Y", 1);
414           add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
415         }
416       add_buffer (the_buffer, "\000\000", 2);
417       closedir (dirp);
418
419       /* Well, we've read in the contents of the dir, now sort them */
420       buf = get_buffer (the_buffer);
421       if (buf[0] == '\0')
422         {
423           flush_buffer (the_buffer);
424           new_buf = NULL;
425         }
426       else
427         {
428           n_strs = 0;
429           for (p_buf = buf; *p_buf;)
430             {
431               int tmp;
432
433               tmp = strlen (p_buf) + 1;
434               n_strs++;
435               p_buf += tmp;
436             }
437           vec = (char **) ck_malloc (sizeof (char *) * (n_strs + 1));
438           for (p_vec = vec, p_buf = buf; *p_buf; p_buf += strlen (p_buf) + 1)
439             *p_vec++ = p_buf;
440           *p_vec = 0;
441           qsort ((PTR) vec, n_strs, sizeof (char *), dirent_cmp);
442           new_buf = (char *) ck_malloc (p_buf - buf + 2);
443           for (p_vec = vec, p_buf = new_buf; *p_vec; p_vec++)
444             {
445               char *p_tmp;
446
447               for (p_tmp = *p_vec; *p_buf++ = *p_tmp++;)
448                 ;
449             }
450           *p_buf++ = '\0';
451           free (vec);
452           flush_buffer (the_buffer);
453         }
454     }
455   free (namebuf);
456   return new_buf;
457 }
458
459 /* p is a directory.  Add all the files in P to the namelist.  If any of the
460    files is a directory, recurse on the subdirectory. . . */
461 static void
462 add_dir_name (p, device)
463      char *p;
464      int device;
465 {
466   char *new_buf;
467   char *p_buf;
468
469   char *namebuf;
470   int buflen;
471   register int len;
472   int sublen;
473
474   /*    PTR the_buffer;*/
475
476   /*    char *buf;*/
477   /*    char **vec,**p_vec;*/
478   /*    int n_strs,n_size;*/
479
480   struct name *n;
481
482   int dirent_cmp ();
483
484   new_buf = get_dir_contents (p, device);
485
486   for (n = namelist; n; n = n->next)
487     {
488       if (!strcmp (n->name, p))
489         {
490           n->dir_contents = new_buf ? new_buf : "\0\0\0\0";
491           break;
492         }
493     }
494
495   if (new_buf)
496     {
497       len = strlen (p);
498       buflen = NAMSIZ <= len ? len + NAMSIZ : NAMSIZ;
499       namebuf = ck_malloc (buflen + 1);
500
501       (void) strcpy (namebuf, p);
502       if (namebuf[len - 1] != '/')
503         {
504           namebuf[len++] = '/';
505           namebuf[len] = '\0';
506         }
507       for (p_buf = new_buf; *p_buf; p_buf += sublen + 1)
508         {
509           sublen = strlen (p_buf);
510           if (*p_buf == 'D')
511             {
512               if (len + sublen >= buflen)
513                 {
514                   buflen += NAMSIZ;
515                   namebuf = ck_realloc (namebuf, buflen + 1);
516                 }
517               (void) strcpy (namebuf + len, p_buf + 1);
518               addname (namebuf);
519               add_dir_name (namebuf, device);
520             }
521         }
522       free (namebuf);
523     }
524 }
525
526 /* Returns non-zero if p is . or ..   This could be a macro for speed. */
527 int
528 is_dot_or_dotdot (p)
529      char *p;
530 {
531   return (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')));
532 }
533
534
535
536
537
538
539 void
540 gnu_restore (skipcrud)
541      int skipcrud;
542 {
543   char *current_dir;
544   /*    int current_dir_length; */
545
546   char *archive_dir;
547   /*    int archive_dir_length; */
548   PTR the_buffer;
549   char *p;
550   DIR *dirp;
551   struct dirent *d;
552   char *cur, *arc;
553   extern struct stat hstat;     /* Stat struct corresponding */
554   long size, copied;
555   char *from, *to;
556   extern union record *head;
557
558   dirp = opendir (skipcrud + current_file_name);
559
560   if (!dirp)
561     {
562       /* The directory doesn't exist now.  It'll be created.
563                            In any case, we don't have to delete any files out
564                            of it */
565       skip_file ((long) hstat.st_size);
566       return;
567     }
568
569   the_buffer = init_buffer ();
570   while (d = readdir (dirp))
571     {
572       if (is_dot_or_dotdot (d->d_name))
573         continue;
574
575       add_buffer (the_buffer, d->d_name, (int) (NLENGTH (d) + 1));
576     }
577   closedir (dirp);
578   add_buffer (the_buffer, "", 1);
579
580   current_dir = get_buffer (the_buffer);
581   archive_dir = (char *) ck_malloc (hstat.st_size);
582   if (archive_dir == 0)
583     {
584       msg ("Can't allocate %d bytes for restore", hstat.st_size);
585       skip_file ((long) hstat.st_size);
586       return;
587     }
588   to = archive_dir;
589   for (size = hstat.st_size; size > 0; size -= copied)
590     {
591       from = findrec ()->charptr;
592       if (!from)
593         {
594           msg ("Unexpected EOF in archive\n");
595           break;
596         }
597       copied = endofrecs ()->charptr - from;
598       if (copied > size)
599         copied = size;
600       bcopy ((PTR) from, (PTR) to, (int) copied);
601       to += copied;
602       userec ((union record *) (from + copied - 1));
603     }
604
605   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
606     {
607       for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
608         {
609           arc++;
610           if (!strcmp (arc, cur))
611             break;
612         }
613       if (*arc == '\0')
614         {
615           p = new_name (skipcrud + current_file_name, cur);
616           if (f_confirm && !confirm ("delete", p))
617             {
618               free (p);
619               continue;
620             }
621           if (f_verbose)
622             fprintf (msg_file, "%s: deleting %s\n", tar, p);
623           if (recursively_delete (p))
624             {
625               msg ("%s: Error while deleting %s\n", tar, p);
626             }
627           free (p);
628         }
629
630     }
631   flush_buffer (the_buffer);
632   free (archive_dir);
633 }
634
635 int
636 recursively_delete (path)
637      char *path;
638 {
639   struct stat sbuf;
640   DIR *dirp;
641   struct dirent *dp;
642   char *path_buf;
643   /* int path_len; */
644
645
646   if (lstat (path, &sbuf) < 0)
647     return 1;
648   if (S_ISDIR (sbuf.st_mode))
649     {
650
651       /* path_len=strlen(path); */
652       dirp = opendir (path);
653       if (dirp == 0)
654         return 1;
655       while (dp = readdir (dirp))
656         {
657           if (is_dot_or_dotdot (dp->d_name))
658             continue;
659           path_buf = new_name (path, dp->d_name);
660           if (recursively_delete (path_buf))
661             {
662               free (path_buf);
663               closedir (dirp);
664               return 1;
665             }
666           free (path_buf);
667         }
668       closedir (dirp);
669
670       if (rmdir (path) < 0)
671         return 1;
672       return 0;
673     }
674   if (unlink (path) < 0)
675     return 1;
676   return 0;
677 }