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. */
25 #define obstack_chunk_alloc xmalloc
26 #define obstack_chunk_free free
29 /* Incremental dump specialities. */
31 /* Which child files to save under a directory. */
32 enum children
{NO_CHILDREN
, CHANGED_CHILDREN
, ALL_CHILDREN
};
34 /* Directory attributes. */
37 dev_t device_number
; /* device number for directory */
38 ino_t inode_number
; /* inode number for directory */
39 enum children children
;
42 char name
[1]; /* file name of directory */
45 static Hash_table
*directory_table
;
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)
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)
55 /* Calculate the hash of a directory. */
57 hash_directory (void const *entry
, unsigned n_buckets
)
59 struct directory
const *directory
= entry
;
60 return hash_string (directory
->name
, n_buckets
);
63 /* Compare two directories for equality. */
65 compare_directories (void const *entry1
, void const *entry2
)
67 struct directory
const *directory1
= entry1
;
68 struct directory
const *directory2
= entry2
;
69 return strcmp (directory1
->name
, directory2
->name
) == 0;
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
)
79 size_t size
= offsetof (struct directory
, name
) + strlen (name
) + 1;
80 struct directory
*directory
= xmalloc (size
);
82 directory
->device_number
= dev
;
83 directory
->inode_number
= ino
;
84 directory
->children
= CHANGED_CHILDREN
;
86 directory
->found
= found
;
87 strcpy (directory
->name
, name
);
89 if (! ((directory_table
90 || (directory_table
= hash_initialize (0, 0, hash_directory
,
91 compare_directories
, 0)))
92 && hash_insert (directory_table
, directory
)))
98 /* Return a directory entry for a given file NAME, or zero if none found. */
99 static struct directory
*
100 find_directory (char *name
)
102 if (! directory_table
)
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
);
114 compare_dirents (const void *first
, const void *second
)
116 return strcmp ((*(char *const *) first
) + 1,
117 (*(char *const *) second
) + 1);
120 /* Recursively scan the given directory. */
122 scan_directory (struct obstack
*stk
, char *dir_name
, dev_t device
)
124 char *dirp
= savedir (dir_name
); /* 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 seen */
131 enum children children
;
135 savedir_error (dir_name
);
139 name_buffer_size
= strlen (dir_name
) + NAME_FIELD_SIZE
;
140 name_buffer
= xmalloc (name_buffer_size
+ 2);
141 strcpy (name_buffer
, dir_name
);
142 if (! ISSLASH (dir_name
[strlen (dir_name
) - 1]))
143 strcat (name_buffer
, "/");
144 name_length
= strlen (name_buffer
);
146 directory
= find_directory (dir_name
);
147 children
= directory
? directory
->children
: CHANGED_CHILDREN
;
149 if (dirp
&& children
!= NO_CHILDREN
)
151 (entrylen
= strlen (entry
)) != 0;
152 entry
+= entrylen
+ 1)
154 if (name_buffer_size
<= entrylen
+ name_length
)
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);
161 strcpy (name_buffer
+ name_length
, entry
);
163 if (excluded_name (name_buffer
))
164 obstack_1grow (stk
, 'N');
167 struct stat stat_data
;
169 if (deref_stat (dereference_option
, name_buffer
, &stat_data
))
171 stat_diag (name_buffer
);
175 if (S_ISDIR (stat_data
.st_mode
))
177 bool nfs
= NFS_FILE_STAT (stat_data
);
179 if ((directory
= find_directory (name_buffer
)) != NULL
)
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. */
188 if (! (((directory
->nfs
& nfs
)
189 || directory
->device_number
== stat_data
.st_dev
)
190 && directory
->inode_number
== stat_data
.st_ino
))
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
;
200 directory
->found
= 1;
205 WARN ((0, 0, _("%s: Directory is new"),
206 quotearg_colon (name_buffer
)));
207 directory
= note_directory (name_buffer
,
209 stat_data
.st_ino
, nfs
, 1);
210 directory
->children
=
211 ((listed_incremental_option
212 || OLDER_STAT_TIME (stat_data
, m
)
213 || (after_date_option
214 && OLDER_STAT_TIME (stat_data
, c
)))
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
;
224 obstack_1grow (stk
, 'D');
227 else if (one_file_system_option
&& device
!= stat_data
.st_dev
)
228 obstack_1grow (stk
, 'N');
231 else if (S_ISHIDDEN (stat_data
.st_mode
))
233 obstack_1grow (stk
, 'D');
234 obstack_grow (stk
, entry
, entrylen
);
235 obstack_grow (stk
, "A", 2);
241 if (children
== CHANGED_CHILDREN
242 && OLDER_STAT_TIME (stat_data
, m
)
243 && (!after_date_option
|| OLDER_STAT_TIME (stat_data
, c
)))
244 obstack_1grow (stk
, 'N');
246 obstack_1grow (stk
, 'Y');
249 obstack_grow (stk
, entry
, entrylen
+ 1);
252 obstack_grow (stk
, "\000\000", 2);
259 /* Sort the contents of the obstack, and convert it to the char * */
261 sort_obstack (struct obstack
*stk
)
263 char *pointer
= obstack_finish (stk
);
271 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
277 array
= obstack_alloc (stk
, sizeof (char *) * (counter
+ 1));
279 array_cursor
= array
;
280 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
281 *array_cursor
++ = cursor
;
284 qsort (array
, counter
, sizeof (char *), compare_dirents
);
286 buffer
= xmalloc (cursor
- pointer
+ 2);
289 for (array_cursor
= array
; *array_cursor
; array_cursor
++)
291 char *string
= *array_cursor
;
293 while ((*cursor
++ = *string
++))
301 get_directory_contents (char *dir_name
, dev_t device
)
307 scan_directory (&stk
, dir_name
, device
);
308 buffer
= sort_obstack (&stk
);
309 obstack_free (&stk
, NULL
);
315 static FILE *listed_incremental_stream
;
318 read_directory_file (void)
325 /* Open the file for both read and write. That way, we can write
326 it later without having to reopen it, and don't have to worry if
327 we chdir in the meantime. */
328 fd
= open (listed_incremental_option
, O_RDWR
| O_CREAT
, MODE_RW
);
331 open_error (listed_incremental_option
);
335 fp
= fdopen (fd
, "r+");
338 open_error (listed_incremental_option
);
343 listed_incremental_stream
= fp
;
345 if (0 < getline (&buf
, &bufsize
, fp
))
350 unsigned long u
= (errno
= 0, strtoul (buf
, &ebuf
, 10));
352 if (buf
== ebuf
|| (u
== 0 && errno
== EINVAL
))
353 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
354 _("Invalid time stamp")));
355 else if (t
!= u
|| (u
== -1 && errno
== ERANGE
))
356 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
357 _("Time stamp out of range")));
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
= strtoul (strp
, &ebuf
, 10);
378 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
379 ERROR ((0, 0, "%s:%ld: %s",
380 quotearg_colon (listed_incremental_option
), lineno
,
381 _("Invalid device number")));
382 else if (dev
!= u
|| (u
== -1 && errno
== ERANGE
))
383 ERROR ((0, 0, "%s:%ld: %s",
384 quotearg_colon (listed_incremental_option
), lineno
,
385 _("Device number out of range")));
389 ino
= u
= strtoul (strp
, &ebuf
, 10);
390 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
391 ERROR ((0, 0, "%s:%ld: %s",
392 quotearg_colon (listed_incremental_option
), lineno
,
393 _("Invalid inode number")));
394 else if (ino
!= u
|| (u
== -1 && errno
== ERANGE
))
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 *str
= quote_copy_string (directory
->name
);
424 fprintf (fp
, "+%lu %lu %s\n" + ! directory
->nfs
,
425 (unsigned long) directory
->device_number
,
426 (unsigned long) directory
->inode_number
,
427 str
? str
: directory
->name
);
434 return ! ferror (fp
);
438 write_directory_file (void)
440 FILE *fp
= listed_incremental_stream
;
445 if (fseek (fp
, 0L, SEEK_SET
) != 0)
446 seek_error (listed_incremental_option
);
447 if (sys_truncate (fileno (fp
)) != 0)
448 truncate_error (listed_incremental_option
);
450 fprintf (fp
, "%lu\n", (unsigned long) start_time
);
451 if (! ferror (fp
) && directory_table
)
452 hash_do_for_each (directory_table
, write_directory_file_entry
, fp
);
454 write_error (listed_incremental_option
);
455 if (fclose (fp
) != 0)
456 close_error (listed_incremental_option
);
460 /* Restoration of incremental dumps. */
462 /* Examine the directories under directory_name and delete any
463 files that were not there at the time of the back-up. */
465 purge_directory (char const *directory_name
)
472 union block
*data_block
;
475 current_dir
= savedir (directory_name
);
479 /* The directory doesn't exist now. It'll be created. In any
480 case, we don't have to delete any files out of it. */
486 size
= current_stat_info
.stat
.st_size
;
487 if (size
!= current_stat_info
.stat
.st_size
)
489 archive_dir
= xmalloc (size
);
491 for (; size
> 0; size
-= copied
)
493 data_block
= find_next_block ();
496 ERROR ((0, 0, _("Unexpected EOF in archive")));
497 break; /* FIXME: What happens then? */
499 copied
= available_space_after (data_block
);
502 memcpy (to
, data_block
->buffer
, copied
);
504 set_next_block_after ((union block
*)
505 (data_block
->buffer
+ copied
- 1));
508 for (cur
= current_dir
; *cur
; cur
+= strlen (cur
) + 1)
510 for (arc
= archive_dir
; *arc
; arc
+= strlen (arc
) + 1)
513 if (!strcmp (arc
, cur
))
519 char *p
= new_name (directory_name
, cur
);
521 if (deref_stat (true, p
, &st
))
524 WARN((0, 0, _("%s: Not purging directory %s: unable to stat"),
525 quotearg_colon (p
)));
528 else if (one_file_system_option
&& st
.st_dev
!= root_device
)
531 _("%s: directory %s is on a different device: not purging"),
532 quotearg_colon (p
)));
536 if (! interactive_option
|| confirm ("delete", p
))
539 fprintf (stdlis
, _("%s: Deleting %s\n"),
540 program_name
, quote (p
));
541 if (! remove_any_file (p
, RECURSIVE_REMOVE_OPTION
))
544 ERROR ((0, e
, _("%s: Cannot remove"), quotearg_colon (p
)));