]> Dogcows Code - chaz/tar/blob - src/incremen.c
GNU tar 1.12
[chaz/tar] / src / incremen.c
1 /* GNU dump extensions to tar.
2 Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 59 Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18 #include "system.h"
19
20 #include <time.h>
21 time_t time ();
22
23 #define ISDIGIT(Char) (ISASCII (Char) && isdigit (Char))
24 #define ISSPACE(Char) (ISASCII (Char) && isspace (Char))
25
26 #include "common.h"
27 \f
28 /* Variable sized generic character buffers. */
29
30 struct accumulator
31 {
32 int allocated;
33 int length;
34 char *pointer;
35 };
36
37 /* Amount of space guaranteed just after a reallocation. */
38 #define ACCUMULATOR_SLACK 50
39
40 /*---------------------------------------------------------.
41 | Return the accumulated data from an ACCUMULATOR buffer. |
42 `---------------------------------------------------------*/
43
44 static char *
45 get_accumulator (struct accumulator *accumulator)
46 {
47 return accumulator->pointer;
48 }
49
50 /*-----------------------------------------------.
51 | Allocate and return a new accumulator buffer. |
52 `-----------------------------------------------*/
53
54 static struct accumulator *
55 new_accumulator (void)
56 {
57 struct accumulator *accumulator
58 = (struct accumulator *) xmalloc (sizeof (struct accumulator));
59
60 accumulator->allocated = ACCUMULATOR_SLACK;
61 accumulator->pointer = (char *) xmalloc (ACCUMULATOR_SLACK);
62 accumulator->length = 0;
63 return accumulator;
64 }
65
66 /*-----------------------------------.
67 | Deallocate an ACCUMULATOR buffer. |
68 `-----------------------------------*/
69
70 static void
71 delete_accumulator (struct accumulator *accumulator)
72 {
73 free (accumulator->pointer);
74 free (accumulator);
75 }
76
77 /*----------------------------------------------------------------------.
78 | At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes. |
79 `----------------------------------------------------------------------*/
80
81 static void
82 add_to_accumulator (struct accumulator *accumulator,
83 const char *data, int size)
84 {
85 if (accumulator->length + size > accumulator->allocated)
86 {
87 accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
88 accumulator->pointer = (char *)
89 xrealloc (accumulator->pointer, (size_t) accumulator->allocated);
90 }
91 memcpy (accumulator->pointer + accumulator->length, data, (size_t) size);
92 accumulator->length += size;
93 }
94 \f
95 /* Incremental dump specialities. */
96
97 /* Current time. */
98 static time_t time_now;
99
100 /* List of directory names. */
101 struct directory
102 {
103 struct directory *next; /* next entry in list */
104 const char *name; /* path name of directory */
105 int device_number; /* device number for directory */
106 int inode_number; /* inode number for directory */
107 int allnew;
108 const char *dir_text;
109 };
110 static struct directory *directory_list = NULL;
111
112 /*-------------------------------------------------------------------.
113 | Create and link a new directory entry for directory NAME, having a |
114 | DEVICE_NUMBER and a INODE_NUMBER, with some TEXT. |
115 `-------------------------------------------------------------------*/
116
117 static void
118 note_directory (char *name, dev_t device_number, ino_t inode_number,
119 const char *text)
120 {
121 struct directory *directory
122 = (struct directory *) xmalloc (sizeof (struct directory));
123
124 directory->next = directory_list;
125 directory_list = directory;
126
127 directory->device_number = device_number;
128 directory->inode_number = inode_number;
129 directory->name = xstrdup (name);
130 directory->dir_text = text;
131 directory->allnew = 0;
132 }
133
134 /*------------------------------------------------------------------------.
135 | Return a directory entry for a given path NAME, or NULL if none found. |
136 `------------------------------------------------------------------------*/
137
138 static struct directory *
139 find_directory (char *name)
140 {
141 struct directory *directory;
142
143 for (directory = directory_list;
144 directory;
145 directory = directory->next)
146 {
147 if (!strcmp (directory->name, name))
148 return directory;
149 }
150 return NULL;
151 }
152
153 /*---.
154 | ? |
155 `---*/
156
157 static int
158 compare_dirents (const voidstar first, const voidstar second)
159 {
160 return strcmp ((*(char *const *) first) + 1,
161 (*(char *const *) second) + 1);
162 }
163
164 /*---.
165 | ? |
166 `---*/
167
168 char *
169 get_directory_contents (char *path, int device)
170 {
171 struct accumulator *accumulator;
172
173 /* Recursively scan the given PATH. */
174
175 {
176 DIR *dirp = opendir (path); /* for scanning directory */
177 struct dirent *entry; /* directory entry being scanned */
178 char *name_buffer; /* directory, `/', and directory member */
179 int name_buffer_size; /* allocated size of name_buffer, minus 2 */
180 int name_length; /* used length in name_buffer */
181 struct directory *directory; /* for checking if already already seen */
182 int all_children;
183
184 if (dirp == NULL)
185 {
186 ERROR ((0, errno, _("Cannot open directory %s"), path));
187 return NULL;
188 }
189 errno = 0; /* FIXME: errno should be read-only */
190
191 name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
192 name_buffer = xmalloc ((size_t) (name_buffer_size + 2));
193 strcpy (name_buffer, path);
194 if (path[strlen (path) - 1] != '/')
195 strcat (name_buffer, "/");
196 name_length = strlen (name_buffer);
197
198 directory = find_directory (path);
199 all_children = directory ? directory->allnew : 0;
200
201 accumulator = new_accumulator ();
202
203 while (entry = readdir (dirp), entry)
204 {
205 struct stat stat_data;
206
207 /* Skip `.' and `..'. */
208
209 if (is_dot_or_dotdot (entry->d_name))
210 continue;
211
212 if ((int) NAMLEN (entry) + name_length >= name_buffer_size)
213 {
214 while ((int) NAMLEN (entry) + name_length >= name_buffer_size)
215 name_buffer_size += NAME_FIELD_SIZE;
216 name_buffer = (char *)
217 xrealloc (name_buffer, (size_t) (name_buffer_size + 2));
218 }
219 strcpy (name_buffer + name_length, entry->d_name);
220
221 if (dereference_option
222 #ifdef AIX
223 ? statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN)
224 : statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN | STX_LINK)
225 #else
226 ? stat (name_buffer, &stat_data)
227 : lstat (name_buffer, &stat_data)
228 #endif
229 )
230 {
231 ERROR ((0, errno, _("Cannot stat %s"), name_buffer));
232 continue;
233 }
234
235 if ((one_file_system_option && device != stat_data.st_dev)
236 || (exclude_option && check_exclude (name_buffer)))
237 add_to_accumulator (accumulator, "N", 1);
238
239 #ifdef AIX
240 else if (S_ISHIDDEN (stat_data.st_mode))
241 {
242 add_to_accumulator (accumulator, "D", 1);
243 strcat (entry->d_name, "A");
244 entry->d_namlen++;
245 }
246 #endif
247
248 else if (S_ISDIR (stat_data.st_mode))
249 {
250 if (directory = find_directory (name_buffer), directory)
251 {
252 /* Devices having the high bit set are NFS devices, which are
253 attributed somewhat randomly in automounting situations.
254 For avoiding spurious incremental redumping of directories,
255 we have to plainly consider all NFS devices as equal,
256 relying on the i-node only to establish differences. */
257
258 /* FIXME: Göran Uddeborg <goeran@uddeborg.pp.se> says, on
259 1996-09-20, that SunOS 5/Solaris 2 uses unsigned long for
260 the device number type. */
261
262 if ((((short) directory->device_number >= 0
263 || (short) stat_data.st_dev >= 0)
264 && directory->device_number != stat_data.st_dev)
265 || directory->inode_number != stat_data.st_ino)
266 {
267 if (verbose_option)
268 WARN ((0, 0, _("Directory %s has been renamed"),
269 name_buffer));
270 directory->allnew = 1;
271 directory->device_number = stat_data.st_dev;
272 directory->inode_number = stat_data.st_ino;
273 }
274 directory->dir_text = "";
275 }
276 else
277 {
278 if (verbose_option)
279 WARN ((0, 0, _("Directory %s is new"), name_buffer));
280 note_directory (name_buffer, stat_data.st_dev, stat_data.st_ino,
281 "");
282 directory = find_directory (name_buffer);
283 directory->allnew = 1;
284 }
285 if (all_children && directory)
286 directory->allnew = 1;
287
288 add_to_accumulator (accumulator, "D", 1);
289 }
290
291 else
292 if (!all_children
293 && stat_data.st_mtime < newer_mtime_option
294 && (!after_date_option
295 || stat_data.st_ctime < newer_ctime_option))
296 add_to_accumulator (accumulator, "N", 1);
297 else
298 add_to_accumulator (accumulator, "Y", 1);
299
300 add_to_accumulator (accumulator,
301 entry->d_name, (int) (NAMLEN (entry) + 1));
302 }
303 add_to_accumulator (accumulator, "\000\000", 2);
304
305 free (name_buffer);
306 closedir (dirp);
307 }
308
309 /* Sort the contents of the directory, now that we have it all. */
310
311 {
312 char *pointer = get_accumulator (accumulator);
313 size_t counter;
314 char *cursor;
315 char *buffer;
316 char **array;
317 char **array_cursor;
318
319 counter = 0;
320 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
321 counter++;
322
323 if (counter == 0)
324 {
325 delete_accumulator (accumulator);
326 return NULL;
327 }
328
329 array = (char **) xmalloc (sizeof (char *) * (counter + 1));
330
331 array_cursor = array;
332 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
333 *array_cursor++ = cursor;
334 *array_cursor = NULL;
335
336 qsort ((voidstar) array, counter, sizeof (char *), compare_dirents);
337
338 buffer = (char *) xmalloc ((size_t) (cursor - pointer + 2));
339
340 cursor = buffer;
341 for (array_cursor = array; *array_cursor; array_cursor++)
342 {
343 char *string = *array_cursor;
344
345 while ((*cursor++ = *string++))
346 continue;
347 }
348 *cursor = '\0';
349
350 delete_accumulator (accumulator);
351 free (array);
352 return buffer;
353 }
354 }
355
356 /*----------------------------------------------------------------------.
357 | Add all the files in PATH, which is a directory, to the namelist. If |
358 | any of the files is a directory, recurse on the subdirectory. |
359 `----------------------------------------------------------------------*/
360
361 static void
362 add_hierarchy_to_namelist (char *path, int device)
363 {
364 char *buffer = get_directory_contents (path, device);
365
366 {
367 struct name *name;
368
369 for (name = namelist; name; name = name->next)
370 if (strcmp (name->name, path) == 0)
371 break;
372 if (name)
373 name->dir_contents = buffer ? buffer : "\0\0\0\0";
374 }
375
376 if (buffer)
377 {
378 int name_length = strlen (path);
379 int allocated_length = (name_length >= NAME_FIELD_SIZE
380 ? name_length + NAME_FIELD_SIZE
381 : NAME_FIELD_SIZE);
382 char *name_buffer = xmalloc ((size_t) (allocated_length + 1));
383 /* FIXME: + 2 above? */
384 char *string;
385 int string_length;
386
387 strcpy (name_buffer, path);
388 if (name_buffer[name_length - 1] != '/')
389 {
390 name_buffer[name_length++] = '/';
391 name_buffer[name_length] = '\0';
392 }
393
394 for (string = buffer; *string; string += string_length + 1)
395 {
396 string_length = strlen (string);
397 if (*string == 'D')
398 {
399 if (name_length + string_length >= allocated_length)
400 {
401 while (name_length + string_length >= allocated_length)
402 allocated_length += NAME_FIELD_SIZE;
403 name_buffer = (char *)
404 xrealloc (name_buffer, (size_t) (allocated_length + 1));
405 }
406 strcpy (name_buffer + name_length, string + 1);
407 addname (name_buffer);
408 add_hierarchy_to_namelist (name_buffer, device);
409 }
410 }
411
412 free (name_buffer);
413 }
414 }
415 \f
416 /*---.
417 | ? |
418 `---*/
419
420 static void
421 read_directory_file (void)
422 {
423 dev_t device_number;
424 ino_t inode_number;
425 char *strp;
426 FILE *fp;
427 char buf[512];
428 static char *path = NULL;
429
430 if (path == NULL)
431 path = xmalloc (PATH_MAX);
432 time (&time_now);
433 if (listed_incremental_option[0] != '/')
434 {
435 #if HAVE_GETCWD
436 if (!getcwd (path, PATH_MAX))
437 FATAL_ERROR ((0, 0, _("Could not get current directory")));
438 #else
439 char *getwd ();
440
441 if (!getwd (path))
442 FATAL_ERROR ((0, 0, _("Could not get current directory: %s"), path));
443 #endif
444
445 if (strlen (path) + 1 + strlen (listed_incremental_option) + 1 > PATH_MAX)
446 ERROR ((TAREXIT_FAILURE, 0, _("File name %s/%s too long"),
447 path, listed_incremental_option));
448
449 strcat (path, "/");
450 strcat (path, listed_incremental_option);
451 listed_incremental_option = path;
452 }
453 fp = fopen (listed_incremental_option, "r");
454 if (fp == 0 && errno != ENOENT)
455 {
456 ERROR ((0, errno, _("Cannot open %s"), listed_incremental_option));
457 return;
458 }
459 if (!fp)
460 return;
461 fgets (buf, sizeof (buf), fp);
462
463 /* FIXME: Using after_date_option as a first time flag looks fairly
464 dubious to me! So, using -N with incremental might be buggy just
465 because of the next few lines. I saw a few unexplained, almost harsh
466 advices, from other GNU people, about *not* using -N with incremental
467 dumps, and here might lie (part of) the reason. */
468 if (!after_date_option)
469 {
470 newer_mtime_option = atol (buf);
471 after_date_option = 1;
472 }
473
474 while (fgets (buf, sizeof (buf), fp))
475 {
476 strp = &buf[strlen (buf)];
477 if (strp[-1] == '\n')
478 strp[-1] = '\0';
479 /* FIXME: For files ending with an incomplete line, maybe a NUL might
480 be missing, here... */
481
482 strp = buf;
483 device_number = atol (strp);
484 while (ISDIGIT (*strp))
485 strp++;
486 inode_number = atol (strp);
487 while (ISSPACE (*strp))
488 strp++;
489 while (ISDIGIT (*strp))
490 strp++;
491 strp++;
492 unquote_string (strp);
493 note_directory (strp, device_number, inode_number, NULL);
494 }
495 if (fclose (fp) == EOF)
496 ERROR ((0, errno, "%s", listed_incremental_option));
497 }
498
499 /*---.
500 | ? |
501 `---*/
502
503 void
504 write_dir_file (void)
505 {
506 FILE *fp;
507 struct directory *directory;
508 char *str;
509
510 fp = fopen (listed_incremental_option, "w");
511 if (fp == 0)
512 {
513 ERROR ((0, errno, _("Cannot write to %s"), listed_incremental_option));
514 return;
515 }
516 fprintf (fp, "%lu\n", time_now);
517 for (directory = directory_list; directory; directory = directory->next)
518 {
519 if (!directory->dir_text)
520 continue;
521 str = quote_copy_string (directory->name);
522 if (str)
523 {
524 fprintf (fp, "%u %u %s\n", directory->device_number,
525 directory->inode_number, str);
526 free (str);
527 }
528 else
529 fprintf (fp, "%u %u %s\n", directory->device_number,
530 directory->inode_number, directory->name);
531 }
532 if (fclose (fp) == EOF)
533 ERROR ((0, errno, "%s", listed_incremental_option));
534 }
535
536 /*---.
537 | ? |
538 `---*/
539
540 static int
541 compare_names (char *param1, char *param2)
542 {
543 struct name *n1 = (struct name *) param1;
544 struct name *n2 = (struct name *) param2;
545
546 if (n1->found)
547 return n2->found ? strcmp (n1->name, n2->name) : -1;
548
549 if (n2->found)
550 return 1;
551
552 return strcmp (n1->name, n2->name);
553 }
554
555 /*-------------------------------------------------------------------------.
556 | Collect all the names from argv[] (or whatever), then expand them into a |
557 | directory tree, and put all the directories at the beginning. |
558 `-------------------------------------------------------------------------*/
559
560 void
561 collect_and_sort_names (void)
562 {
563 struct name *name;
564 struct name *next_name;
565 int num_names;
566 struct stat statbuf;
567
568 name_gather ();
569
570 if (listed_incremental_option)
571 read_directory_file ();
572
573 if (!namelist)
574 addname (".");
575
576 for (name = namelist; name; name = next_name)
577 {
578 next_name = name->next;
579 if (name->found || name->dir_contents)
580 continue;
581 if (name->regexp) /* FIXME: just skip regexps for now */
582 continue;
583 if (name->change_dir)
584 if (chdir (name->change_dir) < 0)
585 {
586 ERROR ((0, errno, _("Cannot chdir to %s"), name->change_dir));
587 continue;
588 }
589
590 if (
591 #ifdef AIX
592 statx (name->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK)
593 #else
594 lstat (name->name, &statbuf) < 0
595 #endif
596 )
597 {
598 ERROR ((0, errno, _("Cannot stat %s"), name->name));
599 continue;
600 }
601 if (S_ISDIR (statbuf.st_mode))
602 {
603 name->found = 1;
604 add_hierarchy_to_namelist (name->name, statbuf.st_dev);
605 }
606 }
607
608 num_names = 0;
609 for (name = namelist; name; name = name->next)
610 num_names++;
611 namelist = (struct name *)
612 merge_sort ((voidstar) namelist, num_names,
613 (char *) (&(namelist->next)) - (char *) namelist,
614 compare_names);
615
616 for (name = namelist; name; name = name->next)
617 name->found = 0;
618
619 if (listed_incremental_option)
620 write_dir_file ();
621 }
622 \f
623 /* Restoration of incremental dumps. */
624
625 /*---.
626 | ? |
627 `---*/
628
629 void
630 gnu_restore (int skipcrud)
631 {
632 char *current_dir;
633 char *archive_dir;
634 struct accumulator *accumulator;
635 char *p;
636 DIR *dirp;
637 struct dirent *d;
638 char *cur, *arc;
639 long size, copied;
640 union block *data_block;
641 char *to;
642
643 #define CURRENT_FILE_NAME (skipcrud + current_file_name)
644
645 dirp = opendir (CURRENT_FILE_NAME);
646
647 if (!dirp)
648 {
649 /* The directory doesn't exist now. It'll be created. In any
650 case, we don't have to delete any files out of it. */
651
652 skip_file ((long) current_stat.st_size);
653 return;
654 }
655
656 accumulator = new_accumulator ();
657 while (d = readdir (dirp), d)
658 {
659 if (is_dot_or_dotdot (d->d_name))
660 continue;
661
662 add_to_accumulator (accumulator, d->d_name, (int) (NAMLEN (d) + 1));
663 }
664 closedir (dirp);
665 add_to_accumulator (accumulator, "", 1);
666
667 current_dir = get_accumulator (accumulator);
668 archive_dir = (char *) xmalloc ((size_t) current_stat.st_size);
669 to = archive_dir;
670 for (size = current_stat.st_size; size > 0; size -= copied)
671 {
672 data_block = find_next_block ();
673 if (!data_block)
674 {
675 ERROR ((0, 0, _("Unexpected EOF in archive")));
676 break; /* FIXME: What happens then? */
677 }
678 copied = available_space_after (data_block);
679 if (copied > size)
680 copied = size;
681 memcpy (to, data_block->buffer, (size_t) copied);
682 to += copied;
683 set_next_block_after ((union block *)
684 (data_block->buffer + copied - 1));
685 }
686
687 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
688 {
689 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
690 {
691 arc++;
692 if (!strcmp (arc, cur))
693 break;
694 }
695 if (*arc == '\0')
696 {
697 p = new_name (CURRENT_FILE_NAME, cur);
698 if (interactive_option && !confirm ("delete", p))
699 {
700 free (p);
701 continue;
702 }
703 if (verbose_option)
704 fprintf (stdlis, _("%s: Deleting %s\n"), program_name, p);
705 if (!remove_any_file (p, 1))
706 ERROR ((0, errno, _("Error while deleting %s"), p));
707 free (p);
708 }
709
710 }
711 delete_accumulator (accumulator);
712 free (archive_dir);
713
714 #undef CURRENT_FILE_NAME
715 }
This page took 0.067788 seconds and 5 git commands to generate.