]> Dogcows Code - chaz/tar/blob - src/incremen.c
Carefully crafted invalid headers can cause buffer overrun.
[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 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 /* FIXME: This should also input nanoseconds, but that will be a
358 change in file format. */
359 newer_mtime_option.tv_sec = t;
360 newer_mtime_option.tv_nsec = 0;
361 }
362
363 while (0 < (n = getline (&buf, &bufsize, fp)))
364 {
365 dev_t dev;
366 ino_t ino;
367 bool nfs = buf[0] == '+';
368 char *strp = buf + nfs;
369
370 lineno++;
371
372 if (buf[n - 1] == '\n')
373 buf[n - 1] = '\0';
374
375 errno = 0;
376 dev = u = strtoul (strp, &ebuf, 10);
377 if (strp == ebuf || (u == 0 && errno == EINVAL))
378 ERROR ((0, 0, "%s:%ld: %s",
379 quotearg_colon (listed_incremental_option), lineno,
380 _("Invalid device number")));
381 else if (dev != u || (u == -1 && errno == ERANGE))
382 ERROR ((0, 0, "%s:%ld: %s",
383 quotearg_colon (listed_incremental_option), lineno,
384 _("Device number out of range")));
385 strp = ebuf;
386
387 errno = 0;
388 ino = u = strtoul (strp, &ebuf, 10);
389 if (strp == ebuf || (u == 0 && errno == EINVAL))
390 ERROR ((0, 0, "%s:%ld: %s",
391 quotearg_colon (listed_incremental_option), lineno,
392 _("Invalid inode number")));
393 else if (ino != u || (u == -1 && errno == ERANGE))
394 ERROR ((0, 0, "%s:%ld: %s",
395 quotearg_colon (listed_incremental_option), lineno,
396 _("Inode number out of range")));
397 strp = ebuf;
398
399 strp++;
400 unquote_string (strp);
401 note_directory (strp, dev, ino, nfs, 0);
402 }
403 }
404
405 if (ferror (fp))
406 read_error (listed_incremental_option);
407 if (buf)
408 free (buf);
409 }
410
411 /* Output incremental data for the directory ENTRY to the file DATA.
412 Return nonzero if successful, preserving errno on write failure. */
413 static bool
414 write_directory_file_entry (void *entry, void *data)
415 {
416 struct directory const *directory = entry;
417 FILE *fp = data;
418
419 if (directory->found)
420 {
421 int e;
422 char *str = quote_copy_string (directory->name);
423 fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
424 (unsigned long) directory->device_number,
425 (unsigned long) directory->inode_number,
426 str ? str : directory->name);
427 e = errno;
428 if (str)
429 free (str);
430 errno = e;
431 }
432
433 return ! ferror (fp);
434 }
435
436 void
437 write_directory_file (void)
438 {
439 FILE *fp = listed_incremental_stream;
440
441 if (! fp)
442 return;
443
444 if (fseek (fp, 0L, SEEK_SET) != 0)
445 seek_error (listed_incremental_option);
446 if (sys_truncate (fileno (fp)) != 0)
447 truncate_error (listed_incremental_option);
448
449 /* FIXME: This should also output nanoseconds, but that will be a
450 change in file format. */
451 fprintf (fp, "%lu\n", (unsigned long int) start_time.tv_sec);
452 if (! ferror (fp) && directory_table)
453 hash_do_for_each (directory_table, write_directory_file_entry, fp);
454 if (ferror (fp))
455 write_error (listed_incremental_option);
456 if (fclose (fp) != 0)
457 close_error (listed_incremental_option);
458 }
459
460 \f
461 /* Restoration of incremental dumps. */
462
463 /* Examine the directories under directory_name and delete any
464 files that were not there at the time of the back-up. */
465 void
466 purge_directory (char const *directory_name)
467 {
468 char *archive_dir;
469 char *current_dir;
470 char *cur, *arc;
471 size_t size;
472 size_t copied;
473 union block *data_block;
474 char *to;
475
476 current_dir = savedir (directory_name);
477
478 if (!current_dir)
479 {
480 /* The directory doesn't exist now. It'll be created. In any
481 case, we don't have to delete any files out of it. */
482
483 skip_member ();
484 return;
485 }
486
487 size = current_stat_info.stat.st_size;
488 if (size != current_stat_info.stat.st_size)
489 xalloc_die ();
490 archive_dir = xmalloc (size);
491 to = archive_dir;
492 for (; size > 0; size -= copied)
493 {
494 data_block = find_next_block ();
495 if (!data_block)
496 {
497 ERROR ((0, 0, _("Unexpected EOF in archive")));
498 break; /* FIXME: What happens then? */
499 }
500 copied = available_space_after (data_block);
501 if (copied > size)
502 copied = size;
503 memcpy (to, data_block->buffer, copied);
504 to += copied;
505 set_next_block_after ((union block *)
506 (data_block->buffer + copied - 1));
507 }
508
509 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
510 {
511 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
512 {
513 arc++;
514 if (!strcmp (arc, cur))
515 break;
516 }
517 if (*arc == '\0')
518 {
519 struct stat st;
520 char *p = new_name (directory_name, cur);
521
522 if (deref_stat (true, p, &st))
523 {
524 stat_diag (p);
525 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
526 quotearg_colon (p)));
527 continue;
528 }
529 else if (one_file_system_option && st.st_dev != root_device)
530 {
531 WARN((0, 0,
532 _("%s: directory is on a different device: not purging"),
533 quotearg_colon (p)));
534 continue;
535 }
536
537 if (! interactive_option || confirm ("delete", p))
538 {
539 if (verbose_option)
540 fprintf (stdlis, _("%s: Deleting %s\n"),
541 program_name, quote (p));
542 if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION))
543 {
544 int e = errno;
545 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
546 }
547 }
548 free (p);
549 }
550
551 }
552 free (current_dir);
553 free (archive_dir);
554 }
This page took 0.061581 seconds and 5 git commands to generate.