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