1 /* GNU dump extensions to tar.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
, unsigned 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 newer_mtime_option
.tv_sec
= t
;
358 newer_mtime_option
.tv_nsec
= 0;
361 while (0 < (n
= getline (&buf
, &bufsize
, fp
)))
365 bool nfs
= buf
[0] == '+';
366 char *strp
= buf
+ nfs
;
370 if (buf
[n
- 1] == '\n')
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")));
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")));
398 unquote_string (strp
);
399 note_directory (strp
, dev
, ino
, nfs
, 0);
404 read_error (listed_incremental_option
);
409 /* Output incremental data for the directory ENTRY to the file DATA.
410 Return nonzero if successful, preserving errno on write failure. */
412 write_directory_file_entry (void *entry
, void *data
)
414 struct directory
const *directory
= entry
;
417 if (directory
->found
)
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
);
431 return ! ferror (fp
);
435 write_directory_file (void)
437 FILE *fp
= listed_incremental_stream
;
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
);
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
);
451 write_error (listed_incremental_option
);
452 if (fclose (fp
) != 0)
453 close_error (listed_incremental_option
);
457 /* Restoration of incremental dumps. */
459 /* Examine the directories under directory_name and delete any
460 files that were not there at the time of the back-up. */
462 purge_directory (char const *directory_name
)
469 union block
*data_block
;
472 current_dir
= savedir (directory_name
);
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. */
483 size
= current_stat_info
.stat
.st_size
;
484 if (size
!= current_stat_info
.stat
.st_size
)
486 archive_dir
= xmalloc (size
);
488 for (; size
> 0; size
-= copied
)
490 data_block
= find_next_block ();
493 ERROR ((0, 0, _("Unexpected EOF in archive")));
494 break; /* FIXME: What happens then? */
496 copied
= available_space_after (data_block
);
499 memcpy (to
, data_block
->buffer
, copied
);
501 set_next_block_after ((union block
*)
502 (data_block
->buffer
+ copied
- 1));
505 for (cur
= current_dir
; *cur
; cur
+= strlen (cur
) + 1)
507 for (arc
= archive_dir
; *arc
; arc
+= strlen (arc
) + 1)
510 if (!strcmp (arc
, cur
))
516 char *p
= new_name (directory_name
, cur
);
518 if (deref_stat (true, p
, &st
))
521 WARN((0, 0, _("%s: Not purging directory: unable to stat"),
522 quotearg_colon (p
)));
525 else if (one_file_system_option
&& st
.st_dev
!= root_device
)
528 _("%s: directory is on a different device: not purging"),
529 quotearg_colon (p
)));
533 if (! interactive_option
|| confirm ("delete", p
))
536 fprintf (stdlis
, _("%s: Deleting %s\n"),
537 program_name
, quote (p
));
538 if (! remove_any_file (p
, RECURSIVE_REMOVE_OPTION
))
541 ERROR ((0, e
, _("%s: Cannot remove"), quotearg_colon (p
)));