]> Dogcows Code - chaz/tar/blob - src/incremen.c
(read_directory_file): Use strtoumax to read snapshot file contents.
[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 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 uintmax_t u = (errno = 0, strtoumax (buf, &ebuf, 10));
348 time_t t = u;
349
350 if (buf == ebuf || (u == 0 && errno == EINVAL))
351 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
352 _("Invalid time stamp")));
353 else if (t != u)
354 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
355 _("Time stamp out of range")));
356 else
357 {
358 /* FIXME: This should also input nanoseconds, but that will be a
359 change in file format. */
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 = strtoumax (strp, &ebuf, 10);
378 if (!isspace (*ebuf))
379 ERROR ((0, 0, "%s:%ld: %s",
380 quotearg_colon (listed_incremental_option), lineno,
381 _("Invalid device number")));
382 else if (dev != u)
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 = strtoumax (strp, &ebuf, 10);
390 if (!isspace (*ebuf))
391 ERROR ((0, 0, "%s:%ld: %s",
392 quotearg_colon (listed_incremental_option), lineno,
393 _("Invalid inode number")));
394 else if (ino != u)
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 buf[UINTMAX_STRSIZE_BOUND];
424 char *str = quote_copy_string (directory->name);
425
426 if (directory->nfs)
427 fprintf (fp, "+");
428 fprintf (fp, "%s ", umaxtostr (directory->device_number, buf));
429 fprintf (fp, "%s ", umaxtostr (directory->inode_number, buf));
430 fprintf (fp, "%s\n", str ? str : directory->name);
431
432 e = errno;
433 if (str)
434 free (str);
435 errno = e;
436 }
437
438 return ! ferror (fp);
439 }
440
441 void
442 write_directory_file (void)
443 {
444 FILE *fp = listed_incremental_stream;
445
446 if (! fp)
447 return;
448
449 if (fseek (fp, 0L, SEEK_SET) != 0)
450 seek_error (listed_incremental_option);
451 if (sys_truncate (fileno (fp)) != 0)
452 truncate_error (listed_incremental_option);
453
454 /* FIXME: This should also output nanoseconds, but that will be a
455 change in file format. */
456 fprintf (fp, "%lu\n", (unsigned long int) start_time.tv_sec);
457 if (! ferror (fp) && directory_table)
458 hash_do_for_each (directory_table, write_directory_file_entry, fp);
459 if (ferror (fp))
460 write_error (listed_incremental_option);
461 if (fclose (fp) != 0)
462 close_error (listed_incremental_option);
463 }
464
465 \f
466 /* Restoration of incremental dumps. */
467
468 /* Examine the directories under directory_name and delete any
469 files that were not there at the time of the back-up. */
470 void
471 purge_directory (char const *directory_name)
472 {
473 char *archive_dir;
474 char *current_dir;
475 char *cur, *arc;
476 size_t size;
477 size_t copied;
478 union block *data_block;
479 char *to;
480
481 current_dir = savedir (directory_name);
482
483 if (!current_dir)
484 {
485 /* The directory doesn't exist now. It'll be created. In any
486 case, we don't have to delete any files out of it. */
487
488 skip_member ();
489 return;
490 }
491
492 size = current_stat_info.stat.st_size;
493 if (size != current_stat_info.stat.st_size)
494 xalloc_die ();
495 archive_dir = xmalloc (size);
496 to = archive_dir;
497 for (; size > 0; size -= copied)
498 {
499 data_block = find_next_block ();
500 if (!data_block)
501 {
502 ERROR ((0, 0, _("Unexpected EOF in archive")));
503 break; /* FIXME: What happens then? */
504 }
505 copied = available_space_after (data_block);
506 if (copied > size)
507 copied = size;
508 memcpy (to, data_block->buffer, copied);
509 to += copied;
510 set_next_block_after ((union block *)
511 (data_block->buffer + copied - 1));
512 }
513
514 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
515 {
516 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
517 {
518 arc++;
519 if (!strcmp (arc, cur))
520 break;
521 }
522 if (*arc == '\0')
523 {
524 struct stat st;
525 char *p = new_name (directory_name, cur);
526
527 if (deref_stat (true, p, &st))
528 {
529 stat_diag (p);
530 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
531 quotearg_colon (p)));
532 continue;
533 }
534 else if (one_file_system_option && st.st_dev != root_device)
535 {
536 WARN((0, 0,
537 _("%s: directory is on a different device: not purging"),
538 quotearg_colon (p)));
539 continue;
540 }
541
542 if (! interactive_option || confirm ("delete", p))
543 {
544 if (verbose_option)
545 fprintf (stdlis, _("%s: Deleting %s\n"),
546 program_name, quote (p));
547 if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION))
548 {
549 int e = errno;
550 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
551 }
552 }
553 free (p);
554 }
555
556 }
557 free (current_dir);
558 free (archive_dir);
559 }
This page took 0.066147 seconds and 5 git commands to generate.