1 /* GNU dump extensions to tar.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004, 2005 Free 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
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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
26 /* Incremental dump specialities. */
28 /* Which child files to save under a directory. */
29 enum children
{NO_CHILDREN
, CHANGED_CHILDREN
, ALL_CHILDREN
};
31 /* Directory attributes. */
34 dev_t device_number
; /* device number for directory */
35 ino_t inode_number
; /* inode number for directory */
36 enum children children
;
39 char name
[1]; /* file name of directory */
42 static Hash_table
*directory_table
;
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)
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)
52 /* Calculate the hash of a directory. */
54 hash_directory (void const *entry
, size_t n_buckets
)
56 struct directory
const *directory
= entry
;
57 return hash_string (directory
->name
, n_buckets
);
60 /* Compare two directories for equality. */
62 compare_directories (void const *entry1
, void const *entry2
)
64 struct directory
const *directory1
= entry1
;
65 struct directory
const *directory2
= entry2
;
66 return strcmp (directory1
->name
, directory2
->name
) == 0;
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
)
76 size_t size
= offsetof (struct directory
, name
) + strlen (name
) + 1;
77 struct directory
*directory
= xmalloc (size
);
79 directory
->device_number
= dev
;
80 directory
->inode_number
= ino
;
81 directory
->children
= CHANGED_CHILDREN
;
83 directory
->found
= found
;
84 strcpy (directory
->name
, name
);
86 if (! ((directory_table
87 || (directory_table
= hash_initialize (0, 0, hash_directory
,
88 compare_directories
, 0)))
89 && hash_insert (directory_table
, directory
)))
95 /* Return a directory entry for a given file NAME, or zero if none found. */
96 static struct directory
*
97 find_directory (char *name
)
99 if (! directory_table
)
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
);
111 compare_dirents (const void *first
, const void *second
)
113 return strcmp ((*(char *const *) first
) + 1,
114 (*(char *const *) second
) + 1);
117 /* Recursively scan the given directory. */
119 scan_directory (struct obstack
*stk
, char *dir_name
, dev_t device
)
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
;
132 savedir_error (dir_name
);
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
);
143 directory
= find_directory (dir_name
);
144 children
= directory
? directory
->children
: CHANGED_CHILDREN
;
146 if (dirp
&& children
!= NO_CHILDREN
)
148 (entrylen
= strlen (entry
)) != 0;
149 entry
+= entrylen
+ 1)
151 if (name_buffer_size
<= entrylen
+ name_length
)
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);
158 strcpy (name_buffer
+ name_length
, entry
);
160 if (excluded_name (name_buffer
))
161 obstack_1grow (stk
, 'N');
164 struct stat stat_data
;
166 if (deref_stat (dereference_option
, name_buffer
, &stat_data
))
168 stat_diag (name_buffer
);
172 if (S_ISDIR (stat_data
.st_mode
))
174 bool nfs
= NFS_FILE_STAT (stat_data
);
176 if ((directory
= find_directory (name_buffer
)) != NULL
)
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. */
185 if (! (((directory
->nfs
& nfs
)
186 || directory
->device_number
== stat_data
.st_dev
)
187 && directory
->inode_number
== stat_data
.st_ino
))
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
;
197 directory
->found
= 1;
202 WARN ((0, 0, _("%s: Directory is new"),
203 quotearg_colon (name_buffer
)));
204 directory
= note_directory (name_buffer
,
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
)))
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
;
221 obstack_1grow (stk
, 'D');
224 else if (one_file_system_option
&& device
!= stat_data
.st_dev
)
225 obstack_1grow (stk
, 'N');
228 else if (S_ISHIDDEN (stat_data
.st_mode
))
230 obstack_1grow (stk
, 'D');
231 obstack_grow (stk
, entry
, entrylen
);
232 obstack_grow (stk
, "A", 2);
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');
243 obstack_1grow (stk
, 'Y');
246 obstack_grow (stk
, entry
, entrylen
+ 1);
249 obstack_grow (stk
, "\000\000", 2);
256 /* Sort the contents of the obstack, and convert it to the char * */
258 sort_obstack (struct obstack
*stk
)
260 char *pointer
= obstack_finish (stk
);
268 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
274 array
= obstack_alloc (stk
, sizeof (char *) * (counter
+ 1));
276 array_cursor
= array
;
277 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
278 *array_cursor
++ = cursor
;
281 qsort (array
, counter
, sizeof (char *), compare_dirents
);
283 buffer
= xmalloc (cursor
- pointer
+ 2);
286 for (array_cursor
= array
; *array_cursor
; array_cursor
++)
288 char *string
= *array_cursor
;
290 while ((*cursor
++ = *string
++))
298 get_directory_contents (char *dir_name
, dev_t device
)
304 scan_directory (&stk
, dir_name
, device
);
305 buffer
= sort_obstack (&stk
);
306 obstack_free (&stk
, NULL
);
312 static FILE *listed_incremental_stream
;
315 read_directory_file (void)
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
);
328 open_error (listed_incremental_option
);
332 fp
= fdopen (fd
, "r+");
335 open_error (listed_incremental_option
);
340 listed_incremental_stream
= fp
;
342 if (0 < getline (&buf
, &bufsize
, fp
))
347 unsigned long u
= (errno
= 0, strtoul (buf
, &ebuf
, 10));
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")));
357 /* FIXME: This should also input nanoseconds, but that will be a
358 change in file format. */
359 newer_mtime_option
.tv_sec
= t
;
360 newer_mtime_option
.tv_nsec
= 0;
363 while (0 < (n
= getline (&buf
, &bufsize
, fp
)))
367 bool nfs
= buf
[0] == '+';
368 char *strp
= buf
+ nfs
;
372 if (buf
[n
- 1] == '\n')
376 dev
= u
= strtoul (strp
, &ebuf
, 10);
377 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
378 ERROR ((0, 0, "%s:%ld: %s",
379 quotearg_colon (listed_incremental_option
), lineno
,
380 _("Invalid device number")));
381 else if (dev
!= u
|| (u
== -1 && errno
== ERANGE
))
382 ERROR ((0, 0, "%s:%ld: %s",
383 quotearg_colon (listed_incremental_option
), lineno
,
384 _("Device number out of range")));
388 ino
= u
= strtoul (strp
, &ebuf
, 10);
389 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
390 ERROR ((0, 0, "%s:%ld: %s",
391 quotearg_colon (listed_incremental_option
), lineno
,
392 _("Invalid inode number")));
393 else if (ino
!= u
|| (u
== -1 && errno
== ERANGE
))
394 ERROR ((0, 0, "%s:%ld: %s",
395 quotearg_colon (listed_incremental_option
), lineno
,
396 _("Inode number out of range")));
400 unquote_string (strp
);
401 note_directory (strp
, dev
, ino
, nfs
, 0);
406 read_error (listed_incremental_option
);
411 /* Output incremental data for the directory ENTRY to the file DATA.
412 Return nonzero if successful, preserving errno on write failure. */
414 write_directory_file_entry (void *entry
, void *data
)
416 struct directory
const *directory
= entry
;
419 if (directory
->found
)
422 char *str
= quote_copy_string (directory
->name
);
423 fprintf (fp
, "+%lu %lu %s\n" + ! directory
->nfs
,
424 (unsigned long) directory
->device_number
,
425 (unsigned long) directory
->inode_number
,
426 str
? str
: directory
->name
);
433 return ! ferror (fp
);
437 write_directory_file (void)
439 FILE *fp
= listed_incremental_stream
;
444 if (fseek (fp
, 0L, SEEK_SET
) != 0)
445 seek_error (listed_incremental_option
);
446 if (sys_truncate (fileno (fp
)) != 0)
447 truncate_error (listed_incremental_option
);
449 /* FIXME: This should also output nanoseconds, but that will be a
450 change in file format. */
451 fprintf (fp
, "%lu\n", (unsigned long int) start_time
.tv_sec
);
452 if (! ferror (fp
) && directory_table
)
453 hash_do_for_each (directory_table
, write_directory_file_entry
, fp
);
455 write_error (listed_incremental_option
);
456 if (fclose (fp
) != 0)
457 close_error (listed_incremental_option
);
461 /* Restoration of incremental dumps. */
463 /* Examine the directories under directory_name and delete any
464 files that were not there at the time of the back-up. */
466 purge_directory (char const *directory_name
)
473 union block
*data_block
;
476 current_dir
= savedir (directory_name
);
480 /* The directory doesn't exist now. It'll be created. In any
481 case, we don't have to delete any files out of it. */
487 size
= current_stat_info
.stat
.st_size
;
488 if (size
!= current_stat_info
.stat
.st_size
)
490 archive_dir
= xmalloc (size
);
492 for (; size
> 0; size
-= copied
)
494 data_block
= find_next_block ();
497 ERROR ((0, 0, _("Unexpected EOF in archive")));
498 break; /* FIXME: What happens then? */
500 copied
= available_space_after (data_block
);
503 memcpy (to
, data_block
->buffer
, copied
);
505 set_next_block_after ((union block
*)
506 (data_block
->buffer
+ copied
- 1));
509 for (cur
= current_dir
; *cur
; cur
+= strlen (cur
) + 1)
511 for (arc
= archive_dir
; *arc
; arc
+= strlen (arc
) + 1)
514 if (!strcmp (arc
, cur
))
520 char *p
= new_name (directory_name
, cur
);
522 if (deref_stat (true, p
, &st
))
525 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
526 quotearg_colon (p
)));
529 else if (one_file_system_option
&& st
.st_dev
!= root_device
)
532 _("%s: directory is on a different device: not purging"),
533 quotearg_colon (p
)));
537 if (! interactive_option
|| confirm ("delete", p
))
540 fprintf (stdlis
, _("%s: Deleting %s\n"),
541 program_name
, quote (p
));
542 if (! remove_any_file (p
, RECURSIVE_REMOVE_OPTION
))
545 ERROR ((0, e
, _("%s: Cannot remove"), quotearg_colon (p
)));