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 uintmax_t u
= (errno
= 0, strtoumax (buf
, &ebuf
, 10));
350 if (buf
== ebuf
|| (u
== 0 && errno
== EINVAL
))
351 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
352 _("Invalid time stamp")));
354 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
355 _("Time stamp out of range")));
358 /* FIXME: This should also input nanoseconds, but that will be a
359 change in file format. */
360 newer_mtime_option
.tv_sec
= t
;
361 newer_mtime_option
.tv_nsec
= 0;
364 while (0 < (n
= getline (&buf
, &bufsize
, fp
)))
368 bool nfs
= buf
[0] == '+';
369 char *strp
= buf
+ nfs
;
373 if (buf
[n
- 1] == '\n')
377 dev
= u
= strtoumax (strp
, &ebuf
, 10);
378 if (!isspace (*ebuf
))
379 ERROR ((0, 0, "%s:%ld: %s",
380 quotearg_colon (listed_incremental_option
), lineno
,
381 _("Invalid device number")));
383 ERROR ((0, 0, "%s:%ld: %s",
384 quotearg_colon (listed_incremental_option
), lineno
,
385 _("Device number out of range")));
389 ino
= u
= strtoumax (strp
, &ebuf
, 10);
390 if (!isspace (*ebuf
))
391 ERROR ((0, 0, "%s:%ld: %s",
392 quotearg_colon (listed_incremental_option
), lineno
,
393 _("Invalid inode number")));
395 ERROR ((0, 0, "%s:%ld: %s",
396 quotearg_colon (listed_incremental_option
), lineno
,
397 _("Inode number out of range")));
401 unquote_string (strp
);
402 note_directory (strp
, dev
, ino
, nfs
, 0);
407 read_error (listed_incremental_option
);
412 /* Output incremental data for the directory ENTRY to the file DATA.
413 Return nonzero if successful, preserving errno on write failure. */
415 write_directory_file_entry (void *entry
, void *data
)
417 struct directory
const *directory
= entry
;
420 if (directory
->found
)
423 char buf
[UINTMAX_STRSIZE_BOUND
];
424 char *str
= quote_copy_string (directory
->name
);
428 fprintf (fp
, "%s ", umaxtostr (directory
->device_number
, buf
));
429 fprintf (fp
, "%s ", umaxtostr (directory
->inode_number
, buf
));
430 fprintf (fp
, "%s\n", str
? str
: directory
->name
);
438 return ! ferror (fp
);
442 write_directory_file (void)
444 FILE *fp
= listed_incremental_stream
;
449 if (fseek (fp
, 0L, SEEK_SET
) != 0)
450 seek_error (listed_incremental_option
);
451 if (sys_truncate (fileno (fp
)) != 0)
452 truncate_error (listed_incremental_option
);
454 /* FIXME: This should also output nanoseconds, but that will be a
455 change in file format. */
456 fprintf (fp
, "%lu\n", (unsigned long int) start_time
.tv_sec
);
457 if (! ferror (fp
) && directory_table
)
458 hash_do_for_each (directory_table
, write_directory_file_entry
, fp
);
460 write_error (listed_incremental_option
);
461 if (fclose (fp
) != 0)
462 close_error (listed_incremental_option
);
466 /* Restoration of incremental dumps. */
468 /* Examine the directories under directory_name and delete any
469 files that were not there at the time of the back-up. */
471 purge_directory (char const *directory_name
)
478 union block
*data_block
;
481 current_dir
= savedir (directory_name
);
485 /* The directory doesn't exist now. It'll be created. In any
486 case, we don't have to delete any files out of it. */
492 size
= current_stat_info
.stat
.st_size
;
493 if (size
!= current_stat_info
.stat
.st_size
)
495 archive_dir
= xmalloc (size
);
497 for (; size
> 0; size
-= copied
)
499 data_block
= find_next_block ();
502 ERROR ((0, 0, _("Unexpected EOF in archive")));
503 break; /* FIXME: What happens then? */
505 copied
= available_space_after (data_block
);
508 memcpy (to
, data_block
->buffer
, copied
);
510 set_next_block_after ((union block
*)
511 (data_block
->buffer
+ copied
- 1));
514 for (cur
= current_dir
; *cur
; cur
+= strlen (cur
) + 1)
516 for (arc
= archive_dir
; *arc
; arc
+= strlen (arc
) + 1)
519 if (!strcmp (arc
, cur
))
525 char *p
= new_name (directory_name
, cur
);
527 if (deref_stat (true, p
, &st
))
530 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
531 quotearg_colon (p
)));
534 else if (one_file_system_option
&& st
.st_dev
!= root_device
)
537 _("%s: directory is on a different device: not purging"),
538 quotearg_colon (p
)));
542 if (! interactive_option
|| confirm ("delete", p
))
545 fprintf (stdlis
, _("%s: Deleting %s\n"),
546 program_name
, quote (p
));
547 if (! remove_any_file (p
, RECURSIVE_REMOVE_OPTION
))
550 ERROR ((0, e
, _("%s: Cannot remove"), quotearg_colon (p
)));