1 /* GNU dump extensions to tar.
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 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]; /* path 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 path 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 PATH. */
122 scan_path (struct obstack
*stk
, char *path
, dev_t device
)
124 char *dirp
= savedir (path
); /* 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 (path
);
139 name_buffer_size
= strlen (path
) + NAME_FIELD_SIZE
;
140 name_buffer
= xmalloc (name_buffer_size
+ 2);
141 strcpy (name_buffer
, path
);
142 if (! ISSLASH (path
[strlen (path
) - 1]))
143 strcat (name_buffer
, "/");
144 name_length
= strlen (name_buffer
);
146 directory
= find_directory (path
);
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
), directory
)
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 || newer_mtime_option
<= stat_data
.st_mtime
213 || (after_date_option
&&
214 newer_ctime_option
<= stat_data
.st_ctime
))
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 && stat_data
.st_mtime
< newer_mtime_option
243 && (!after_date_option
244 || stat_data
.st_ctime
< newer_ctime_option
))
245 obstack_1grow (stk
, 'N');
247 obstack_1grow (stk
, 'Y');
250 obstack_grow (stk
, entry
, entrylen
+ 1);
253 obstack_grow (stk
, "\000\000", 2);
260 /* Sort the contents of the obstack, anr convert it to the char * */
262 sort_obstack (struct obstack
*stk
)
264 char *pointer
= obstack_finish (stk
);
272 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
278 array
= obstack_alloc (stk
, sizeof (char *) * (counter
+ 1));
280 array_cursor
= array
;
281 for (cursor
= pointer
; *cursor
; cursor
+= strlen (cursor
) + 1)
282 *array_cursor
++ = cursor
;
285 qsort (array
, counter
, sizeof (char *), compare_dirents
);
287 buffer
= xmalloc (cursor
- pointer
+ 2);
290 for (array_cursor
= array
; *array_cursor
; array_cursor
++)
292 char *string
= *array_cursor
;
294 while ((*cursor
++ = *string
++))
302 get_directory_contents (char *path
, dev_t device
)
308 scan_path (&stk
, path
, device
);
309 buffer
= sort_obstack (&stk
);
310 obstack_free (&stk
, NULL
);
316 static FILE *listed_incremental_stream
;
319 read_directory_file (void)
326 /* Open the file for both read and write. That way, we can write
327 it later without having to reopen it, and don't have to worry if
328 we chdir in the meantime. */
329 fd
= open (listed_incremental_option
, O_RDWR
| O_CREAT
, MODE_RW
);
332 open_error (listed_incremental_option
);
336 fp
= fdopen (fd
, "r+");
339 open_error (listed_incremental_option
);
344 listed_incremental_stream
= fp
;
346 if (0 < getline (&buf
, &bufsize
, fp
))
351 unsigned long u
= (errno
= 0, strtoul (buf
, &ebuf
, 10));
353 if (buf
== ebuf
|| (u
== 0 && errno
== EINVAL
))
354 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
355 _("Invalid time stamp")));
356 else if (t
!= u
|| (u
== -1 && errno
== ERANGE
))
357 ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option
),
358 _("Time stamp out of range")));
360 newer_mtime_option
= t
;
362 while (0 < (n
= getline (&buf
, &bufsize
, fp
)))
366 bool nfs
= buf
[0] == '+';
367 char *strp
= buf
+ nfs
;
371 if (buf
[n
- 1] == '\n')
375 dev
= u
= strtoul (strp
, &ebuf
, 10);
376 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
377 ERROR ((0, 0, "%s:%ld: %s",
378 quotearg_colon (listed_incremental_option
), lineno
,
379 _("Invalid device number")));
380 else if (dev
!= u
|| (u
== -1 && errno
== ERANGE
))
381 ERROR ((0, 0, "%s:%ld: %s",
382 quotearg_colon (listed_incremental_option
), lineno
,
383 _("Device number out of range")));
387 ino
= u
= strtoul (strp
, &ebuf
, 10);
388 if (strp
== ebuf
|| (u
== 0 && errno
== EINVAL
))
389 ERROR ((0, 0, "%s:%ld: %s",
390 quotearg_colon (listed_incremental_option
), lineno
,
391 _("Invalid inode number")));
392 else if (ino
!= u
|| (u
== -1 && errno
== ERANGE
))
393 ERROR ((0, 0, "%s:%ld: %s",
394 quotearg_colon (listed_incremental_option
), lineno
,
395 _("Inode number out of range")));
399 unquote_string (strp
);
400 note_directory (strp
, dev
, ino
, nfs
, 0);
405 read_error (listed_incremental_option
);
410 /* Output incremental data for the directory ENTRY to the file DATA.
411 Return nonzero if successful, preserving errno on write failure. */
413 write_directory_file_entry (void *entry
, void *data
)
415 struct directory
const *directory
= entry
;
418 if (directory
->found
)
421 char *str
= quote_copy_string (directory
->name
);
422 fprintf (fp
, "+%lu %lu %s\n" + ! directory
->nfs
,
423 (unsigned long) directory
->device_number
,
424 (unsigned long) directory
->inode_number
,
425 str
? str
: directory
->name
);
432 return ! ferror (fp
);
436 write_directory_file (void)
438 FILE *fp
= listed_incremental_stream
;
443 if (fseek (fp
, 0L, SEEK_SET
) != 0)
444 seek_error (listed_incremental_option
);
445 if (sys_truncate (fileno (fp
)) != 0)
446 truncate_error (listed_incremental_option
);
448 fprintf (fp
, "%lu\n", (unsigned long) start_time
);
449 if (! ferror (fp
) && directory_table
)
450 hash_do_for_each (directory_table
, write_directory_file_entry
, fp
);
452 write_error (listed_incremental_option
);
453 if (fclose (fp
) != 0)
454 close_error (listed_incremental_option
);
457 /* Restoration of incremental dumps. */
460 gnu_restore (char const *directory_name
)
467 union block
*data_block
;
470 current_dir
= savedir (directory_name
);
474 /* The directory doesn't exist now. It'll be created. In any
475 case, we don't have to delete any files out of it. */
481 size
= current_stat_info
.stat
.st_size
;
482 if (size
!= current_stat_info
.stat
.st_size
)
484 archive_dir
= xmalloc (size
);
486 for (; size
> 0; size
-= copied
)
488 data_block
= find_next_block ();
491 ERROR ((0, 0, _("Unexpected EOF in archive")));
492 break; /* FIXME: What happens then? */
494 copied
= available_space_after (data_block
);
497 memcpy (to
, data_block
->buffer
, copied
);
499 set_next_block_after ((union block
*)
500 (data_block
->buffer
+ copied
- 1));
503 for (cur
= current_dir
; *cur
; cur
+= strlen (cur
) + 1)
505 for (arc
= archive_dir
; *arc
; arc
+= strlen (arc
) + 1)
508 if (!strcmp (arc
, cur
))
513 char *p
= new_name (directory_name
, cur
);
514 if (! interactive_option
|| confirm ("delete", p
))
517 fprintf (stdlis
, _("%s: Deleting %s\n"),
518 program_name
, quote (p
));
519 if (! remove_any_file (p
, 1))
522 ERROR ((0, e
, _("%s: Cannot remove"), quotearg_colon (p
)));