Implement window size passing between real tty and virtual console.
[dragonfly/port-amd64.git] / contrib / tar / src / incremen.c
blob7dd43e9644155884f8bca8fecf75051b91094a5a
1 /* GNU dump extensions to tar.
3 Copyright 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001 Free
4 Software Foundation, Inc.
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.
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.
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. */
20 #include "system.h"
21 #include <getline.h>
22 #include <hash.h>
23 #include <quotearg.h>
24 #include "common.h"
26 /* Variable sized generic character buffers. */
28 struct accumulator
30 size_t allocated;
31 size_t length;
32 char *pointer;
35 /* Amount of space guaranteed just after a reallocation. */
36 #define ACCUMULATOR_SLACK 50
38 /* Return the accumulated data from an ACCUMULATOR buffer. */
39 static char *
40 get_accumulator (struct accumulator *accumulator)
42 return accumulator->pointer;
45 /* Allocate and return a new accumulator buffer. */
46 static struct accumulator *
47 new_accumulator (void)
49 struct accumulator *accumulator
50 = xmalloc (sizeof (struct accumulator));
52 accumulator->allocated = ACCUMULATOR_SLACK;
53 accumulator->pointer = xmalloc (ACCUMULATOR_SLACK);
54 accumulator->length = 0;
55 return accumulator;
58 /* Deallocate an ACCUMULATOR buffer. */
59 static void
60 delete_accumulator (struct accumulator *accumulator)
62 free (accumulator->pointer);
63 free (accumulator);
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)
71 if (accumulator->length + size > accumulator->allocated)
73 accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
74 accumulator->pointer =
75 xrealloc (accumulator->pointer, accumulator->allocated);
77 memcpy (accumulator->pointer + accumulator->length, data, size);
78 accumulator->length += size;
81 /* Incremental dump specialities. */
83 /* Which child files to save under a directory. */
84 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
86 /* Directory attributes. */
87 struct directory
89 dev_t device_number; /* device number for directory */
90 ino_t inode_number; /* inode number for directory */
91 enum children children;
92 char nfs;
93 char found;
94 char name[1]; /* path name of directory */
97 static Hash_table *directory_table;
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
107 /* Calculate the hash of a directory. */
108 static unsigned
109 hash_directory (void const *entry, unsigned n_buckets)
111 struct directory const *directory = entry;
112 return hash_string (directory->name, n_buckets);
115 /* Compare two directories for equality. */
116 static bool
117 compare_directories (void const *entry1, void const *entry2)
119 struct directory const *directory1 = entry1;
120 struct directory const *directory2 = entry2;
121 return strcmp (directory1->name, directory2->name) == 0;
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)
131 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
132 struct directory *directory = xmalloc (size);
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);
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 ();
147 return directory;
150 /* Return a directory entry for a given path NAME, or zero if none found. */
151 static struct directory *
152 find_directory (char *name)
154 if (! directory_table)
155 return 0;
156 else
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);
165 static int
166 compare_dirents (const void *first, const void *second)
168 return strcmp ((*(char *const *) first) + 1,
169 (*(char *const *) second) + 1);
172 char *
173 get_directory_contents (char *path, dev_t device)
175 struct accumulator *accumulator;
177 /* Recursively scan the given PATH. */
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;
189 if (! dirp)
190 savedir_error (path);
191 errno = 0;
193 name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
194 name_buffer = xmalloc (name_buffer_size + 2);
195 strcpy (name_buffer, path);
196 if (! ISSLASH (path[strlen (path) - 1]))
197 strcat (name_buffer, "/");
198 name_length = strlen (name_buffer);
200 directory = find_directory (path);
201 children = directory ? directory->children : CHANGED_CHILDREN;
203 accumulator = new_accumulator ();
205 if (children != NO_CHILDREN)
206 for (entry = dirp;
207 (entrylen = strlen (entry)) != 0;
208 entry += entrylen + 1)
210 if (name_buffer_size <= entrylen + name_length)
213 name_buffer_size += NAME_FIELD_SIZE;
214 while (name_buffer_size <= entrylen + name_length);
215 name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
217 strcpy (name_buffer + name_length, entry);
219 if (excluded_name (name_buffer))
220 add_to_accumulator (accumulator, "N", 1);
221 else
223 struct stat stat_data;
225 if (deref_stat (dereference_option, name_buffer, &stat_data))
227 if (ignore_failed_read_option)
228 stat_warn (name_buffer);
229 else
230 stat_error (name_buffer);
231 continue;
234 if (S_ISDIR (stat_data.st_mode))
236 bool nfs = NFS_FILE_STAT (stat_data);
238 if (directory = find_directory (name_buffer), directory)
240 /* With NFS, the same file can have two different devices
241 if an NFS directory is mounted in multiple locations,
242 which is relatively common when automounting.
243 To avoid spurious incremental redumping of
244 directories, consider all NFS devices as equal,
245 relying on the i-node to establish differences. */
247 if (! (((directory->nfs & nfs)
248 || directory->device_number == stat_data.st_dev)
249 && directory->inode_number == stat_data.st_ino))
251 if (verbose_option)
252 WARN ((0, 0, _("%s: Directory has been renamed"),
253 quotearg_colon (name_buffer)));
254 directory->children = ALL_CHILDREN;
255 directory->nfs = nfs;
256 directory->device_number = stat_data.st_dev;
257 directory->inode_number = stat_data.st_ino;
259 directory->found = 1;
261 else
263 if (verbose_option)
264 WARN ((0, 0, _("%s: Directory is new"),
265 quotearg_colon (name_buffer)));
266 directory = note_directory (name_buffer,
267 stat_data.st_dev,
268 stat_data.st_ino, nfs, 1);
269 directory->children =
270 ((listed_incremental_option
271 || newer_mtime_option <= stat_data.st_mtime
272 || (after_date_option &&
273 newer_ctime_option <= stat_data.st_ctime))
274 ? ALL_CHILDREN
275 : CHANGED_CHILDREN);
278 if (one_file_system_option && device != stat_data.st_dev)
279 directory->children = NO_CHILDREN;
280 else if (children == ALL_CHILDREN)
281 directory->children = ALL_CHILDREN;
283 add_to_accumulator (accumulator, "D", 1);
286 else if (one_file_system_option && device != stat_data.st_dev)
287 add_to_accumulator (accumulator, "N", 1);
289 #ifdef S_ISHIDDEN
290 else if (S_ISHIDDEN (stat_data.st_mode))
292 add_to_accumulator (accumulator, "D", 1);
293 add_to_accumulator (accumulator, entry, entrylen);
294 add_to_accumulator (accumulator, "A", 2);
295 continue;
297 #endif
299 else
300 if (children == CHANGED_CHILDREN
301 && stat_data.st_mtime < newer_mtime_option
302 && (!after_date_option
303 || stat_data.st_ctime < newer_ctime_option))
304 add_to_accumulator (accumulator, "N", 1);
305 else
306 add_to_accumulator (accumulator, "Y", 1);
309 add_to_accumulator (accumulator, entry, entrylen + 1);
312 add_to_accumulator (accumulator, "\000\000", 2);
314 free (name_buffer);
315 free (dirp);
318 /* Sort the contents of the directory, now that we have it all. */
321 char *pointer = get_accumulator (accumulator);
322 size_t counter;
323 char *cursor;
324 char *buffer;
325 char **array;
326 char **array_cursor;
328 counter = 0;
329 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
330 counter++;
332 if (! counter)
334 delete_accumulator (accumulator);
335 return 0;
338 array = xmalloc (sizeof (char *) * (counter + 1));
340 array_cursor = array;
341 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
342 *array_cursor++ = cursor;
343 *array_cursor = 0;
345 qsort (array, counter, sizeof (char *), compare_dirents);
347 buffer = xmalloc (cursor - pointer + 2);
349 cursor = buffer;
350 for (array_cursor = array; *array_cursor; array_cursor++)
352 char *string = *array_cursor;
354 while ((*cursor++ = *string++))
355 continue;
357 *cursor = '\0';
359 delete_accumulator (accumulator);
360 free (array);
361 return buffer;
365 static FILE *listed_incremental_stream;
367 void
368 read_directory_file (void)
370 int fd;
371 FILE *fp;
372 char *buf = 0;
373 size_t bufsize;
375 /* Open the file for both read and write. That way, we can write
376 it later without having to reopen it, and don't have to worry if
377 we chdir in the meantime. */
378 fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
379 if (fd < 0)
381 open_error (listed_incremental_option);
382 return;
385 fp = fdopen (fd, "r+");
386 if (! fp)
388 open_error (listed_incremental_option);
389 close (fd);
390 return;
393 listed_incremental_stream = fp;
395 if (0 < getline (&buf, &bufsize, fp))
397 char *ebuf;
398 int n;
399 long lineno = 1;
400 unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
401 time_t t = u;
402 if (buf == ebuf || (u == 0 && errno == EINVAL))
403 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
404 _("Invalid time stamp")));
405 else if (t != u || (u == -1 && errno == ERANGE))
406 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
407 _("Time stamp out of range")));
408 else
409 newer_mtime_option = t;
411 while (0 < (n = getline (&buf, &bufsize, fp)))
413 dev_t dev;
414 ino_t ino;
415 int nfs = buf[0] == '+';
416 char *strp = buf + nfs;
418 lineno++;
420 if (buf[n - 1] == '\n')
421 buf[n - 1] = '\0';
423 errno = 0;
424 dev = u = strtoul (strp, &ebuf, 10);
425 if (strp == ebuf || (u == 0 && errno == EINVAL))
426 ERROR ((0, 0, "%s:%ld: %s",
427 quotearg_colon (listed_incremental_option), lineno,
428 _("Invalid device number")));
429 else if (dev != u || (u == -1 && errno == ERANGE))
430 ERROR ((0, 0, "%s:%ld: %s",
431 quotearg_colon (listed_incremental_option), lineno,
432 _("Device number out of range")));
433 strp = ebuf;
435 errno = 0;
436 ino = u = strtoul (strp, &ebuf, 10);
437 if (strp == ebuf || (u == 0 && errno == EINVAL))
438 ERROR ((0, 0, "%s:%ld: %s",
439 quotearg_colon (listed_incremental_option), lineno,
440 _("Invalid inode number")));
441 else if (ino != u || (u == -1 && errno == ERANGE))
442 ERROR ((0, 0, "%s:%ld: %s",
443 quotearg_colon (listed_incremental_option), lineno,
444 _("Inode number out of range")));
445 strp = ebuf;
447 strp++;
448 unquote_string (strp);
449 note_directory (strp, dev, ino, nfs, 0);
453 if (ferror (fp))
454 read_error (listed_incremental_option);
455 if (buf)
456 free (buf);
459 /* Output incremental data for the directory ENTRY to the file DATA.
460 Return nonzero if successful, preserving errno on write failure. */
461 static bool
462 write_directory_file_entry (void *entry, void *data)
464 struct directory const *directory = entry;
465 FILE *fp = data;
467 if (directory->found)
469 int e;
470 char *str = quote_copy_string (directory->name);
471 fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
472 (unsigned long) directory->device_number,
473 (unsigned long) directory->inode_number,
474 str ? str : directory->name);
475 e = errno;
476 if (str)
477 free (str);
478 errno = e;
481 return ! ferror (fp);
484 void
485 write_directory_file (void)
487 FILE *fp = listed_incremental_stream;
489 if (! fp)
490 return;
492 if (fseek (fp, 0L, SEEK_SET) != 0)
493 seek_error (listed_incremental_option);
494 if (ftruncate (fileno (fp), (off_t) 0) != 0)
495 truncate_error (listed_incremental_option);
497 fprintf (fp, "%lu\n", (unsigned long) start_time);
498 if (! ferror (fp) && directory_table)
499 hash_do_for_each (directory_table, write_directory_file_entry, fp);
500 if (ferror (fp))
501 write_error (listed_incremental_option);
502 if (fclose (fp) != 0)
503 close_error (listed_incremental_option);
506 /* Restoration of incremental dumps. */
508 void
509 gnu_restore (size_t skipcrud)
511 char *archive_dir;
512 char *current_dir;
513 char *cur, *arc;
514 size_t size;
515 size_t copied;
516 union block *data_block;
517 char *to;
519 #define CURRENT_FILE_NAME (skipcrud + current_file_name)
521 current_dir = savedir (CURRENT_FILE_NAME);
523 if (!current_dir)
525 /* The directory doesn't exist now. It'll be created. In any
526 case, we don't have to delete any files out of it. */
528 skip_member ();
529 return;
532 size = current_stat.st_size;
533 if (size != current_stat.st_size)
534 xalloc_die ();
535 archive_dir = xmalloc (size);
536 to = archive_dir;
537 for (; size > 0; size -= copied)
539 data_block = find_next_block ();
540 if (!data_block)
542 ERROR ((0, 0, _("Unexpected EOF in archive")));
543 break; /* FIXME: What happens then? */
545 copied = available_space_after (data_block);
546 if (copied > size)
547 copied = size;
548 memcpy (to, data_block->buffer, copied);
549 to += copied;
550 set_next_block_after ((union block *)
551 (data_block->buffer + copied - 1));
554 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
556 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
558 arc++;
559 if (!strcmp (arc, cur))
560 break;
562 if (*arc == '\0')
564 char *p = new_name (CURRENT_FILE_NAME, cur);
565 if (! interactive_option || confirm ("delete", p))
567 if (verbose_option)
568 fprintf (stdlis, _("%s: Deleting %s\n"),
569 program_name, quote (p));
570 if (! remove_any_file (p, 1))
572 int e = errno;
573 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
576 free (p);
580 free (current_dir);
581 free (archive_dir);
583 #undef CURRENT_FILE_NAME