]> Dogcows Code - chaz/tar/blob - src/incremen.c
Use new diagnostics functions.
[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 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #include "system.h"
21 #include <getline.h>
22 #include <hash.h>
23 #include <quotearg.h>
24 #include "common.h"
25 \f
26 /* Variable sized generic character buffers. */
27
28 struct accumulator
29 {
30 size_t allocated;
31 size_t length;
32 char *pointer;
33 };
34
35 /* Amount of space guaranteed just after a reallocation. */
36 #define ACCUMULATOR_SLACK 50
37
38 /* Return the accumulated data from an ACCUMULATOR buffer. */
39 static char *
40 get_accumulator (struct accumulator *accumulator)
41 {
42 return accumulator->pointer;
43 }
44
45 /* Allocate and return a new accumulator buffer. */
46 static struct accumulator *
47 new_accumulator (void)
48 {
49 struct accumulator *accumulator
50 = xmalloc (sizeof (struct accumulator));
51
52 accumulator->allocated = ACCUMULATOR_SLACK;
53 accumulator->pointer = xmalloc (ACCUMULATOR_SLACK);
54 accumulator->length = 0;
55 return accumulator;
56 }
57
58 /* Deallocate an ACCUMULATOR buffer. */
59 static void
60 delete_accumulator (struct accumulator *accumulator)
61 {
62 free (accumulator->pointer);
63 free (accumulator);
64 }
65
66 /* At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes. */
67 static void
68 add_to_accumulator (struct accumulator *accumulator,
69 const char *data, size_t size)
70 {
71 if (accumulator->length + size > accumulator->allocated)
72 {
73 accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
74 accumulator->pointer =
75 xrealloc (accumulator->pointer, accumulator->allocated);
76 }
77 memcpy (accumulator->pointer + accumulator->length, data, size);
78 accumulator->length += size;
79 }
80 \f
81 /* Incremental dump specialities. */
82
83 /* Which child files to save under a directory. */
84 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
85
86 /* Directory attributes. */
87 struct directory
88 {
89 dev_t device_number; /* device number for directory */
90 ino_t inode_number; /* inode number for directory */
91 enum children children;
92 bool nfs;
93 bool found;
94 char name[1]; /* path name of directory */
95 };
96
97 static Hash_table *directory_table;
98
99 #if HAVE_ST_FSTYPE_STRING
100 static char const nfs_string[] = "nfs";
101 # define NFS_FILE_STAT(st) (strcmp ((st).st_fstype, nfs_string) == 0)
102 #else
103 # define ST_DEV_MSB(st) (~ (dev_t) 0 << (sizeof (st).st_dev * CHAR_BIT - 1))
104 # define NFS_FILE_STAT(st) (((st).st_dev & ST_DEV_MSB (st)) != 0)
105 #endif
106
107 /* Calculate the hash of a directory. */
108 static unsigned
109 hash_directory (void const *entry, unsigned n_buckets)
110 {
111 struct directory const *directory = entry;
112 return hash_string (directory->name, n_buckets);
113 }
114
115 /* Compare two directories for equality. */
116 static bool
117 compare_directories (void const *entry1, void const *entry2)
118 {
119 struct directory const *directory1 = entry1;
120 struct directory const *directory2 = entry2;
121 return strcmp (directory1->name, directory2->name) == 0;
122 }
123
124 /* Create and link a new directory entry for directory NAME, having a
125 device number DEV and an inode number INO, with NFS indicating
126 whether it is an NFS device and FOUND indicating whether we have
127 found that the directory exists. */
128 static struct directory *
129 note_directory (char const *name, dev_t dev, ino_t ino, bool nfs, bool found)
130 {
131 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
132 struct directory *directory = xmalloc (size);
133
134 directory->device_number = dev;
135 directory->inode_number = ino;
136 directory->children = CHANGED_CHILDREN;
137 directory->nfs = nfs;
138 directory->found = found;
139 strcpy (directory->name, name);
140
141 if (! ((directory_table
142 || (directory_table = hash_initialize (0, 0, hash_directory,
143 compare_directories, 0)))
144 && hash_insert (directory_table, directory)))
145 xalloc_die ();
146
147 return directory;
148 }
149
150 /* Return a directory entry for a given path NAME, or zero if none found. */
151 static struct directory *
152 find_directory (char *name)
153 {
154 if (! directory_table)
155 return 0;
156 else
157 {
158 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
159 struct directory *dir = alloca (size);
160 strcpy (dir->name, name);
161 return hash_lookup (directory_table, dir);
162 }
163 }
164
165 static int
166 compare_dirents (const void *first, const void *second)
167 {
168 return strcmp ((*(char *const *) first) + 1,
169 (*(char *const *) second) + 1);
170 }
171
172 char *
173 get_directory_contents (char *path, dev_t device)
174 {
175 struct accumulator *accumulator;
176
177 /* Recursively scan the given PATH. */
178
179 {
180 char *dirp = savedir (path); /* for scanning directory */
181 char const *entry; /* directory entry being scanned */
182 size_t entrylen; /* length of directory entry */
183 char *name_buffer; /* directory, `/', and directory member */
184 size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
185 size_t name_length; /* used length in name_buffer */
186 struct directory *directory; /* for checking if already already seen */
187 enum children children;
188
189 if (! dirp)
190 {
191 savedir_error (path);
192 }
193 errno = 0;
194
195 name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
196 name_buffer = xmalloc (name_buffer_size + 2);
197 strcpy (name_buffer, path);
198 if (! ISSLASH (path[strlen (path) - 1]))
199 strcat (name_buffer, "/");
200 name_length = strlen (name_buffer);
201
202 directory = find_directory (path);
203 children = directory ? directory->children : CHANGED_CHILDREN;
204
205 accumulator = new_accumulator ();
206
207 if (dirp && children != NO_CHILDREN)
208 for (entry = dirp;
209 (entrylen = strlen (entry)) != 0;
210 entry += entrylen + 1)
211 {
212 if (name_buffer_size <= entrylen + name_length)
213 {
214 do
215 name_buffer_size += NAME_FIELD_SIZE;
216 while (name_buffer_size <= entrylen + name_length);
217 name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
218 }
219 strcpy (name_buffer + name_length, entry);
220
221 if (excluded_name (name_buffer))
222 add_to_accumulator (accumulator, "N", 1);
223 else
224 {
225 struct stat stat_data;
226
227 if (deref_stat (dereference_option, name_buffer, &stat_data))
228 {
229 stat_diag (name_buffer);
230 continue;
231 }
232
233 if (S_ISDIR (stat_data.st_mode))
234 {
235 bool nfs = NFS_FILE_STAT (stat_data);
236
237 if (directory = find_directory (name_buffer), directory)
238 {
239 /* With NFS, the same file can have two different devices
240 if an NFS directory is mounted in multiple locations,
241 which is relatively common when automounting.
242 To avoid spurious incremental redumping of
243 directories, consider all NFS devices as equal,
244 relying on the i-node to establish differences. */
245
246 if (! (((directory->nfs & nfs)
247 || directory->device_number == stat_data.st_dev)
248 && directory->inode_number == stat_data.st_ino))
249 {
250 if (verbose_option)
251 WARN ((0, 0, _("%s: Directory has been renamed"),
252 quotearg_colon (name_buffer)));
253 directory->children = ALL_CHILDREN;
254 directory->nfs = nfs;
255 directory->device_number = stat_data.st_dev;
256 directory->inode_number = stat_data.st_ino;
257 }
258 directory->found = 1;
259 }
260 else
261 {
262 if (verbose_option)
263 WARN ((0, 0, _("%s: Directory is new"),
264 quotearg_colon (name_buffer)));
265 directory = note_directory (name_buffer,
266 stat_data.st_dev,
267 stat_data.st_ino, nfs, 1);
268 directory->children =
269 ((listed_incremental_option
270 || newer_mtime_option <= stat_data.st_mtime
271 || (after_date_option &&
272 newer_ctime_option <= stat_data.st_ctime))
273 ? ALL_CHILDREN
274 : CHANGED_CHILDREN);
275 }
276
277 if (one_file_system_option && device != stat_data.st_dev)
278 directory->children = NO_CHILDREN;
279 else if (children == ALL_CHILDREN)
280 directory->children = ALL_CHILDREN;
281
282 add_to_accumulator (accumulator, "D", 1);
283 }
284
285 else if (one_file_system_option && device != stat_data.st_dev)
286 add_to_accumulator (accumulator, "N", 1);
287
288 #ifdef S_ISHIDDEN
289 else if (S_ISHIDDEN (stat_data.st_mode))
290 {
291 add_to_accumulator (accumulator, "D", 1);
292 add_to_accumulator (accumulator, entry, entrylen);
293 add_to_accumulator (accumulator, "A", 2);
294 continue;
295 }
296 #endif
297
298 else
299 if (children == CHANGED_CHILDREN
300 && stat_data.st_mtime < newer_mtime_option
301 && (!after_date_option
302 || stat_data.st_ctime < newer_ctime_option))
303 add_to_accumulator (accumulator, "N", 1);
304 else
305 add_to_accumulator (accumulator, "Y", 1);
306 }
307
308 add_to_accumulator (accumulator, entry, entrylen + 1);
309 }
310
311 add_to_accumulator (accumulator, "\000\000", 2);
312
313 free (name_buffer);
314 if (dirp)
315 free (dirp);
316 }
317
318 /* Sort the contents of the directory, now that we have it all. */
319
320 {
321 char *pointer = get_accumulator (accumulator);
322 size_t counter;
323 char *cursor;
324 char *buffer;
325 char **array;
326 char **array_cursor;
327
328 counter = 0;
329 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
330 counter++;
331
332 if (! counter)
333 {
334 delete_accumulator (accumulator);
335 return 0;
336 }
337
338 array = xmalloc (sizeof (char *) * (counter + 1));
339
340 array_cursor = array;
341 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
342 *array_cursor++ = cursor;
343 *array_cursor = 0;
344
345 qsort (array, counter, sizeof (char *), compare_dirents);
346
347 buffer = xmalloc (cursor - pointer + 2);
348
349 cursor = buffer;
350 for (array_cursor = array; *array_cursor; array_cursor++)
351 {
352 char *string = *array_cursor;
353
354 while ((*cursor++ = *string++))
355 continue;
356 }
357 *cursor = '\0';
358
359 delete_accumulator (accumulator);
360 free (array);
361 return buffer;
362 }
363 }
364 \f
365 static FILE *listed_incremental_stream;
366
367 void
368 read_directory_file (void)
369 {
370 int fd;
371 FILE *fp;
372 char *buf = 0;
373 size_t bufsize;
374
375 /* Open the file for both read and write. That way, we can write
376 it later without having to reopen it, and don't have to worry if
377 we chdir in the meantime. */
378 fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
379 if (fd < 0)
380 {
381 open_error (listed_incremental_option);
382 return;
383 }
384
385 fp = fdopen (fd, "r+");
386 if (! fp)
387 {
388 open_error (listed_incremental_option);
389 close (fd);
390 return;
391 }
392
393 listed_incremental_stream = fp;
394
395 if (0 < getline (&buf, &bufsize, fp))
396 {
397 char *ebuf;
398 int n;
399 long lineno = 1;
400 unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
401 time_t t = u;
402 if (buf == ebuf || (u == 0 && errno == EINVAL))
403 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
404 _("Invalid time stamp")));
405 else if (t != u || (u == -1 && errno == ERANGE))
406 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
407 _("Time stamp out of range")));
408 else
409 newer_mtime_option = t;
410
411 while (0 < (n = getline (&buf, &bufsize, fp)))
412 {
413 dev_t dev;
414 ino_t ino;
415 bool nfs = buf[0] == '+';
416 char *strp = buf + nfs;
417
418 lineno++;
419
420 if (buf[n - 1] == '\n')
421 buf[n - 1] = '\0';
422
423 errno = 0;
424 dev = u = strtoul (strp, &ebuf, 10);
425 if (strp == ebuf || (u == 0 && errno == EINVAL))
426 ERROR ((0, 0, "%s:%ld: %s",
427 quotearg_colon (listed_incremental_option), lineno,
428 _("Invalid device number")));
429 else if (dev != u || (u == -1 && errno == ERANGE))
430 ERROR ((0, 0, "%s:%ld: %s",
431 quotearg_colon (listed_incremental_option), lineno,
432 _("Device number out of range")));
433 strp = ebuf;
434
435 errno = 0;
436 ino = u = strtoul (strp, &ebuf, 10);
437 if (strp == ebuf || (u == 0 && errno == EINVAL))
438 ERROR ((0, 0, "%s:%ld: %s",
439 quotearg_colon (listed_incremental_option), lineno,
440 _("Invalid inode number")));
441 else if (ino != u || (u == -1 && errno == ERANGE))
442 ERROR ((0, 0, "%s:%ld: %s",
443 quotearg_colon (listed_incremental_option), lineno,
444 _("Inode number out of range")));
445 strp = ebuf;
446
447 strp++;
448 unquote_string (strp);
449 note_directory (strp, dev, ino, nfs, 0);
450 }
451 }
452
453 if (ferror (fp))
454 read_error (listed_incremental_option);
455 if (buf)
456 free (buf);
457 }
458
459 /* Output incremental data for the directory ENTRY to the file DATA.
460 Return nonzero if successful, preserving errno on write failure. */
461 static bool
462 write_directory_file_entry (void *entry, void *data)
463 {
464 struct directory const *directory = entry;
465 FILE *fp = data;
466
467 if (directory->found)
468 {
469 int e;
470 char *str = quote_copy_string (directory->name);
471 fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
472 (unsigned long) directory->device_number,
473 (unsigned long) directory->inode_number,
474 str ? str : directory->name);
475 e = errno;
476 if (str)
477 free (str);
478 errno = e;
479 }
480
481 return ! ferror (fp);
482 }
483
484 void
485 write_directory_file (void)
486 {
487 FILE *fp = listed_incremental_stream;
488
489 if (! fp)
490 return;
491
492 if (fseek (fp, 0L, SEEK_SET) != 0)
493 seek_error (listed_incremental_option);
494 if (ftruncate (fileno (fp), (off_t) 0) != 0)
495 truncate_error (listed_incremental_option);
496
497 fprintf (fp, "%lu\n", (unsigned long) start_time);
498 if (! ferror (fp) && directory_table)
499 hash_do_for_each (directory_table, write_directory_file_entry, fp);
500 if (ferror (fp))
501 write_error (listed_incremental_option);
502 if (fclose (fp) != 0)
503 close_error (listed_incremental_option);
504 }
505 \f
506 /* Restoration of incremental dumps. */
507
508 void
509 gnu_restore (char const *directory_name)
510 {
511 char *archive_dir;
512 char *current_dir;
513 char *cur, *arc;
514 size_t size;
515 size_t copied;
516 union block *data_block;
517 char *to;
518
519 current_dir = savedir (directory_name);
520
521 if (!current_dir)
522 {
523 /* The directory doesn't exist now. It'll be created. In any
524 case, we don't have to delete any files out of it. */
525
526 skip_member ();
527 return;
528 }
529
530 size = current_stat_info.stat.st_size;
531 if (size != current_stat_info.stat.st_size)
532 xalloc_die ();
533 archive_dir = xmalloc (size);
534 to = archive_dir;
535 for (; size > 0; size -= copied)
536 {
537 data_block = find_next_block ();
538 if (!data_block)
539 {
540 ERROR ((0, 0, _("Unexpected EOF in archive")));
541 break; /* FIXME: What happens then? */
542 }
543 copied = available_space_after (data_block);
544 if (copied > size)
545 copied = size;
546 memcpy (to, data_block->buffer, copied);
547 to += copied;
548 set_next_block_after ((union block *)
549 (data_block->buffer + copied - 1));
550 }
551
552 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
553 {
554 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
555 {
556 arc++;
557 if (!strcmp (arc, cur))
558 break;
559 }
560 if (*arc == '\0')
561 {
562 char *p = new_name (directory_name, cur);
563 if (! interactive_option || confirm ("delete", p))
564 {
565 if (verbose_option)
566 fprintf (stdlis, _("%s: Deleting %s\n"),
567 program_name, quote (p));
568 if (! remove_any_file (p, 1))
569 {
570 int e = errno;
571 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
572 }
573 }
574 free (p);
575 }
576
577 }
578 free (current_dir);
579 free (archive_dir);
580 }
This page took 0.070436 seconds and 5 git commands to generate.