Commit | Line | Data |
---|---|---|
8c4ebc23 JH |
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 | } |