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