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