]> Dogcows Code - chaz/tar/blob - src/incremen.c
(struct directory.mtime): New member.
[chaz/tar] / src / incremen.c
1 /* GNU dump extensions to tar.
2
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004, 2005 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14 Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 #include <system.h>
21 #include <getline.h>
22 #include <hash.h>
23 #include <quotearg.h>
24 #include "common.h"
25
26 /* Incremental dump specialities. */
27
28 /* Which child files to save under a directory. */
29 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
30
31 /* Directory attributes. */
32 struct directory
33 {
34 struct timespec mtime; /* Modification time */
35 dev_t device_number; /* device number for directory */
36 ino_t inode_number; /* inode number for directory */
37 enum children children;
38 bool nfs;
39 bool found;
40 char name[1]; /* file name of directory */
41 };
42
43 static Hash_table *directory_table;
44
45 #if HAVE_ST_FSTYPE_STRING
46 static char const nfs_string[] = "nfs";
47 # define NFS_FILE_STAT(st) (strcmp ((st).st_fstype, nfs_string) == 0)
48 #else
49 # define ST_DEV_MSB(st) (~ (dev_t) 0 << (sizeof (st).st_dev * CHAR_BIT - 1))
50 # define NFS_FILE_STAT(st) (((st).st_dev & ST_DEV_MSB (st)) != 0)
51 #endif
52
53 /* Calculate the hash of a directory. */
54 static size_t
55 hash_directory (void const *entry, size_t n_buckets)
56 {
57 struct directory const *directory = entry;
58 return hash_string (directory->name, n_buckets);
59 }
60
61 /* Compare two directories for equality. */
62 static bool
63 compare_directories (void const *entry1, void const *entry2)
64 {
65 struct directory const *directory1 = entry1;
66 struct directory const *directory2 = entry2;
67 return strcmp (directory1->name, directory2->name) == 0;
68 }
69
70 /* Create and link a new directory entry for directory NAME, having a
71 device number DEV and an inode number INO, with NFS indicating
72 whether it is an NFS device and FOUND indicating whether we have
73 found that the directory exists. */
74 static struct directory *
75 note_directory (char const *name, struct timespec mtime,
76 dev_t dev, ino_t ino, bool nfs, bool found)
77 {
78 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
79 struct directory *directory = xmalloc (size);
80
81 directory->mtime = mtime;
82 directory->device_number = dev;
83 directory->inode_number = ino;
84 directory->children = CHANGED_CHILDREN;
85 directory->nfs = nfs;
86 directory->found = found;
87 strcpy (directory->name, name);
88
89 if (! ((directory_table
90 || (directory_table = hash_initialize (0, 0, hash_directory,
91 compare_directories, 0)))
92 && hash_insert (directory_table, directory)))
93 xalloc_die ();
94
95 return directory;
96 }
97
98 /* Return a directory entry for a given file NAME, or zero if none found. */
99 static struct directory *
100 find_directory (char *name)
101 {
102 if (! directory_table)
103 return 0;
104 else
105 {
106 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
107 struct directory *dir = alloca (size);
108 strcpy (dir->name, name);
109 return hash_lookup (directory_table, dir);
110 }
111 }
112
113 void
114 update_parent_directory (const char *name)
115 {
116 struct directory *directory;
117 char *p, *name_buffer;
118
119 p = dir_name (name);
120 name_buffer = xmalloc (strlen (p) + 2);
121 strcpy (name_buffer, p);
122 if (! ISSLASH (p[strlen (p) - 1]))
123 strcat (name_buffer, "/");
124
125 directory = find_directory (name_buffer);
126 free (name_buffer);
127 if (directory)
128 {
129 struct stat st;
130 if (deref_stat (dereference_option, p, &st) != 0)
131 stat_diag (name);
132 else
133 directory->mtime = get_stat_mtime (&st);
134 }
135 free (p);
136 }
137
138 static int
139 compare_dirents (const void *first, const void *second)
140 {
141 return strcmp ((*(char *const *) first) + 1,
142 (*(char *const *) second) + 1);
143 }
144
145 enum children
146 procdir (char *name_buffer, struct stat *stat_data,
147 dev_t device,
148 enum children children,
149 bool verbose)
150 {
151 struct directory *directory;
152 bool nfs = NFS_FILE_STAT (*stat_data);
153
154 if ((directory = find_directory (name_buffer)) != NULL)
155 {
156 /* With NFS, the same file can have two different devices
157 if an NFS directory is mounted in multiple locations,
158 which is relatively common when automounting.
159 To avoid spurious incremental redumping of
160 directories, consider all NFS devices as equal,
161 relying on the i-node to establish differences. */
162
163 if (! (((directory->nfs & nfs)
164 || directory->device_number == stat_data->st_dev)
165 && directory->inode_number == stat_data->st_ino))
166 {
167 if (verbose)
168 WARN ((0, 0, _("%s: Directory has been renamed"),
169 quotearg_colon (name_buffer)));
170 directory->children = ALL_CHILDREN;
171 directory->nfs = nfs;
172 directory->device_number = stat_data->st_dev;
173 directory->inode_number = stat_data->st_ino;
174 }
175 else if (listed_incremental_option)
176 /* Newer modification time can mean that new files were
177 created in the directory or some of the existing files
178 were renamed. */
179 directory->children =
180 timespec_cmp (get_stat_mtime (stat_data), directory->mtime) > 0
181 ? ALL_CHILDREN : CHANGED_CHILDREN;
182
183 directory->found = true;
184 }
185 else
186 {
187 if (verbose)
188 WARN ((0, 0, _("%s: Directory is new"),
189 quotearg_colon (name_buffer)));
190 directory = note_directory (name_buffer,
191 get_stat_mtime(stat_data),
192 stat_data->st_dev,
193 stat_data->st_ino,
194 nfs,
195 true);
196
197 directory->children =
198 (listed_incremental_option
199 || (OLDER_STAT_TIME (*stat_data, m)
200 || (after_date_option
201 && OLDER_STAT_TIME (*stat_data, c))))
202 ? ALL_CHILDREN
203 : CHANGED_CHILDREN;
204 }
205
206 if (one_file_system_option && device != stat_data->st_dev)
207 directory->children = NO_CHILDREN;
208 else if (children == ALL_CHILDREN)
209 directory->children = ALL_CHILDREN;
210
211 return directory->children;
212 }
213
214
215 /* Recursively scan the given directory. */
216 static void
217 scan_directory (struct obstack *stk, char *dir_name, dev_t device)
218 {
219 char *dirp = savedir (dir_name); /* for scanning directory */
220 char const *entry; /* directory entry being scanned */
221 size_t entrylen; /* length of directory entry */
222 char *name_buffer; /* directory, `/', and directory member */
223 size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
224 size_t name_length; /* used length in name_buffer */
225 enum children children;
226 struct stat stat_data;
227
228 if (! dirp)
229 savedir_error (dir_name);
230
231 name_buffer_size = strlen (dir_name) + NAME_FIELD_SIZE;
232 name_buffer = xmalloc (name_buffer_size + 2);
233 strcpy (name_buffer, dir_name);
234 if (! ISSLASH (dir_name[strlen (dir_name) - 1]))
235 strcat (name_buffer, "/");
236 name_length = strlen (name_buffer);
237
238 if (deref_stat (dereference_option, name_buffer, &stat_data))
239 {
240 stat_diag (name_buffer);
241 children = CHANGED_CHILDREN;
242 }
243 else
244 children = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
245
246 if (dirp && children != NO_CHILDREN)
247 for (entry = dirp;
248 (entrylen = strlen (entry)) != 0;
249 entry += entrylen + 1)
250 {
251 if (name_buffer_size <= entrylen + name_length)
252 {
253 do
254 name_buffer_size += NAME_FIELD_SIZE;
255 while (name_buffer_size <= entrylen + name_length);
256 name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
257 }
258 strcpy (name_buffer + name_length, entry);
259
260 if (excluded_name (name_buffer))
261 obstack_1grow (stk, 'N');
262 else
263 {
264
265 if (deref_stat (dereference_option, name_buffer, &stat_data))
266 {
267 stat_diag (name_buffer);
268 continue;
269 }
270
271 if (S_ISDIR (stat_data.st_mode))
272 {
273 procdir (name_buffer, &stat_data, device, children,
274 verbose_option);
275 obstack_1grow (stk, 'D');
276 }
277
278 else if (one_file_system_option && device != stat_data.st_dev)
279 obstack_1grow (stk, 'N');
280
281 #ifdef S_ISHIDDEN
282 else if (S_ISHIDDEN (stat_data.st_mode))
283 {
284 obstack_1grow (stk, 'D');
285 obstack_grow (stk, entry, entrylen);
286 obstack_grow (stk, "A", 2);
287 continue;
288 }
289 #endif
290
291 else
292 if (children == CHANGED_CHILDREN
293 && OLDER_STAT_TIME (stat_data, m)
294 && (!after_date_option || OLDER_STAT_TIME (stat_data, c)))
295 obstack_1grow (stk, 'N');
296 else
297 obstack_1grow (stk, 'Y');
298 }
299
300 obstack_grow (stk, entry, entrylen + 1);
301 }
302
303 obstack_grow (stk, "\000\000", 2);
304
305 free (name_buffer);
306 if (dirp)
307 free (dirp);
308 }
309
310 /* Sort the contents of the obstack, and convert it to the char * */
311 static char *
312 sort_obstack (struct obstack *stk)
313 {
314 char *pointer = obstack_finish (stk);
315 size_t counter;
316 char *cursor;
317 char *buffer;
318 char **array;
319 char **array_cursor;
320
321 counter = 0;
322 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
323 counter++;
324
325 if (!counter)
326 return NULL;
327
328 array = obstack_alloc (stk, sizeof (char *) * (counter + 1));
329
330 array_cursor = array;
331 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
332 *array_cursor++ = cursor;
333 *array_cursor = 0;
334
335 qsort (array, counter, sizeof (char *), compare_dirents);
336
337 buffer = xmalloc (cursor - pointer + 2);
338
339 cursor = buffer;
340 for (array_cursor = array; *array_cursor; array_cursor++)
341 {
342 char *string = *array_cursor;
343
344 while ((*cursor++ = *string++))
345 continue;
346 }
347 *cursor = '\0';
348 return buffer;
349 }
350
351 char *
352 get_directory_contents (char *dir_name, dev_t device)
353 {
354 struct obstack stk;
355 char *buffer;
356
357 obstack_init (&stk);
358 scan_directory (&stk, dir_name, device);
359 buffer = sort_obstack (&stk);
360 obstack_free (&stk, NULL);
361 return buffer;
362 }
363
364 \f
365
366 static FILE *listed_incremental_stream;
367
368 /* Version of incremental format snapshots (directory files) used by this
369 tar. Currently it is supposed to be a single decimal number. 0 means
370 incremental snapshots as per tar version before 1.15.2.
371
372 The current tar version supports incremental versions from
373 0 up to TAR_INCREMENTAL_VERSION, inclusive.
374 It is able to create only snapshots of TAR_INCREMENTAL_VERSION */
375
376 #define TAR_INCREMENTAL_VERSION 1
377
378 /* Read incremental snapshot file (directory file).
379 If the file has older incremental version, make sure that it is processed
380 correctly and that tar will use the most conservative backup method among
381 possible alternatives (i.e. prefer ALL_CHILDREN over CHANGED_CHILDREN,
382 etc.) This ensures that the snapshots are updated to the recent version
383 without any loss of data. */
384 void
385 read_directory_file (void)
386 {
387 int fd;
388 FILE *fp;
389 char *buf = 0;
390 size_t bufsize;
391
392 /* Open the file for both read and write. That way, we can write
393 it later without having to reopen it, and don't have to worry if
394 we chdir in the meantime. */
395 fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
396 if (fd < 0)
397 {
398 open_error (listed_incremental_option);
399 return;
400 }
401
402 fp = fdopen (fd, "r+");
403 if (! fp)
404 {
405 open_error (listed_incremental_option);
406 close (fd);
407 return;
408 }
409
410 listed_incremental_stream = fp;
411
412 if (0 < getline (&buf, &bufsize, fp))
413 {
414 char *ebuf;
415 int n;
416 long lineno = 1;
417 uintmax_t u;
418 time_t t = u;
419 int incremental_version;
420
421 if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
422 {
423 ebuf = buf + sizeof PACKAGE_NAME - 1;
424 if (*ebuf++ != '-')
425 ERROR((1, 0, _("Bad incremental file format")));
426 for (; *ebuf != '-'; ebuf++)
427 if (!*ebuf)
428 ERROR((1, 0, _("Bad incremental file format")));
429
430 incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
431 if (getline (&buf, &bufsize, fp) <= 0)
432 {
433 read_error (listed_incremental_option);
434 free (buf);
435 return;
436 }
437 ++lineno;
438 }
439 else
440 incremental_version = 0;
441
442 if (incremental_version > TAR_INCREMENTAL_VERSION)
443 ERROR((1, 0, _("Unsupported incremental format version: %s"),
444 incremental_version));
445
446 t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
447 if (buf == ebuf || (u == 0 && errno == EINVAL))
448 ERROR ((0, 0, "%s:%ld: %s",
449 quotearg_colon (listed_incremental_option),
450 lineno,
451 _("Invalid time stamp")));
452 else if (t != u)
453 ERROR ((0, 0, "%s:%ld: %s",
454 quotearg_colon (listed_incremental_option),
455 lineno,
456 _("Time stamp out of range")));
457 else if (incremental_version == 1)
458 {
459 newer_mtime_option.tv_sec = t;
460
461 t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
462 if (buf == ebuf || (u == 0 && errno == EINVAL))
463 ERROR ((0, 0, "%s:%ld: %s",
464 quotearg_colon (listed_incremental_option),
465 lineno,
466 _("Invalid time stamp")));
467 else if (t != u)
468 ERROR ((0, 0, "%s:%ld: %s",
469 quotearg_colon (listed_incremental_option),
470 lineno,
471 _("Time stamp out of range")));
472 newer_mtime_option.tv_nsec = t;
473 }
474 else
475 {
476 /* pre-1 incremental format does not contain nanoseconds */
477 newer_mtime_option.tv_sec = t;
478 newer_mtime_option.tv_nsec = 0;
479 }
480
481 while (0 < (n = getline (&buf, &bufsize, fp)))
482 {
483 dev_t dev;
484 ino_t ino;
485 bool nfs = buf[0] == '+';
486 char *strp = buf + nfs;
487 struct timespec mtime;
488
489 lineno++;
490
491 if (buf[n - 1] == '\n')
492 buf[n - 1] = '\0';
493
494 if (incremental_version == 1)
495 {
496 errno = 0;
497 mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
498 if (!isspace (*ebuf))
499 ERROR ((0, 0, "%s:%ld: %s",
500 quotearg_colon (listed_incremental_option), lineno,
501 _("Invalid modification time (seconds)")));
502 else if (mtime.tv_sec != u)
503 ERROR ((0, 0, "%s:%ld: %s",
504 quotearg_colon (listed_incremental_option), lineno,
505 _("Modification time (seconds) out of range")));
506 strp = ebuf;
507
508 errno = 0;
509 mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
510 if (!isspace (*ebuf))
511 ERROR ((0, 0, "%s:%ld: %s",
512 quotearg_colon (listed_incremental_option), lineno,
513 _("Invalid modification time (nanoseconds)")));
514 else if (mtime.tv_nsec != u)
515 ERROR ((0, 0, "%s:%ld: %s",
516 quotearg_colon (listed_incremental_option), lineno,
517 _("Modification time (nanoseconds) out of range")));
518 strp = ebuf;
519 }
520 else
521 memset (&mtime, 0, sizeof mtime);
522
523 errno = 0;
524 dev = u = strtoumax (strp, &ebuf, 10);
525 if (!isspace (*ebuf))
526 ERROR ((0, 0, "%s:%ld: %s",
527 quotearg_colon (listed_incremental_option), lineno,
528 _("Invalid device number")));
529 else if (dev != u)
530 ERROR ((0, 0, "%s:%ld: %s",
531 quotearg_colon (listed_incremental_option), lineno,
532 _("Device number out of range")));
533 strp = ebuf;
534
535 errno = 0;
536 ino = u = strtoumax (strp, &ebuf, 10);
537 if (!isspace (*ebuf))
538 ERROR ((0, 0, "%s:%ld: %s",
539 quotearg_colon (listed_incremental_option), lineno,
540 _("Invalid inode number")));
541 else if (ino != u)
542 ERROR ((0, 0, "%s:%ld: %s",
543 quotearg_colon (listed_incremental_option), lineno,
544 _("Inode number out of range")));
545 strp = ebuf;
546
547 strp++;
548 unquote_string (strp);
549 note_directory (strp, mtime, dev, ino, nfs, 0);
550 }
551 }
552
553 if (ferror (fp))
554 read_error (listed_incremental_option);
555 if (buf)
556 free (buf);
557 }
558
559 /* Output incremental data for the directory ENTRY to the file DATA.
560 Return nonzero if successful, preserving errno on write failure. */
561 static bool
562 write_directory_file_entry (void *entry, void *data)
563 {
564 struct directory const *directory = entry;
565 FILE *fp = data;
566
567 if (directory->found)
568 {
569 int e;
570 char buf[UINTMAX_STRSIZE_BOUND];
571 char *str = quote_copy_string (directory->name);
572
573 if (directory->nfs)
574 fprintf (fp, "+");
575 fprintf (fp, "%s ", umaxtostr (directory->mtime.tv_sec, buf));
576 fprintf (fp, "%s ", umaxtostr (directory->mtime.tv_nsec, buf));
577 fprintf (fp, "%s ", umaxtostr (directory->device_number, buf));
578 fprintf (fp, "%s ", umaxtostr (directory->inode_number, buf));
579 fprintf (fp, "%s\n", str ? str : directory->name);
580
581 e = errno;
582 if (str)
583 free (str);
584 errno = e;
585 }
586
587 return ! ferror (fp);
588 }
589
590 void
591 write_directory_file (void)
592 {
593 FILE *fp = listed_incremental_stream;
594
595 if (! fp)
596 return;
597
598 if (fseek (fp, 0L, SEEK_SET) != 0)
599 seek_error (listed_incremental_option);
600 if (sys_truncate (fileno (fp)) != 0)
601 truncate_error (listed_incremental_option);
602
603 fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
604 TAR_INCREMENTAL_VERSION);
605
606 fprintf (fp, "%lu %lu\n",
607 (unsigned long int) start_time.tv_sec,
608 (unsigned long int) start_time.tv_nsec);
609 if (! ferror (fp) && directory_table)
610 hash_do_for_each (directory_table, write_directory_file_entry, fp);
611 if (ferror (fp))
612 write_error (listed_incremental_option);
613 if (fclose (fp) != 0)
614 close_error (listed_incremental_option);
615 }
616
617 \f
618 /* Restoration of incremental dumps. */
619
620 /* Examine the directories under directory_name and delete any
621 files that were not there at the time of the back-up. */
622 void
623 purge_directory (char const *directory_name)
624 {
625 char *archive_dir;
626 char *current_dir;
627 char *cur, *arc;
628 size_t size;
629 size_t copied;
630 union block *data_block;
631 char *to;
632
633 current_dir = savedir (directory_name);
634
635 if (!current_dir)
636 {
637 /* The directory doesn't exist now. It'll be created. In any
638 case, we don't have to delete any files out of it. */
639
640 skip_member ();
641 return;
642 }
643
644 size = current_stat_info.stat.st_size;
645 if (size != current_stat_info.stat.st_size)
646 xalloc_die ();
647 archive_dir = xmalloc (size);
648 to = archive_dir;
649 for (; size > 0; size -= copied)
650 {
651 data_block = find_next_block ();
652 if (!data_block)
653 {
654 ERROR ((0, 0, _("Unexpected EOF in archive")));
655 break; /* FIXME: What happens then? */
656 }
657 copied = available_space_after (data_block);
658 if (copied > size)
659 copied = size;
660 memcpy (to, data_block->buffer, copied);
661 to += copied;
662 set_next_block_after ((union block *)
663 (data_block->buffer + copied - 1));
664 }
665
666 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
667 {
668 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
669 {
670 arc++;
671 if (!strcmp (arc, cur))
672 break;
673 }
674 if (*arc == '\0')
675 {
676 struct stat st;
677 char *p = new_name (directory_name, cur);
678
679 if (deref_stat (false, p, &st))
680 {
681 stat_diag (p);
682 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
683 quotearg_colon (p)));
684 continue;
685 }
686 else if (one_file_system_option && st.st_dev != root_device)
687 {
688 WARN((0, 0,
689 _("%s: directory is on a different device: not purging"),
690 quotearg_colon (p)));
691 continue;
692 }
693
694 if (! interactive_option || confirm ("delete", p))
695 {
696 if (verbose_option)
697 fprintf (stdlis, _("%s: Deleting %s\n"),
698 program_name, quote (p));
699 if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION))
700 {
701 int e = errno;
702 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
703 }
704 }
705 free (p);
706 }
707
708 }
709 free (current_dir);
710 free (archive_dir);
711 }
712
713 void
714 list_dumpdir (char *buffer, size_t size)
715 {
716 while (size)
717 {
718 switch (*buffer)
719 {
720 case 'Y':
721 case 'N':
722 case 'D':
723 fprintf (stdlis, "%c ", *buffer);
724 buffer++;
725 size--;
726 break;
727
728 case 0:
729 fputc ('\n', stdlis);
730 buffer++;
731 size--;
732 break;
733
734 default:
735 fputc (*buffer, stdlis);
736 buffer++;
737 size--;
738 }
739 }
740 }
This page took 0.071378 seconds and 5 git commands to generate.