]> git.gag.com Git - debian/tar/blob - src/incremen.c
Minor changes
[debian/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 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 \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     bool nfs;
93     bool 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       {
191         if (ignore_failed_read_option)
192           savedir_warn (path);
193         else
194           savedir_error (path);
195       }
196     errno = 0;
197
198     name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
199     name_buffer = xmalloc (name_buffer_size + 2);
200     strcpy (name_buffer, path);
201     if (! ISSLASH (path[strlen (path) - 1]))
202       strcat (name_buffer, "/");
203     name_length = strlen (name_buffer);
204
205     directory = find_directory (path);
206     children = directory ? directory->children : CHANGED_CHILDREN;
207
208     accumulator = new_accumulator ();
209
210     if (dirp && children != NO_CHILDREN)
211       for (entry = dirp;
212            (entrylen = strlen (entry)) != 0;
213            entry += entrylen + 1)
214         {
215           if (name_buffer_size <= entrylen + name_length)
216             {
217               do
218                 name_buffer_size += NAME_FIELD_SIZE;
219               while (name_buffer_size <= entrylen + name_length);
220               name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
221             }
222           strcpy (name_buffer + name_length, entry);
223
224           if (excluded_name (name_buffer))
225             add_to_accumulator (accumulator, "N", 1);
226           else
227             {
228               struct stat stat_data;
229
230               if (deref_stat (dereference_option, name_buffer, &stat_data))
231                 {
232                   if (ignore_failed_read_option)
233                     stat_warn (name_buffer);
234                   else
235                     stat_error (name_buffer);
236                   continue;
237                 }
238
239               if (S_ISDIR (stat_data.st_mode))
240                 {
241                   bool nfs = NFS_FILE_STAT (stat_data);
242
243                   if (directory = find_directory (name_buffer), directory)
244                     {
245                       /* With NFS, the same file can have two different devices
246                          if an NFS directory is mounted in multiple locations,
247                          which is relatively common when automounting.
248                          To avoid spurious incremental redumping of
249                          directories, consider all NFS devices as equal,
250                          relying on the i-node to establish differences.  */
251
252                       if (! (((directory->nfs & nfs)
253                               || directory->device_number == stat_data.st_dev)
254                              && directory->inode_number == stat_data.st_ino))
255                         {
256                           if (verbose_option)
257                             WARN ((0, 0, _("%s: Directory has been renamed"),
258                                    quotearg_colon (name_buffer)));
259                           directory->children = ALL_CHILDREN;
260                           directory->nfs = nfs;
261                           directory->device_number = stat_data.st_dev;
262                           directory->inode_number = stat_data.st_ino;
263                         }
264                       directory->found = 1;
265                     }
266                   else
267                     {
268                       if (verbose_option)
269                         WARN ((0, 0, _("%s: Directory is new"),
270                                quotearg_colon (name_buffer)));
271                       directory = note_directory (name_buffer,
272                                                   stat_data.st_dev,
273                                                   stat_data.st_ino, nfs, 1);
274                       directory->children =
275                         ((listed_incremental_option
276                           || newer_mtime_option <= stat_data.st_mtime
277                           || (after_date_option &&
278                               newer_ctime_option <= stat_data.st_ctime))
279                          ? ALL_CHILDREN
280                          : CHANGED_CHILDREN);
281                     }
282
283                   if (one_file_system_option && device != stat_data.st_dev)
284                     directory->children = NO_CHILDREN;
285                   else if (children == ALL_CHILDREN)
286                     directory->children = ALL_CHILDREN;
287
288                   add_to_accumulator (accumulator, "D", 1);
289                 }
290
291               else if (one_file_system_option && device != stat_data.st_dev)
292                 add_to_accumulator (accumulator, "N", 1);
293
294 #ifdef S_ISHIDDEN
295               else if (S_ISHIDDEN (stat_data.st_mode))
296                 {
297                   add_to_accumulator (accumulator, "D", 1);
298                   add_to_accumulator (accumulator, entry, entrylen);
299                   add_to_accumulator (accumulator, "A", 2);
300                   continue;
301                 }
302 #endif
303
304               else
305                 if (children == CHANGED_CHILDREN
306                     && stat_data.st_mtime < newer_mtime_option
307                     && (!after_date_option
308                         || stat_data.st_ctime < newer_ctime_option))
309                   add_to_accumulator (accumulator, "N", 1);
310                 else
311                   add_to_accumulator (accumulator, "Y", 1);
312             }
313
314           add_to_accumulator (accumulator, entry, entrylen + 1);
315         }
316
317     add_to_accumulator (accumulator, "\000\000", 2);
318
319     free (name_buffer);
320     if (dirp)
321       free (dirp);
322   }
323
324   /* Sort the contents of the directory, now that we have it all.  */
325
326   {
327     char *pointer = get_accumulator (accumulator);
328     size_t counter;
329     char *cursor;
330     char *buffer;
331     char **array;
332     char **array_cursor;
333
334     counter = 0;
335     for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
336       counter++;
337
338     if (! counter)
339       {
340         delete_accumulator (accumulator);
341         return 0;
342       }
343
344     array = xmalloc (sizeof (char *) * (counter + 1));
345
346     array_cursor = array;
347     for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
348       *array_cursor++ = cursor;
349     *array_cursor = 0;
350
351     qsort (array, counter, sizeof (char *), compare_dirents);
352
353     buffer = xmalloc (cursor - pointer + 2);
354
355     cursor = buffer;
356     for (array_cursor = array; *array_cursor; array_cursor++)
357       {
358         char *string = *array_cursor;
359
360         while ((*cursor++ = *string++))
361           continue;
362       }
363     *cursor = '\0';
364
365     delete_accumulator (accumulator);
366     free (array);
367     return buffer;
368   }
369 }
370 \f
371 static FILE *listed_incremental_stream;
372
373 void
374 read_directory_file (void)
375 {
376   int fd;
377   FILE *fp;
378   char *buf = 0;
379   size_t bufsize;
380
381   /* Open the file for both read and write.  That way, we can write
382      it later without having to reopen it, and don't have to worry if
383      we chdir in the meantime.  */
384   fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
385   if (fd < 0)
386     {
387       open_error (listed_incremental_option);
388       return;
389     }
390
391   fp = fdopen (fd, "r+");
392   if (! fp)
393     {
394       open_error (listed_incremental_option);
395       close (fd);
396       return;
397     }
398
399   listed_incremental_stream = fp;
400
401   if (0 < getline (&buf, &bufsize, fp))
402     {
403       char *ebuf;
404       int n;
405       long lineno = 1;
406       unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
407       time_t t = u;
408       if (buf == ebuf || (u == 0 && errno == EINVAL))
409         ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
410                 _("Invalid time stamp")));
411       else if (t != u || (u == -1 && errno == ERANGE))
412         ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
413                 _("Time stamp out of range")));
414       else
415         newer_mtime_option = t;
416
417       while (0 < (n = getline (&buf, &bufsize, fp)))
418         {
419           dev_t dev;
420           ino_t ino;
421           bool nfs = buf[0] == '+';
422           char *strp = buf + nfs;
423
424           lineno++;
425
426           if (buf[n - 1] == '\n')
427             buf[n - 1] = '\0';
428
429           errno = 0;
430           dev = u = strtoul (strp, &ebuf, 10);
431           if (strp == ebuf || (u == 0 && errno == EINVAL))
432             ERROR ((0, 0, "%s:%ld: %s",
433                     quotearg_colon (listed_incremental_option), lineno,
434                     _("Invalid device number")));
435           else if (dev != u || (u == -1 && errno == ERANGE))
436             ERROR ((0, 0, "%s:%ld: %s",
437                     quotearg_colon (listed_incremental_option), lineno,
438                     _("Device number out of range")));
439           strp = ebuf;
440
441           errno = 0;
442           ino = u = strtoul (strp, &ebuf, 10);
443           if (strp == ebuf || (u == 0 && errno == EINVAL))
444             ERROR ((0, 0, "%s:%ld: %s",
445                     quotearg_colon (listed_incremental_option), lineno,
446                     _("Invalid inode number")));
447           else if (ino != u || (u == -1 && errno == ERANGE))
448             ERROR ((0, 0, "%s:%ld: %s",
449                     quotearg_colon (listed_incremental_option), lineno,
450                     _("Inode number out of range")));
451           strp = ebuf;
452
453           strp++;
454           unquote_string (strp);
455           note_directory (strp, dev, ino, nfs, 0);
456         }
457     }
458
459   if (ferror (fp))
460     read_error (listed_incremental_option);
461   if (buf)
462     free (buf);
463 }
464
465 /* Output incremental data for the directory ENTRY to the file DATA.
466    Return nonzero if successful, preserving errno on write failure.  */
467 static bool
468 write_directory_file_entry (void *entry, void *data)
469 {
470   struct directory const *directory = entry;
471   FILE *fp = data;
472
473   if (directory->found)
474     {
475       int e;
476       char *str = quote_copy_string (directory->name);
477       fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
478                (unsigned long) directory->device_number,
479                (unsigned long) directory->inode_number,
480                str ? str : directory->name);
481       e = errno;
482       if (str)
483         free (str);
484       errno = e;
485     }
486
487   return ! ferror (fp);
488 }
489
490 void
491 write_directory_file (void)
492 {
493   FILE *fp = listed_incremental_stream;
494
495   if (! fp)
496     return;
497
498   if (fseek (fp, 0L, SEEK_SET) != 0)
499     seek_error (listed_incremental_option);
500   if (ftruncate (fileno (fp), (off_t) 0) != 0)
501     truncate_error (listed_incremental_option);
502
503   fprintf (fp, "%lu\n", (unsigned long) start_time);
504   if (! ferror (fp) && directory_table)
505     hash_do_for_each (directory_table, write_directory_file_entry, fp);
506   if (ferror (fp))
507     write_error (listed_incremental_option);
508   if (fclose (fp) != 0)
509     close_error (listed_incremental_option);
510 }
511 \f
512 /* Restoration of incremental dumps.  */
513
514 void
515 gnu_restore (char const *directory_name)
516 {
517   char *archive_dir;
518   char *current_dir;
519   char *cur, *arc;
520   size_t size;
521   size_t copied;
522   union block *data_block;
523   char *to;
524
525   current_dir = savedir (directory_name);
526
527   if (!current_dir)
528     {
529       /* The directory doesn't exist now.  It'll be created.  In any
530          case, we don't have to delete any files out of it.  */
531
532       skip_member ();
533       return;
534     }
535
536   size = current_stat_info.stat.st_size;
537   if (size != current_stat_info.stat.st_size)
538     xalloc_die ();
539   archive_dir = xmalloc (size);
540   to = archive_dir;
541   for (; size > 0; size -= copied)
542     {
543       data_block = find_next_block ();
544       if (!data_block)
545         {
546           ERROR ((0, 0, _("Unexpected EOF in archive")));
547           break;                /* FIXME: What happens then?  */
548         }
549       copied = available_space_after (data_block);
550       if (copied > size)
551         copied = size;
552       memcpy (to, data_block->buffer, copied);
553       to += copied;
554       set_next_block_after ((union block *)
555                             (data_block->buffer + copied - 1));
556     }
557
558   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
559     {
560       for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
561         {
562           arc++;
563           if (!strcmp (arc, cur))
564             break;
565         }
566       if (*arc == '\0')
567         {
568           char *p = new_name (directory_name, cur);
569           if (! interactive_option || confirm ("delete", p))
570             {
571               if (verbose_option)
572                 fprintf (stdlis, _("%s: Deleting %s\n"),
573                          program_name, quote (p));
574               if (! remove_any_file (p, 1))
575                 {
576                   int e = errno;
577                   ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
578                 }
579             }
580           free (p);
581         }
582
583     }
584   free (current_dir);
585   free (archive_dir);
586 }