Merge from vendor branch GCC:
[dragonfly.git] / contrib / tar / src / incremen.c
1 /* GNU dump extensions to tar.
2
3    Copyright 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001 Free
4    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 \f
26 /* Variable sized generic character buffers.  */
27
28 struct accumulator
29 {
30   size_t allocated;
31   size_t length;
32   char *pointer;
33 };
34
35 /* Amount of space guaranteed just after a reallocation.  */
36 #define ACCUMULATOR_SLACK 50
37
38 /* Return the accumulated data from an ACCUMULATOR buffer.  */
39 static char *
40 get_accumulator (struct accumulator *accumulator)
41 {
42   return accumulator->pointer;
43 }
44
45 /* Allocate and return a new accumulator buffer.  */
46 static struct accumulator *
47 new_accumulator (void)
48 {
49   struct accumulator *accumulator
50     = xmalloc (sizeof (struct accumulator));
51
52   accumulator->allocated = ACCUMULATOR_SLACK;
53   accumulator->pointer = xmalloc (ACCUMULATOR_SLACK);
54   accumulator->length = 0;
55   return accumulator;
56 }
57
58 /* Deallocate an ACCUMULATOR buffer.  */
59 static void
60 delete_accumulator (struct accumulator *accumulator)
61 {
62   free (accumulator->pointer);
63   free (accumulator);
64 }
65
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)
70 {
71   if (accumulator->length + size > accumulator->allocated)
72     {
73       accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
74       accumulator->pointer =
75         xrealloc (accumulator->pointer, accumulator->allocated);
76     }
77   memcpy (accumulator->pointer + accumulator->length, data, size);
78   accumulator->length += size;
79 }
80 \f
81 /* Incremental dump specialities.  */
82
83 /* Which child files to save under a directory.  */
84 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
85
86 /* Directory attributes.  */
87 struct directory
88   {
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 */
95   };
96
97 static Hash_table *directory_table;
98
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
106
107 /* Calculate the hash of a directory.  */
108 static unsigned
109 hash_directory (void const *entry, unsigned n_buckets)
110 {
111   struct directory const *directory = entry;
112   return hash_string (directory->name, n_buckets);
113 }
114
115 /* Compare two directories for equality.  */
116 static bool
117 compare_directories (void const *entry1, void const *entry2)
118 {
119   struct directory const *directory1 = entry1;
120   struct directory const *directory2 = entry2;
121   return strcmp (directory1->name, directory2->name) == 0;
122 }
123
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)
130 {
131   size_t size = offsetof (struct directory, name) + strlen (name) + 1;
132   struct directory *directory = xmalloc (size);
133
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);
140
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 ();
146
147   return directory;
148 }
149
150 /* Return a directory entry for a given path NAME, or zero if none found.  */
151 static struct directory *
152 find_directory (char *name)
153 {
154   if (! directory_table)
155     return 0;
156   else
157     {
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);
162     }
163 }
164
165 static int
166 compare_dirents (const void *first, const void *second)
167 {
168   return strcmp ((*(char *const *) first) + 1,
169                  (*(char *const *) second) + 1);
170 }
171
172 char *
173 get_directory_contents (char *path, dev_t device)
174 {
175   struct accumulator *accumulator;
176
177   /* Recursively scan the given PATH.  */
178
179   {
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;
188
189     if (! dirp)
190       savedir_error (path);
191     errno = 0;
192
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);
199
200     directory = find_directory (path);
201     children = directory ? directory->children : CHANGED_CHILDREN;
202
203     accumulator = new_accumulator ();
204
205     if (children != NO_CHILDREN)
206       for (entry = dirp;
207            (entrylen = strlen (entry)) != 0;
208            entry += entrylen + 1)
209         {
210           if (name_buffer_size <= entrylen + name_length)
211             {
212               do
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);
216             }
217           strcpy (name_buffer + name_length, entry);
218
219           if (excluded_name (name_buffer))
220             add_to_accumulator (accumulator, "N", 1);
221           else
222             {
223               struct stat stat_data;
224
225               if (deref_stat (dereference_option, name_buffer, &stat_data))
226                 {
227                   if (ignore_failed_read_option)
228                     stat_warn (name_buffer);
229                   else
230                     stat_error (name_buffer);
231                   continue;
232                 }
233
234               if (S_ISDIR (stat_data.st_mode))
235                 {
236                   bool nfs = NFS_FILE_STAT (stat_data);
237
238                   if (directory = find_directory (name_buffer), directory)
239                     {
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.  */
246
247                       if (! (((directory->nfs & nfs)
248                               || directory->device_number == stat_data.st_dev)
249                              && directory->inode_number == stat_data.st_ino))
250                         {
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;
258                         }
259                       directory->found = 1;
260                     }
261                   else
262                     {
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);
276                     }
277
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;
282
283                   add_to_accumulator (accumulator, "D", 1);
284                 }
285
286               else if (one_file_system_option && device != stat_data.st_dev)
287                 add_to_accumulator (accumulator, "N", 1);
288
289 #ifdef S_ISHIDDEN
290               else if (S_ISHIDDEN (stat_data.st_mode))
291                 {
292                   add_to_accumulator (accumulator, "D", 1);
293                   add_to_accumulator (accumulator, entry, entrylen);
294                   add_to_accumulator (accumulator, "A", 2);
295                   continue;
296                 }
297 #endif
298
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);
307             }
308
309           add_to_accumulator (accumulator, entry, entrylen + 1);
310         }
311
312     add_to_accumulator (accumulator, "\000\000", 2);
313
314     free (name_buffer);
315     free (dirp);
316   }
317
318   /* Sort the contents of the directory, now that we have it all.  */
319
320   {
321     char *pointer = get_accumulator (accumulator);
322     size_t counter;
323     char *cursor;
324     char *buffer;
325     char **array;
326     char **array_cursor;
327
328     counter = 0;
329     for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
330       counter++;
331
332     if (! counter)
333       {
334         delete_accumulator (accumulator);
335         return 0;
336       }
337
338     array = xmalloc (sizeof (char *) * (counter + 1));
339
340     array_cursor = array;
341     for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
342       *array_cursor++ = cursor;
343     *array_cursor = 0;
344
345     qsort (array, counter, sizeof (char *), compare_dirents);
346
347     buffer = xmalloc (cursor - pointer + 2);
348
349     cursor = buffer;
350     for (array_cursor = array; *array_cursor; array_cursor++)
351       {
352         char *string = *array_cursor;
353
354         while ((*cursor++ = *string++))
355           continue;
356       }
357     *cursor = '\0';
358
359     delete_accumulator (accumulator);
360     free (array);
361     return buffer;
362   }
363 }
364 \f
365 static FILE *listed_incremental_stream;
366
367 void
368 read_directory_file (void)
369 {
370   int fd;
371   FILE *fp;
372   char *buf = 0;
373   size_t bufsize;
374
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)
380     {
381       open_error (listed_incremental_option);
382       return;
383     }
384
385   fp = fdopen (fd, "r+");
386   if (! fp)
387     {
388       open_error (listed_incremental_option);
389       close (fd);
390       return;
391     }
392
393   listed_incremental_stream = fp;
394
395   if (0 < getline (&buf, &bufsize, fp))
396     {
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;
410
411       while (0 < (n = getline (&buf, &bufsize, fp)))
412         {
413           dev_t dev;
414           ino_t ino;
415           int nfs = buf[0] == '+';
416           char *strp = buf + nfs;
417
418           lineno++;
419
420           if (buf[n - 1] == '\n')
421             buf[n - 1] = '\0';
422
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;
434
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;
446
447           strp++;
448           unquote_string (strp);
449           note_directory (strp, dev, ino, nfs, 0);
450         }
451     }
452
453   if (ferror (fp))
454     read_error (listed_incremental_option);
455   if (buf)
456     free (buf);
457 }
458
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)
463 {
464   struct directory const *directory = entry;
465   FILE *fp = data;
466
467   if (directory->found)
468     {
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;
479     }
480
481   return ! ferror (fp);
482 }
483
484 void
485 write_directory_file (void)
486 {
487   FILE *fp = listed_incremental_stream;
488
489   if (! fp)
490     return;
491
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);
496
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);
504 }
505 \f
506 /* Restoration of incremental dumps.  */
507
508 void
509 gnu_restore (size_t skipcrud)
510 {
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;
518
519 #define CURRENT_FILE_NAME (skipcrud + current_file_name)
520
521   current_dir = savedir (CURRENT_FILE_NAME);
522
523   if (!current_dir)
524     {
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.  */
527
528       skip_member ();
529       return;
530     }
531
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)
538     {
539       data_block = find_next_block ();
540       if (!data_block)
541         {
542           ERROR ((0, 0, _("Unexpected EOF in archive")));
543           break;                /* FIXME: What happens then?  */
544         }
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));
552     }
553
554   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
555     {
556       for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
557         {
558           arc++;
559           if (!strcmp (arc, cur))
560             break;
561         }
562       if (*arc == '\0')
563         {
564           char *p = new_name (CURRENT_FILE_NAME, cur);
565           if (! interactive_option || confirm ("delete", p))
566             {
567               if (verbose_option)
568                 fprintf (stdlis, _("%s: Deleting %s\n"),
569                          program_name, quote (p));
570               if (! remove_any_file (p, 1))
571                 {
572                   int e = errno;
573                   ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
574                 }
575             }
576           free (p);
577         }
578
579     }
580   free (current_dir);
581   free (archive_dir);
582
583 #undef CURRENT_FILE_NAME
584 }