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