Removed accumulator stuff in favor of obstack.
[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 #define obstack_chunk_alloc xmalloc
26 #define obstack_chunk_free free
27 #include <obstack.h>
28
29 /* Incremental dump specialities.  */
30
31 /* Which child files to save under a directory.  */
32 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
33
34 /* Directory attributes.  */
35 struct directory
36   {
37     dev_t device_number;        /* device number for directory */
38     ino_t inode_number;         /* inode number for directory */
39     enum children children;
40     bool nfs;
41     bool found;
42     char name[1];               /* path name of directory */
43   };
44
45 static Hash_table *directory_table;
46
47 #if HAVE_ST_FSTYPE_STRING
48   static char const nfs_string[] = "nfs";
49 # define NFS_FILE_STAT(st) (strcmp ((st).st_fstype, nfs_string) == 0)
50 #else
51 # define ST_DEV_MSB(st) (~ (dev_t) 0 << (sizeof (st).st_dev * CHAR_BIT - 1))
52 # define NFS_FILE_STAT(st) (((st).st_dev & ST_DEV_MSB (st)) != 0)
53 #endif
54
55 /* Calculate the hash of a directory.  */
56 static unsigned
57 hash_directory (void const *entry, unsigned n_buckets)
58 {
59   struct directory const *directory = entry;
60   return hash_string (directory->name, n_buckets);
61 }
62
63 /* Compare two directories for equality.  */
64 static bool
65 compare_directories (void const *entry1, void const *entry2)
66 {
67   struct directory const *directory1 = entry1;
68   struct directory const *directory2 = entry2;
69   return strcmp (directory1->name, directory2->name) == 0;
70 }
71
72 /* Create and link a new directory entry for directory NAME, having a
73    device number DEV and an inode number INO, with NFS indicating
74    whether it is an NFS device and FOUND indicating whether we have
75    found that the directory exists.  */
76 static struct directory *
77 note_directory (char const *name, dev_t dev, ino_t ino, bool nfs, bool found)
78 {
79   size_t size = offsetof (struct directory, name) + strlen (name) + 1;
80   struct directory *directory = xmalloc (size);
81
82   directory->device_number = dev;
83   directory->inode_number = ino;
84   directory->children = CHANGED_CHILDREN;
85   directory->nfs = nfs;
86   directory->found = found;
87   strcpy (directory->name, name);
88
89   if (! ((directory_table
90           || (directory_table = hash_initialize (0, 0, hash_directory,
91                                                  compare_directories, 0)))
92          && hash_insert (directory_table, directory)))
93     xalloc_die ();
94
95   return directory;
96 }
97
98 /* Return a directory entry for a given path NAME, or zero if none found.  */
99 static struct directory *
100 find_directory (char *name)
101 {
102   if (! directory_table)
103     return 0;
104   else
105     {
106       size_t size = offsetof (struct directory, name) + strlen (name) + 1;
107       struct directory *dir = alloca (size);
108       strcpy (dir->name, name);
109       return hash_lookup (directory_table, dir);
110     }
111 }
112
113 static int
114 compare_dirents (const void *first, const void *second)
115 {
116   return strcmp ((*(char *const *) first) + 1,
117                  (*(char *const *) second) + 1);
118 }
119
120 /* Recursively scan the given PATH.  */
121 static void
122 scan_path (struct obstack *stk, char *path, dev_t device)
123 {
124   char *dirp = savedir (path);  /* for scanning directory */
125   char const *entry;    /* directory entry being scanned */
126   size_t entrylen;      /* length of directory entry */
127   char *name_buffer;            /* directory, `/', and directory member */
128   size_t name_buffer_size;      /* allocated size of name_buffer, minus 2 */
129   size_t name_length;           /* used length in name_buffer */
130   struct directory *directory; /* for checking if already already seen */
131   enum children children;
132
133   if (! dirp)
134     {
135       savedir_error (path);
136     }
137   errno = 0;
138
139   name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
140   name_buffer = xmalloc (name_buffer_size + 2);
141   strcpy (name_buffer, path);
142   if (! ISSLASH (path[strlen (path) - 1]))
143     strcat (name_buffer, "/");
144   name_length = strlen (name_buffer);
145
146   directory = find_directory (path);
147   children = directory ? directory->children : CHANGED_CHILDREN;
148   
149   if (dirp && children != NO_CHILDREN)
150     for (entry = dirp;
151          (entrylen = strlen (entry)) != 0;
152          entry += entrylen + 1)
153       {
154         if (name_buffer_size <= entrylen + name_length)
155           {
156             do
157               name_buffer_size += NAME_FIELD_SIZE;
158             while (name_buffer_size <= entrylen + name_length);
159             name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
160           }
161         strcpy (name_buffer + name_length, entry);
162         
163         if (excluded_name (name_buffer))
164           obstack_1grow (stk, 'N');
165         else
166           {
167             struct stat stat_data;
168             
169             if (deref_stat (dereference_option, name_buffer, &stat_data))
170               {
171                 stat_diag (name_buffer);
172                 continue;
173               }
174             
175             if (S_ISDIR (stat_data.st_mode))
176               {
177                 bool nfs = NFS_FILE_STAT (stat_data);
178                 
179                 if (directory = find_directory (name_buffer), directory)
180                   {
181                     /* With NFS, the same file can have two different devices
182                        if an NFS directory is mounted in multiple locations,
183                        which is relatively common when automounting.
184                        To avoid spurious incremental redumping of
185                        directories, consider all NFS devices as equal,
186                        relying on the i-node to establish differences.  */
187                     
188                     if (! (((directory->nfs & nfs)
189                             || directory->device_number == stat_data.st_dev)
190                            && directory->inode_number == stat_data.st_ino))
191                         {
192                           if (verbose_option)
193                             WARN ((0, 0, _("%s: Directory has been renamed"),
194                                    quotearg_colon (name_buffer)));
195                           directory->children = ALL_CHILDREN;
196                           directory->nfs = nfs;
197                           directory->device_number = stat_data.st_dev;
198                           directory->inode_number = stat_data.st_ino;
199                         }
200                     directory->found = 1;
201                   }
202                 else
203                   {
204                     if (verbose_option)
205                       WARN ((0, 0, _("%s: Directory is new"),
206                              quotearg_colon (name_buffer)));
207                     directory = note_directory (name_buffer,
208                                                 stat_data.st_dev,
209                                                 stat_data.st_ino, nfs, 1);
210                     directory->children =
211                       ((listed_incremental_option
212                         || newer_mtime_option <= stat_data.st_mtime
213                         || (after_date_option &&
214                             newer_ctime_option <= stat_data.st_ctime))
215                        ? ALL_CHILDREN
216                        : CHANGED_CHILDREN);
217                   }
218                 
219                 if (one_file_system_option && device != stat_data.st_dev)
220                   directory->children = NO_CHILDREN;
221                 else if (children == ALL_CHILDREN)
222                   directory->children = ALL_CHILDREN;
223
224                 obstack_1grow (stk, 'D');
225               }
226
227             else if (one_file_system_option && device != stat_data.st_dev)
228               obstack_1grow (stk, 'N');
229
230 #ifdef S_ISHIDDEN
231             else if (S_ISHIDDEN (stat_data.st_mode))
232               {
233                 obstack_1grow (stk, 'D');
234                 obstack_grow (stk, entry, entrylen);
235                 obstack_grow (stk, "A", 2);
236                 continue;
237               }
238 #endif
239
240             else
241               if (children == CHANGED_CHILDREN
242                   && stat_data.st_mtime < newer_mtime_option
243                   && (!after_date_option
244                       || stat_data.st_ctime < newer_ctime_option))
245                 obstack_1grow (stk, 'N');
246               else
247                 obstack_1grow (stk, 'Y');
248           }
249         
250         obstack_grow (stk, entry, entrylen + 1);
251       }
252
253   obstack_grow (stk, "\000\000", 2);
254   
255   free (name_buffer);
256   if (dirp)
257     free (dirp);
258 }
259
260 /* Sort the contents of the obstack, anr convert it to the char * */
261 static char *
262 sort_obstack (struct obstack *stk)
263 {
264   char *pointer = obstack_finish (stk);
265   size_t counter;
266   char *cursor;
267   char *buffer;
268   char **array;
269   char **array_cursor;
270   
271   counter = 0;
272   for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
273     counter++;
274   
275   if (!counter)
276     return NULL;
277
278   array = obstack_alloc (stk, sizeof (char *) * (counter + 1));
279   
280   array_cursor = array;
281   for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
282     *array_cursor++ = cursor;
283   *array_cursor = 0;
284
285   qsort (array, counter, sizeof (char *), compare_dirents);
286
287   buffer = xmalloc (cursor - pointer + 2);
288
289   cursor = buffer;
290   for (array_cursor = array; *array_cursor; array_cursor++)
291     {
292       char *string = *array_cursor;
293       
294       while ((*cursor++ = *string++))
295         continue;
296     }
297   *cursor = '\0';
298   return buffer;
299 }
300
301 char *
302 get_directory_contents (char *path, dev_t device)
303 {
304   struct obstack stk;
305   char *buffer;
306
307   obstack_init (&stk);
308   scan_path (&stk, path, device);
309   buffer = sort_obstack (&stk);
310   obstack_free (&stk, NULL);
311   return buffer;;
312 }
313
314 \f
315
316 static FILE *listed_incremental_stream;
317
318 void
319 read_directory_file (void)
320 {
321   int fd;
322   FILE *fp;
323   char *buf = 0;
324   size_t bufsize;
325
326   /* Open the file for both read and write.  That way, we can write
327      it later without having to reopen it, and don't have to worry if
328      we chdir in the meantime.  */
329   fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
330   if (fd < 0)
331     {
332       open_error (listed_incremental_option);
333       return;
334     }
335
336   fp = fdopen (fd, "r+");
337   if (! fp)
338     {
339       open_error (listed_incremental_option);
340       close (fd);
341       return;
342     }
343
344   listed_incremental_stream = fp;
345
346   if (0 < getline (&buf, &bufsize, fp))
347     {
348       char *ebuf;
349       int n;
350       long lineno = 1;
351       unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
352       time_t t = u;
353       if (buf == ebuf || (u == 0 && errno == EINVAL))
354         ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
355                 _("Invalid time stamp")));
356       else if (t != u || (u == -1 && errno == ERANGE))
357         ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
358                 _("Time stamp out of range")));
359       else
360         newer_mtime_option = t;
361
362       while (0 < (n = getline (&buf, &bufsize, fp)))
363         {
364           dev_t dev;
365           ino_t ino;
366           bool nfs = buf[0] == '+';
367           char *strp = buf + nfs;
368
369           lineno++;
370
371           if (buf[n - 1] == '\n')
372             buf[n - 1] = '\0';
373
374           errno = 0;
375           dev = u = strtoul (strp, &ebuf, 10);
376           if (strp == ebuf || (u == 0 && errno == EINVAL))
377             ERROR ((0, 0, "%s:%ld: %s",
378                     quotearg_colon (listed_incremental_option), lineno,
379                     _("Invalid device number")));
380           else if (dev != u || (u == -1 && errno == ERANGE))
381             ERROR ((0, 0, "%s:%ld: %s",
382                     quotearg_colon (listed_incremental_option), lineno,
383                     _("Device number out of range")));
384           strp = ebuf;
385
386           errno = 0;
387           ino = u = strtoul (strp, &ebuf, 10);
388           if (strp == ebuf || (u == 0 && errno == EINVAL))
389             ERROR ((0, 0, "%s:%ld: %s",
390                     quotearg_colon (listed_incremental_option), lineno,
391                     _("Invalid inode number")));
392           else if (ino != u || (u == -1 && errno == ERANGE))
393             ERROR ((0, 0, "%s:%ld: %s",
394                     quotearg_colon (listed_incremental_option), lineno,
395                     _("Inode number out of range")));
396           strp = ebuf;
397
398           strp++;
399           unquote_string (strp);
400           note_directory (strp, dev, ino, nfs, 0);
401         }
402     }
403
404   if (ferror (fp))
405     read_error (listed_incremental_option);
406   if (buf)
407     free (buf);
408 }
409
410 /* Output incremental data for the directory ENTRY to the file DATA.
411    Return nonzero if successful, preserving errno on write failure.  */
412 static bool
413 write_directory_file_entry (void *entry, void *data)
414 {
415   struct directory const *directory = entry;
416   FILE *fp = data;
417
418   if (directory->found)
419     {
420       int e;
421       char *str = quote_copy_string (directory->name);
422       fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
423                (unsigned long) directory->device_number,
424                (unsigned long) directory->inode_number,
425                str ? str : directory->name);
426       e = errno;
427       if (str)
428         free (str);
429       errno = e;
430     }
431
432   return ! ferror (fp);
433 }
434
435 void
436 write_directory_file (void)
437 {
438   FILE *fp = listed_incremental_stream;
439
440   if (! fp)
441     return;
442
443   if (fseek (fp, 0L, SEEK_SET) != 0)
444     seek_error (listed_incremental_option);
445   if (sys_truncate (fileno (fp)) != 0)
446     truncate_error (listed_incremental_option);
447
448   fprintf (fp, "%lu\n", (unsigned long) start_time);
449   if (! ferror (fp) && directory_table)
450     hash_do_for_each (directory_table, write_directory_file_entry, fp);
451   if (ferror (fp))
452     write_error (listed_incremental_option);
453   if (fclose (fp) != 0)
454     close_error (listed_incremental_option);
455 }
456 \f
457 /* Restoration of incremental dumps.  */
458
459 void
460 gnu_restore (char const *directory_name)
461 {
462   char *archive_dir;
463   char *current_dir;
464   char *cur, *arc;
465   size_t size;
466   size_t copied;
467   union block *data_block;
468   char *to;
469
470   current_dir = savedir (directory_name);
471
472   if (!current_dir)
473     {
474       /* The directory doesn't exist now.  It'll be created.  In any
475          case, we don't have to delete any files out of it.  */
476
477       skip_member ();
478       return;
479     }
480
481   size = current_stat_info.stat.st_size;
482   if (size != current_stat_info.stat.st_size)
483     xalloc_die ();
484   archive_dir = xmalloc (size);
485   to = archive_dir;
486   for (; size > 0; size -= copied)
487     {
488       data_block = find_next_block ();
489       if (!data_block)
490         {
491           ERROR ((0, 0, _("Unexpected EOF in archive")));
492           break;                /* FIXME: What happens then?  */
493         }
494       copied = available_space_after (data_block);
495       if (copied > size)
496         copied = size;
497       memcpy (to, data_block->buffer, copied);
498       to += copied;
499       set_next_block_after ((union block *)
500                             (data_block->buffer + copied - 1));
501     }
502
503   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
504     {
505       for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
506         {
507           arc++;
508           if (!strcmp (arc, cur))
509             break;
510         }
511       if (*arc == '\0')
512         {
513           char *p = new_name (directory_name, cur);
514           if (! interactive_option || confirm ("delete", p))
515             {
516               if (verbose_option)
517                 fprintf (stdlis, _("%s: Deleting %s\n"),
518                          program_name, quote (p));
519               if (! remove_any_file (p, 1))
520                 {
521                   int e = errno;
522                   ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
523                 }
524             }
525           free (p);
526         }
527
528     }
529   free (current_dir);
530   free (archive_dir);
531 }