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