]> Dogcows Code - chaz/tar/blob - src/incremen.c
Use current_stat_info
[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 if (ignore_failed_read_option)
192 savedir_warn (path);
193 else
194 savedir_error (path);
195 }
196 errno = 0;
197
198 name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
199 name_buffer = xmalloc (name_buffer_size + 2);
200 strcpy (name_buffer, path);
201 if (! ISSLASH (path[strlen (path) - 1]))
202 strcat (name_buffer, "/");
203 name_length = strlen (name_buffer);
204
205 directory = find_directory (path);
206 children = directory ? directory->children : CHANGED_CHILDREN;
207
208 accumulator = new_accumulator ();
209
210 if (dirp && children != NO_CHILDREN)
211 for (entry = dirp;
212 (entrylen = strlen (entry)) != 0;
213 entry += entrylen + 1)
214 {
215 if (name_buffer_size <= entrylen + name_length)
216 {
217 do
218 name_buffer_size += NAME_FIELD_SIZE;
219 while (name_buffer_size <= entrylen + name_length);
220 name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
221 }
222 strcpy (name_buffer + name_length, entry);
223
224 if (excluded_name (name_buffer))
225 add_to_accumulator (accumulator, "N", 1);
226 else
227 {
228 struct stat stat_data;
229
230 if (deref_stat (dereference_option, name_buffer, &stat_data))
231 {
232 if (ignore_failed_read_option)
233 stat_warn (name_buffer);
234 else
235 stat_error (name_buffer);
236 continue;
237 }
238
239 if (S_ISDIR (stat_data.st_mode))
240 {
241 bool nfs = NFS_FILE_STAT (stat_data);
242
243 if (directory = find_directory (name_buffer), directory)
244 {
245 /* With NFS, the same file can have two different devices
246 if an NFS directory is mounted in multiple locations,
247 which is relatively common when automounting.
248 To avoid spurious incremental redumping of
249 directories, consider all NFS devices as equal,
250 relying on the i-node to establish differences. */
251
252 if (! (((directory->nfs & nfs)
253 || directory->device_number == stat_data.st_dev)
254 && directory->inode_number == stat_data.st_ino))
255 {
256 if (verbose_option)
257 WARN ((0, 0, _("%s: Directory has been renamed"),
258 quotearg_colon (name_buffer)));
259 directory->children = ALL_CHILDREN;
260 directory->nfs = nfs;
261 directory->device_number = stat_data.st_dev;
262 directory->inode_number = stat_data.st_ino;
263 }
264 directory->found = 1;
265 }
266 else
267 {
268 if (verbose_option)
269 WARN ((0, 0, _("%s: Directory is new"),
270 quotearg_colon (name_buffer)));
271 directory = note_directory (name_buffer,
272 stat_data.st_dev,
273 stat_data.st_ino, nfs, 1);
274 directory->children =
275 ((listed_incremental_option
276 || newer_mtime_option <= stat_data.st_mtime
277 || (after_date_option &&
278 newer_ctime_option <= stat_data.st_ctime))
279 ? ALL_CHILDREN
280 : CHANGED_CHILDREN);
281 }
282
283 if (one_file_system_option && device != stat_data.st_dev)
284 directory->children = NO_CHILDREN;
285 else if (children == ALL_CHILDREN)
286 directory->children = ALL_CHILDREN;
287
288 add_to_accumulator (accumulator, "D", 1);
289 }
290
291 else if (one_file_system_option && device != stat_data.st_dev)
292 add_to_accumulator (accumulator, "N", 1);
293
294 #ifdef S_ISHIDDEN
295 else if (S_ISHIDDEN (stat_data.st_mode))
296 {
297 add_to_accumulator (accumulator, "D", 1);
298 add_to_accumulator (accumulator, entry, entrylen);
299 add_to_accumulator (accumulator, "A", 2);
300 continue;
301 }
302 #endif
303
304 else
305 if (children == CHANGED_CHILDREN
306 && stat_data.st_mtime < newer_mtime_option
307 && (!after_date_option
308 || stat_data.st_ctime < newer_ctime_option))
309 add_to_accumulator (accumulator, "N", 1);
310 else
311 add_to_accumulator (accumulator, "Y", 1);
312 }
313
314 add_to_accumulator (accumulator, entry, entrylen + 1);
315 }
316
317 add_to_accumulator (accumulator, "\000\000", 2);
318
319 free (name_buffer);
320 if (dirp)
321 free (dirp);
322 }
323
324 /* Sort the contents of the directory, now that we have it all. */
325
326 {
327 char *pointer = get_accumulator (accumulator);
328 size_t counter;
329 char *cursor;
330 char *buffer;
331 char **array;
332 char **array_cursor;
333
334 counter = 0;
335 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
336 counter++;
337
338 if (! counter)
339 {
340 delete_accumulator (accumulator);
341 return 0;
342 }
343
344 array = xmalloc (sizeof (char *) * (counter + 1));
345
346 array_cursor = array;
347 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
348 *array_cursor++ = cursor;
349 *array_cursor = 0;
350
351 qsort (array, counter, sizeof (char *), compare_dirents);
352
353 buffer = xmalloc (cursor - pointer + 2);
354
355 cursor = buffer;
356 for (array_cursor = array; *array_cursor; array_cursor++)
357 {
358 char *string = *array_cursor;
359
360 while ((*cursor++ = *string++))
361 continue;
362 }
363 *cursor = '\0';
364
365 delete_accumulator (accumulator);
366 free (array);
367 return buffer;
368 }
369 }
370 \f
371 static FILE *listed_incremental_stream;
372
373 void
374 read_directory_file (void)
375 {
376 int fd;
377 FILE *fp;
378 char *buf = 0;
379 size_t bufsize;
380
381 /* Open the file for both read and write. That way, we can write
382 it later without having to reopen it, and don't have to worry if
383 we chdir in the meantime. */
384 fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
385 if (fd < 0)
386 {
387 open_error (listed_incremental_option);
388 return;
389 }
390
391 fp = fdopen (fd, "r+");
392 if (! fp)
393 {
394 open_error (listed_incremental_option);
395 close (fd);
396 return;
397 }
398
399 listed_incremental_stream = fp;
400
401 if (0 < getline (&buf, &bufsize, fp))
402 {
403 char *ebuf;
404 int n;
405 long lineno = 1;
406 unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
407 time_t t = u;
408 if (buf == ebuf || (u == 0 && errno == EINVAL))
409 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
410 _("Invalid time stamp")));
411 else if (t != u || (u == -1 && errno == ERANGE))
412 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
413 _("Time stamp out of range")));
414 else
415 newer_mtime_option = t;
416
417 while (0 < (n = getline (&buf, &bufsize, fp)))
418 {
419 dev_t dev;
420 ino_t ino;
421 bool nfs = buf[0] == '+';
422 char *strp = buf + nfs;
423
424 lineno++;
425
426 if (buf[n - 1] == '\n')
427 buf[n - 1] = '\0';
428
429 errno = 0;
430 dev = u = strtoul (strp, &ebuf, 10);
431 if (strp == ebuf || (u == 0 && errno == EINVAL))
432 ERROR ((0, 0, "%s:%ld: %s",
433 quotearg_colon (listed_incremental_option), lineno,
434 _("Invalid device number")));
435 else if (dev != u || (u == -1 && errno == ERANGE))
436 ERROR ((0, 0, "%s:%ld: %s",
437 quotearg_colon (listed_incremental_option), lineno,
438 _("Device number out of range")));
439 strp = ebuf;
440
441 errno = 0;
442 ino = u = strtoul (strp, &ebuf, 10);
443 if (strp == ebuf || (u == 0 && errno == EINVAL))
444 ERROR ((0, 0, "%s:%ld: %s",
445 quotearg_colon (listed_incremental_option), lineno,
446 _("Invalid inode number")));
447 else if (ino != u || (u == -1 && errno == ERANGE))
448 ERROR ((0, 0, "%s:%ld: %s",
449 quotearg_colon (listed_incremental_option), lineno,
450 _("Inode number out of range")));
451 strp = ebuf;
452
453 strp++;
454 unquote_string (strp);
455 note_directory (strp, dev, ino, nfs, 0);
456 }
457 }
458
459 if (ferror (fp))
460 read_error (listed_incremental_option);
461 if (buf)
462 free (buf);
463 }
464
465 /* Output incremental data for the directory ENTRY to the file DATA.
466 Return nonzero if successful, preserving errno on write failure. */
467 static bool
468 write_directory_file_entry (void *entry, void *data)
469 {
470 struct directory const *directory = entry;
471 FILE *fp = data;
472
473 if (directory->found)
474 {
475 int e;
476 char *str = quote_copy_string (directory->name);
477 fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
478 (unsigned long) directory->device_number,
479 (unsigned long) directory->inode_number,
480 str ? str : directory->name);
481 e = errno;
482 if (str)
483 free (str);
484 errno = e;
485 }
486
487 return ! ferror (fp);
488 }
489
490 void
491 write_directory_file (void)
492 {
493 FILE *fp = listed_incremental_stream;
494
495 if (! fp)
496 return;
497
498 if (fseek (fp, 0L, SEEK_SET) != 0)
499 seek_error (listed_incremental_option);
500 if (ftruncate (fileno (fp), (off_t) 0) != 0)
501 truncate_error (listed_incremental_option);
502
503 fprintf (fp, "%lu\n", (unsigned long) start_time);
504 if (! ferror (fp) && directory_table)
505 hash_do_for_each (directory_table, write_directory_file_entry, fp);
506 if (ferror (fp))
507 write_error (listed_incremental_option);
508 if (fclose (fp) != 0)
509 close_error (listed_incremental_option);
510 }
511 \f
512 /* Restoration of incremental dumps. */
513
514 void
515 gnu_restore (char const *directory_name)
516 {
517 char *archive_dir;
518 char *current_dir;
519 char *cur, *arc;
520 size_t size;
521 size_t copied;
522 union block *data_block;
523 char *to;
524
525 current_dir = savedir (directory_name);
526
527 if (!current_dir)
528 {
529 /* The directory doesn't exist now. It'll be created. In any
530 case, we don't have to delete any files out of it. */
531
532 skip_member ();
533 return;
534 }
535
536 size = current_stat_info.stat.st_size;
537 if (size != current_stat_info.stat.st_size)
538 xalloc_die ();
539 archive_dir = xmalloc (size);
540 to = archive_dir;
541 for (; size > 0; size -= copied)
542 {
543 data_block = find_next_block ();
544 if (!data_block)
545 {
546 ERROR ((0, 0, _("Unexpected EOF in archive")));
547 break; /* FIXME: What happens then? */
548 }
549 copied = available_space_after (data_block);
550 if (copied > size)
551 copied = size;
552 memcpy (to, data_block->buffer, copied);
553 to += copied;
554 set_next_block_after ((union block *)
555 (data_block->buffer + copied - 1));
556 }
557
558 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
559 {
560 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
561 {
562 arc++;
563 if (!strcmp (arc, cur))
564 break;
565 }
566 if (*arc == '\0')
567 {
568 char *p = new_name (directory_name, cur);
569 if (! interactive_option || confirm ("delete", p))
570 {
571 if (verbose_option)
572 fprintf (stdlis, _("%s: Deleting %s\n"),
573 program_name, quote (p));
574 if (! remove_any_file (p, 1))
575 {
576 int e = errno;
577 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
578 }
579 }
580 free (p);
581 }
582
583 }
584 free (current_dir);
585 free (archive_dir);
586 }
This page took 0.068114 seconds and 5 git commands to generate.