1 | /* GNU dump extensions to tar.
|
---|
2 |
|
---|
3 | Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
|
---|
4 | 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
---|
5 |
|
---|
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
|
---|
9 | version.
|
---|
10 |
|
---|
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.
|
---|
15 |
|
---|
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. */
|
---|
19 |
|
---|
20 | #include <system.h>
|
---|
21 | #include <getline.h>
|
---|
22 | #include <hash.h>
|
---|
23 | #include <quotearg.h>
|
---|
24 | #include "common.h"
|
---|
25 |
|
---|
26 | /* Incremental dump specialities. */
|
---|
27 |
|
---|
28 | /* Which child files to save under a directory. */
|
---|
29 | enum children
|
---|
30 | {
|
---|
31 | NO_CHILDREN,
|
---|
32 | CHANGED_CHILDREN,
|
---|
33 | ALL_CHILDREN
|
---|
34 | };
|
---|
35 |
|
---|
36 | #define DIRF_INIT 0x0001 /* directory structure is initialized
|
---|
37 | (procdir called at least once) */
|
---|
38 | #define DIRF_NFS 0x0002 /* directory is mounted on nfs */
|
---|
39 | #define DIRF_FOUND 0x0004 /* directory is found on fs */
|
---|
40 | #define DIRF_NEW 0x0008 /* directory is new (not found
|
---|
41 | in the previous dump) */
|
---|
42 | #define DIRF_RENAMED 0x0010 /* directory is renamed */
|
---|
43 |
|
---|
44 | #define DIR_IS_INITED(d) ((d)->flags & DIRF_INIT)
|
---|
45 | #define DIR_IS_NFS(d) ((d)->flags & DIRF_NFS)
|
---|
46 | #define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
|
---|
47 | #define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
|
---|
48 | #define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
|
---|
49 |
|
---|
50 | #define DIR_SET_FLAG(d,f) (d)->flags |= (f)
|
---|
51 | #define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
|
---|
52 |
|
---|
53 | /* Directory attributes. */
|
---|
54 | struct directory
|
---|
55 | {
|
---|
56 | struct timespec mtime; /* Modification time */
|
---|
57 | dev_t device_number; /* device number for directory */
|
---|
58 | ino_t inode_number; /* inode number for directory */
|
---|
59 | char *contents; /* Directory contents */
|
---|
60 | char *icontents; /* Initial contents if the directory was
|
---|
61 | rescanned */
|
---|
62 | enum children children; /* What to save under this directory */
|
---|
63 | unsigned flags; /* See DIRF_ macros above */
|
---|
64 | struct directory *orig; /* If the directory was renamed, points to
|
---|
65 | the original directory structure */
|
---|
66 | char name[1]; /* file name of directory */
|
---|
67 | };
|
---|
68 |
|
---|
69 | static Hash_table *directory_table;
|
---|
70 | static Hash_table *directory_meta_table;
|
---|
71 |
|
---|
72 | #if HAVE_ST_FSTYPE_STRING
|
---|
73 | static char const nfs_string[] = "nfs";
|
---|
74 | # define NFS_FILE_STAT(st) (strcmp ((st).st_fstype, nfs_string) == 0)
|
---|
75 | #else
|
---|
76 | # define ST_DEV_MSB(st) (~ (dev_t) 0 << (sizeof (st).st_dev * CHAR_BIT - 1))
|
---|
77 | # define NFS_FILE_STAT(st) (((st).st_dev & ST_DEV_MSB (st)) != 0)
|
---|
78 | #endif
|
---|
79 |
|
---|
80 | /* Calculate the hash of a directory. */
|
---|
81 | static size_t
|
---|
82 | hash_directory_name (void const *entry, size_t n_buckets)
|
---|
83 | {
|
---|
84 | struct directory const *directory = entry;
|
---|
85 | return hash_string (directory->name, n_buckets);
|
---|
86 | }
|
---|
87 |
|
---|
88 | /* Compare two directories for equality of their names. */
|
---|
89 | static bool
|
---|
90 | compare_directory_names (void const *entry1, void const *entry2)
|
---|
91 | {
|
---|
92 | struct directory const *directory1 = entry1;
|
---|
93 | struct directory const *directory2 = entry2;
|
---|
94 | return strcmp (directory1->name, directory2->name) == 0;
|
---|
95 | }
|
---|
96 |
|
---|
97 | static size_t
|
---|
98 | hash_directory_meta (void const *entry, size_t n_buckets)
|
---|
99 | {
|
---|
100 | struct directory const *directory = entry;
|
---|
101 | /* FIXME: Work out a better algorytm */
|
---|
102 | return (directory->device_number + directory->inode_number) % n_buckets;
|
---|
103 | }
|
---|
104 |
|
---|
105 | /* Compare two directories for equality of their device and inode numbers. */
|
---|
106 | static bool
|
---|
107 | compare_directory_meta (void const *entry1, void const *entry2)
|
---|
108 | {
|
---|
109 | struct directory const *directory1 = entry1;
|
---|
110 | struct directory const *directory2 = entry2;
|
---|
111 | return directory1->device_number == directory2->device_number
|
---|
112 | && directory1->inode_number == directory2->inode_number;
|
---|
113 | }
|
---|
114 |
|
---|
115 | /* Make a directory entry for given NAME */
|
---|
116 | static struct directory *
|
---|
117 | make_directory (const char *name)
|
---|
118 | {
|
---|
119 | size_t namelen = strlen (name);
|
---|
120 | size_t size = offsetof (struct directory, name) + namelen + 1;
|
---|
121 | struct directory *directory = xmalloc (size);
|
---|
122 | directory->contents = directory->icontents = NULL;
|
---|
123 | directory->orig = NULL;
|
---|
124 | directory->flags = false;
|
---|
125 | strcpy (directory->name, name);
|
---|
126 | if (ISSLASH (directory->name[namelen-1]))
|
---|
127 | directory->name[namelen-1] = 0;
|
---|
128 | return directory;
|
---|
129 | }
|
---|
130 |
|
---|
131 | /* Create and link a new directory entry for directory NAME, having a
|
---|
132 | device number DEV and an inode number INO, with NFS indicating
|
---|
133 | whether it is an NFS device and FOUND indicating whether we have
|
---|
134 | found that the directory exists. */
|
---|
135 | static struct directory *
|
---|
136 | note_directory (char const *name, struct timespec mtime,
|
---|
137 | dev_t dev, ino_t ino, bool nfs, bool found, char *contents)
|
---|
138 | {
|
---|
139 | struct directory *directory = make_directory (name);
|
---|
140 |
|
---|
141 | directory->mtime = mtime;
|
---|
142 | directory->device_number = dev;
|
---|
143 | directory->inode_number = ino;
|
---|
144 | directory->children = CHANGED_CHILDREN;
|
---|
145 | if (nfs)
|
---|
146 | DIR_SET_FLAG (directory, DIRF_NFS);
|
---|
147 | if (found)
|
---|
148 | DIR_SET_FLAG (directory, DIRF_FOUND);
|
---|
149 | if (contents)
|
---|
150 | {
|
---|
151 | size_t size = dumpdir_size (contents);
|
---|
152 | directory->contents = xmalloc (size);
|
---|
153 | memcpy (directory->contents, contents, size);
|
---|
154 | }
|
---|
155 | else
|
---|
156 | directory->contents = NULL;
|
---|
157 |
|
---|
158 | if (! ((directory_table
|
---|
159 | || (directory_table = hash_initialize (0, 0,
|
---|
160 | hash_directory_name,
|
---|
161 | compare_directory_names, 0)))
|
---|
162 | && hash_insert (directory_table, directory)))
|
---|
163 | xalloc_die ();
|
---|
164 |
|
---|
165 | if (! ((directory_meta_table
|
---|
166 | || (directory_meta_table = hash_initialize (0, 0,
|
---|
167 | hash_directory_meta,
|
---|
168 | compare_directory_meta,
|
---|
169 | 0)))
|
---|
170 | && hash_insert (directory_meta_table, directory)))
|
---|
171 | xalloc_die ();
|
---|
172 |
|
---|
173 | return directory;
|
---|
174 | }
|
---|
175 |
|
---|
176 | /* Return a directory entry for a given file NAME, or zero if none found. */
|
---|
177 | static struct directory *
|
---|
178 | find_directory (const char *name)
|
---|
179 | {
|
---|
180 | if (! directory_table)
|
---|
181 | return 0;
|
---|
182 | else
|
---|
183 | {
|
---|
184 | struct directory *dir = make_directory (name);
|
---|
185 | struct directory *ret = hash_lookup (directory_table, dir);
|
---|
186 | free (dir);
|
---|
187 | return ret;
|
---|
188 | }
|
---|
189 | }
|
---|
190 |
|
---|
191 | /* Return a directory entry for a given combination of device and inode
|
---|
192 | numbers, or zero if none found. */
|
---|
193 | static struct directory *
|
---|
194 | find_directory_meta (dev_t dev, ino_t ino)
|
---|
195 | {
|
---|
196 | if (! directory_meta_table)
|
---|
197 | return 0;
|
---|
198 | else
|
---|
199 | {
|
---|
200 | struct directory *dir = make_directory ("");
|
---|
201 | struct directory *ret;
|
---|
202 | dir->device_number = dev;
|
---|
203 | dir->inode_number = ino;
|
---|
204 | ret = hash_lookup (directory_meta_table, dir);
|
---|
205 | free (dir);
|
---|
206 | return ret;
|
---|
207 | }
|
---|
208 | }
|
---|
209 |
|
---|
210 | void
|
---|
211 | update_parent_directory (const char *name)
|
---|
212 | {
|
---|
213 | struct directory *directory;
|
---|
214 | char *p;
|
---|
215 |
|
---|
216 | p = dir_name (name);
|
---|
217 | directory = find_directory (p);
|
---|
218 | if (directory)
|
---|
219 | {
|
---|
220 | struct stat st;
|
---|
221 | if (deref_stat (dereference_option, p, &st) != 0)
|
---|
222 | stat_diag (name);
|
---|
223 | else
|
---|
224 | directory->mtime = get_stat_mtime (&st);
|
---|
225 | }
|
---|
226 | free (p);
|
---|
227 | }
|
---|
228 |
|
---|
229 | static struct directory *
|
---|
230 | procdir (char *name_buffer, struct stat *stat_data,
|
---|
231 | dev_t device,
|
---|
232 | enum children children,
|
---|
233 | bool verbose)
|
---|
234 | {
|
---|
235 | struct directory *directory;
|
---|
236 | bool nfs = NFS_FILE_STAT (*stat_data);
|
---|
237 |
|
---|
238 | if ((directory = find_directory (name_buffer)) != NULL)
|
---|
239 | {
|
---|
240 | if (DIR_IS_INITED (directory))
|
---|
241 | return directory;
|
---|
242 |
|
---|
243 | /* With NFS, the same file can have two different devices
|
---|
244 | if an NFS directory is mounted in multiple locations,
|
---|
245 | which is relatively common when automounting.
|
---|
246 | To avoid spurious incremental redumping of
|
---|
247 | directories, consider all NFS devices as equal,
|
---|
248 | relying on the i-node to establish differences. */
|
---|
249 |
|
---|
250 | if (! (((DIR_IS_NFS (directory) & nfs)
|
---|
251 | || directory->device_number == stat_data->st_dev)
|
---|
252 | && directory->inode_number == stat_data->st_ino))
|
---|
253 | {
|
---|
254 | /* FIXME: find_directory_meta ignores nfs */
|
---|
255 | struct directory *d = find_directory_meta (stat_data->st_dev,
|
---|
256 | stat_data->st_ino);
|
---|
257 | if (d)
|
---|
258 | {
|
---|
259 | if (verbose_option)
|
---|
260 | WARN ((0, 0, _("%s: Directory has been renamed from %s"),
|
---|
261 | quotearg_colon (name_buffer),
|
---|
262 | quote_n (1, d->name)));
|
---|
263 | directory->orig = d;
|
---|
264 | DIR_SET_FLAG (directory, DIRF_RENAMED);
|
---|
265 | directory->children = CHANGED_CHILDREN;
|
---|
266 | }
|
---|
267 | else
|
---|
268 | {
|
---|
269 | if (verbose_option)
|
---|
270 | WARN ((0, 0, _("%s: Directory has been renamed"),
|
---|
271 | quotearg_colon (name_buffer)));
|
---|
272 | directory->children = ALL_CHILDREN;
|
---|
273 | directory->device_number = stat_data->st_dev;
|
---|
274 | directory->inode_number = stat_data->st_ino;
|
---|
275 | }
|
---|
276 | if (nfs)
|
---|
277 | DIR_SET_FLAG (directory, DIRF_NFS);
|
---|
278 | }
|
---|
279 | else
|
---|
280 | directory->children = CHANGED_CHILDREN;
|
---|
281 |
|
---|
282 | DIR_SET_FLAG (directory, DIRF_FOUND);
|
---|
283 | }
|
---|
284 | else
|
---|
285 | {
|
---|
286 | struct directory *d = find_directory_meta (stat_data->st_dev,
|
---|
287 | stat_data->st_ino);
|
---|
288 |
|
---|
289 | directory = note_directory (name_buffer,
|
---|
290 | get_stat_mtime(stat_data),
|
---|
291 | stat_data->st_dev,
|
---|
292 | stat_data->st_ino,
|
---|
293 | nfs,
|
---|
294 | true,
|
---|
295 | NULL);
|
---|
296 |
|
---|
297 | if (d)
|
---|
298 | {
|
---|
299 | if (verbose)
|
---|
300 | WARN ((0, 0, _("%s: Directory has been renamed from %s"),
|
---|
301 | quotearg_colon (name_buffer),
|
---|
302 | quote_n (1, d->name)));
|
---|
303 | directory->orig = d;
|
---|
304 | DIR_SET_FLAG (directory, DIRF_RENAMED);
|
---|
305 | directory->children = CHANGED_CHILDREN;
|
---|
306 | }
|
---|
307 | else
|
---|
308 | {
|
---|
309 | DIR_SET_FLAG (directory, DIRF_NEW);
|
---|
310 | if (verbose)
|
---|
311 | WARN ((0, 0, _("%s: Directory is new"),
|
---|
312 | quotearg_colon (name_buffer)));
|
---|
313 | directory->children =
|
---|
314 | (listed_incremental_option
|
---|
315 | || (OLDER_STAT_TIME (*stat_data, m)
|
---|
316 | || (after_date_option
|
---|
317 | && OLDER_STAT_TIME (*stat_data, c))))
|
---|
318 | ? ALL_CHILDREN
|
---|
319 | : CHANGED_CHILDREN;
|
---|
320 | }
|
---|
321 | }
|
---|
322 |
|
---|
323 | /* If the directory is on another device and --one-file-system was given,
|
---|
324 | omit it... */
|
---|
325 | if (one_file_system_option && device != stat_data->st_dev
|
---|
326 | /* ... except if it was explicitely given in the command line */
|
---|
327 | && !is_individual_file (name_buffer))
|
---|
328 | directory->children = NO_CHILDREN;
|
---|
329 | else if (children == ALL_CHILDREN)
|
---|
330 | directory->children = ALL_CHILDREN;
|
---|
331 |
|
---|
332 | DIR_SET_FLAG (directory, DIRF_INIT);
|
---|
333 |
|
---|
334 | return directory;
|
---|
335 | }
|
---|
336 |
|
---|
337 | /* Locate NAME in the dumpdir array DUMP.
|
---|
338 | Return pointer to the slot in the array, or NULL if not found */
|
---|
339 | const char *
|
---|
340 | dumpdir_locate (const char *dump, const char *name)
|
---|
341 | {
|
---|
342 | if (dump)
|
---|
343 | while (*dump)
|
---|
344 | {
|
---|
345 | /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
|
---|
346 | alphabetical ordering.
|
---|
347 | They normally do not occur in dumpdirs from the snapshot files,
|
---|
348 | but this function is also used by purge_directory, which operates
|
---|
349 | on a dumpdir from the archive, hence the need for this test. */
|
---|
350 | if (!strchr ("RX", *dump))
|
---|
351 | {
|
---|
352 | int rc = strcmp (dump + 1, name);
|
---|
353 | if (rc == 0)
|
---|
354 | return dump;
|
---|
355 | if (rc > 1)
|
---|
356 | break;
|
---|
357 | }
|
---|
358 | dump += strlen (dump) + 1;
|
---|
359 | }
|
---|
360 | return NULL;
|
---|
361 | }
|
---|
362 |
|
---|
363 | /* Return size in bytes of the dumpdir array P */
|
---|
364 | size_t
|
---|
365 | dumpdir_size (const char *p)
|
---|
366 | {
|
---|
367 | size_t totsize = 0;
|
---|
368 |
|
---|
369 | while (*p)
|
---|
370 | {
|
---|
371 | size_t size = strlen (p) + 1;
|
---|
372 | totsize += size;
|
---|
373 | p += size;
|
---|
374 | }
|
---|
375 | return totsize + 1;
|
---|
376 | }
|
---|
377 |
|
---|
378 | static int
|
---|
379 | compare_dirnames (const void *first, const void *second)
|
---|
380 | {
|
---|
381 | return strcmp (*(const char**)first, *(const char**)second);
|
---|
382 | }
|
---|
383 |
|
---|
384 | /* Compare dumpdir array from DIRECTORY with directory listing DIR and
|
---|
385 | build a new dumpdir template.
|
---|
386 |
|
---|
387 | DIR must be returned by a previous call to savedir().
|
---|
388 |
|
---|
389 | File names in DIRECTORY->contents must be sorted
|
---|
390 | alphabetically.
|
---|
391 |
|
---|
392 | DIRECTORY->contents is replaced with the created template. Each entry is
|
---|
393 | prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
|
---|
394 |
|
---|
395 | void
|
---|
396 | makedumpdir (struct directory *directory, const char *dir)
|
---|
397 | {
|
---|
398 | size_t i,
|
---|
399 | dirsize, /* Number of elements in DIR */
|
---|
400 | len; /* Length of DIR, including terminating nul */
|
---|
401 | const char *p;
|
---|
402 | char const **array;
|
---|
403 | char *new_dump, *new_dump_ptr;
|
---|
404 | const char *dump;
|
---|
405 |
|
---|
406 | if (directory->children == ALL_CHILDREN)
|
---|
407 | dump = NULL;
|
---|
408 | else if (DIR_IS_RENAMED (directory))
|
---|
409 | dump = directory->orig->icontents ?
|
---|
410 | directory->orig->icontents : directory->orig->contents;
|
---|
411 | else
|
---|
412 | dump = directory->contents;
|
---|
413 |
|
---|
414 | /* Count the size of DIR and the number of elements it contains */
|
---|
415 | dirsize = 0;
|
---|
416 | len = 0;
|
---|
417 | for (p = dir; *p; p += strlen (p) + 1, dirsize++)
|
---|
418 | len += strlen (p) + 2;
|
---|
419 | len++;
|
---|
420 |
|
---|
421 | /* Create a sorted directory listing */
|
---|
422 | array = xcalloc (dirsize, sizeof array[0]);
|
---|
423 | for (i = 0, p = dir; *p; p += strlen (p) + 1, i++)
|
---|
424 | array[i] = p;
|
---|
425 |
|
---|
426 | qsort (array, dirsize, sizeof (array[0]), compare_dirnames);
|
---|
427 |
|
---|
428 | /* Prepare space for new dumpdir */
|
---|
429 | new_dump = xmalloc (len);
|
---|
430 | new_dump_ptr = new_dump;
|
---|
431 |
|
---|
432 | /* Fill in the dumpdir template */
|
---|
433 | for (i = 0; i < dirsize; i++)
|
---|
434 | {
|
---|
435 | const char *loc = dumpdir_locate (dump, array[i]);
|
---|
436 | if (loc)
|
---|
437 | {
|
---|
438 | *new_dump_ptr++ = ' ';
|
---|
439 | dump = loc + strlen (loc) + 1;
|
---|
440 | }
|
---|
441 | else
|
---|
442 | *new_dump_ptr++ = 'Y'; /* New entry */
|
---|
443 |
|
---|
444 | /* Copy the file name */
|
---|
445 | for (p = array[i]; (*new_dump_ptr++ = *p++); )
|
---|
446 | ;
|
---|
447 | }
|
---|
448 | *new_dump_ptr = 0;
|
---|
449 | directory->icontents = directory->contents;
|
---|
450 | directory->contents = new_dump;
|
---|
451 | free (array);
|
---|
452 | }
|
---|
453 |
|
---|
454 | /* Recursively scan the given directory. */
|
---|
455 | static char *
|
---|
456 | scan_directory (char *dir_name, dev_t device)
|
---|
457 | {
|
---|
458 | char *dirp = savedir (dir_name); /* for scanning directory */
|
---|
459 | char *name_buffer; /* directory, `/', and directory member */
|
---|
460 | size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
|
---|
461 | size_t name_length; /* used length in name_buffer */
|
---|
462 | struct stat stat_data;
|
---|
463 | struct directory *directory;
|
---|
464 |
|
---|
465 | if (! dirp)
|
---|
466 | savedir_error (dir_name);
|
---|
467 |
|
---|
468 | name_buffer_size = strlen (dir_name) + NAME_FIELD_SIZE;
|
---|
469 | name_buffer = xmalloc (name_buffer_size + 2);
|
---|
470 | strcpy (name_buffer, dir_name);
|
---|
471 | if (! ISSLASH (dir_name[strlen (dir_name) - 1]))
|
---|
472 | strcat (name_buffer, "/");
|
---|
473 | name_length = strlen (name_buffer);
|
---|
474 |
|
---|
475 | if (deref_stat (dereference_option, name_buffer, &stat_data))
|
---|
476 | {
|
---|
477 | stat_diag (name_buffer);
|
---|
478 | /* FIXME: used to be
|
---|
479 | children = CHANGED_CHILDREN;
|
---|
480 | but changed to: */
|
---|
481 | free (name_buffer);
|
---|
482 | free (dirp);
|
---|
483 | return NULL;
|
---|
484 | }
|
---|
485 |
|
---|
486 | directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
|
---|
487 |
|
---|
488 | if (dirp && directory->children != NO_CHILDREN)
|
---|
489 | {
|
---|
490 | char *entry; /* directory entry being scanned */
|
---|
491 | size_t entrylen; /* length of directory entry */
|
---|
492 |
|
---|
493 | makedumpdir (directory, dirp);
|
---|
494 |
|
---|
495 | for (entry = directory->contents;
|
---|
496 | (entrylen = strlen (entry)) != 0;
|
---|
497 | entry += entrylen + 1)
|
---|
498 | {
|
---|
499 | if (name_buffer_size <= entrylen - 1 + name_length)
|
---|
500 | {
|
---|
501 | do
|
---|
502 | name_buffer_size += NAME_FIELD_SIZE;
|
---|
503 | while (name_buffer_size <= entrylen - 1 + name_length);
|
---|
504 | name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
|
---|
505 | }
|
---|
506 | strcpy (name_buffer + name_length, entry + 1);
|
---|
507 |
|
---|
508 | if (excluded_name (name_buffer))
|
---|
509 | *entry = 'N';
|
---|
510 | else
|
---|
511 | {
|
---|
512 | if (deref_stat (dereference_option, name_buffer, &stat_data))
|
---|
513 | {
|
---|
514 | stat_diag (name_buffer);
|
---|
515 | *entry = 'N';
|
---|
516 | continue;
|
---|
517 | }
|
---|
518 |
|
---|
519 | if (S_ISDIR (stat_data.st_mode))
|
---|
520 | {
|
---|
521 | procdir (name_buffer, &stat_data, device,
|
---|
522 | directory->children,
|
---|
523 | verbose_option);
|
---|
524 | *entry = 'D';
|
---|
525 | }
|
---|
526 |
|
---|
527 | else if (one_file_system_option && device != stat_data.st_dev)
|
---|
528 | *entry = 'N';
|
---|
529 |
|
---|
530 | else if (*entry == 'Y')
|
---|
531 | /* New entry, skip further checks */;
|
---|
532 |
|
---|
533 | /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
|
---|
534 |
|
---|
535 | else if (OLDER_STAT_TIME (stat_data, m)
|
---|
536 | && (!after_date_option
|
---|
537 | || OLDER_STAT_TIME (stat_data, c)))
|
---|
538 | *entry = 'N';
|
---|
539 | else
|
---|
540 | *entry = 'Y';
|
---|
541 | }
|
---|
542 | }
|
---|
543 | }
|
---|
544 |
|
---|
545 | free (name_buffer);
|
---|
546 | if (dirp)
|
---|
547 | free (dirp);
|
---|
548 |
|
---|
549 | return directory->contents;
|
---|
550 | }
|
---|
551 |
|
---|
552 | char *
|
---|
553 | get_directory_contents (char *dir_name, dev_t device)
|
---|
554 | {
|
---|
555 | return scan_directory (dir_name, device);
|
---|
556 | }
|
---|
557 |
|
---|
558 | |
---|
559 |
|
---|
560 | static void
|
---|
561 | obstack_code_rename (struct obstack *stk, char *from, char *to)
|
---|
562 | {
|
---|
563 | obstack_1grow (stk, 'R');
|
---|
564 | obstack_grow (stk, from, strlen (from) + 1);
|
---|
565 | obstack_1grow (stk, 'T');
|
---|
566 | obstack_grow (stk, to, strlen (to) + 1);
|
---|
567 | }
|
---|
568 |
|
---|
569 | static bool
|
---|
570 | rename_handler (void *data, void *proc_data)
|
---|
571 | {
|
---|
572 | struct directory *dir = data;
|
---|
573 | struct obstack *stk = proc_data;
|
---|
574 |
|
---|
575 | if (DIR_IS_RENAMED (dir))
|
---|
576 | {
|
---|
577 | struct directory *prev, *p;
|
---|
578 |
|
---|
579 | /* Detect eventual cycles and clear DIRF_RENAMED flag, so this entries
|
---|
580 | are ignored when hit by this function next time.
|
---|
581 | If the chain forms a cycle, prev points to the entry DIR is renamed
|
---|
582 | from. In this case it still retains DIRF_RENAMED flag, which will be
|
---|
583 | cleared in the `else' branch below */
|
---|
584 | for (prev = dir; prev && prev->orig != dir; prev = prev->orig)
|
---|
585 | DIR_CLEAR_FLAG (prev, DIRF_RENAMED);
|
---|
586 |
|
---|
587 | if (prev == NULL)
|
---|
588 | {
|
---|
589 | for (p = dir; p && p->orig; p = p->orig)
|
---|
590 | obstack_code_rename (stk, p->orig->name, p->name);
|
---|
591 | }
|
---|
592 | else
|
---|
593 | {
|
---|
594 | char *temp_name;
|
---|
595 |
|
---|
596 | DIR_CLEAR_FLAG (prev, DIRF_RENAMED);
|
---|
597 |
|
---|
598 | /* Break the cycle by using a temporary name for one of its
|
---|
599 | elements.
|
---|
600 | First, create a temp name stub entry. */
|
---|
601 | temp_name = dir_name (dir->name);
|
---|
602 | obstack_1grow (stk, 'X');
|
---|
603 | obstack_grow (stk, temp_name, strlen (temp_name) + 1);
|
---|
604 |
|
---|
605 | obstack_code_rename (stk, dir->name, "");
|
---|
606 |
|
---|
607 | for (p = dir; p != prev; p = p->orig)
|
---|
608 | obstack_code_rename (stk, p->orig->name, p->name);
|
---|
609 |
|
---|
610 | obstack_code_rename (stk, "", prev->name);
|
---|
611 | }
|
---|
612 | }
|
---|
613 | return true;
|
---|
614 | }
|
---|
615 |
|
---|
616 | const char *
|
---|
617 | append_incremental_renames (const char *dump)
|
---|
618 | {
|
---|
619 | struct obstack stk;
|
---|
620 | size_t size;
|
---|
621 |
|
---|
622 | if (directory_table == NULL)
|
---|
623 | return dump;
|
---|
624 |
|
---|
625 | obstack_init (&stk);
|
---|
626 | if (dump)
|
---|
627 | {
|
---|
628 | size = dumpdir_size (dump) - 1;
|
---|
629 | obstack_grow (&stk, dump, size);
|
---|
630 | }
|
---|
631 | else
|
---|
632 | size = 0;
|
---|
633 |
|
---|
634 | hash_do_for_each (directory_table, rename_handler, &stk);
|
---|
635 | if (obstack_object_size (&stk) != size)
|
---|
636 | {
|
---|
637 | obstack_1grow (&stk, 0);
|
---|
638 | dump = obstack_finish (&stk);
|
---|
639 | }
|
---|
640 | else
|
---|
641 | obstack_free (&stk, NULL);
|
---|
642 | return dump;
|
---|
643 | }
|
---|
644 |
|
---|
645 | |
---|
646 |
|
---|
647 |
|
---|
648 | static FILE *listed_incremental_stream;
|
---|
649 |
|
---|
650 | /* Version of incremental format snapshots (directory files) used by this
|
---|
651 | tar. Currently it is supposed to be a single decimal number. 0 means
|
---|
652 | incremental snapshots as per tar version before 1.15.2.
|
---|
653 |
|
---|
654 | The current tar version supports incremental versions from
|
---|
655 | 0 up to TAR_INCREMENTAL_VERSION, inclusive.
|
---|
656 | It is able to create only snapshots of TAR_INCREMENTAL_VERSION */
|
---|
657 |
|
---|
658 | #define TAR_INCREMENTAL_VERSION 2
|
---|
659 |
|
---|
660 | /* Read incremental snapshot formats 0 and 1 */
|
---|
661 | static void
|
---|
662 | read_incr_db_01 (int version, const char *initbuf)
|
---|
663 | {
|
---|
664 | int n;
|
---|
665 | uintmax_t u;
|
---|
666 | time_t sec;
|
---|
667 | long int nsec;
|
---|
668 | char *buf = 0;
|
---|
669 | size_t bufsize;
|
---|
670 | char *ebuf;
|
---|
671 | long lineno = 1;
|
---|
672 |
|
---|
673 | if (version == 1)
|
---|
674 | {
|
---|
675 | if (getline (&buf, &bufsize, listed_incremental_stream) <= 0)
|
---|
676 | {
|
---|
677 | read_error (listed_incremental_option);
|
---|
678 | free (buf);
|
---|
679 | return;
|
---|
680 | }
|
---|
681 | ++lineno;
|
---|
682 | }
|
---|
683 | else
|
---|
684 | {
|
---|
685 | buf = strdup (initbuf);
|
---|
686 | bufsize = strlen (buf) + 1;
|
---|
687 | }
|
---|
688 |
|
---|
689 | sec = TYPE_MINIMUM (time_t);
|
---|
690 | nsec = -1;
|
---|
691 | errno = 0;
|
---|
692 | u = strtoumax (buf, &ebuf, 10);
|
---|
693 | if (!errno && TYPE_MAXIMUM (time_t) < u)
|
---|
694 | errno = ERANGE;
|
---|
695 | if (errno || buf == ebuf)
|
---|
696 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
697 | quotearg_colon (listed_incremental_option),
|
---|
698 | lineno,
|
---|
699 | _("Invalid time stamp")));
|
---|
700 | else
|
---|
701 | {
|
---|
702 | sec = u;
|
---|
703 |
|
---|
704 | if (version == 1 && *ebuf)
|
---|
705 | {
|
---|
706 | char const *buf_ns = ebuf + 1;
|
---|
707 | errno = 0;
|
---|
708 | u = strtoumax (buf_ns, &ebuf, 10);
|
---|
709 | if (!errno && BILLION <= u)
|
---|
710 | errno = ERANGE;
|
---|
711 | if (errno || buf_ns == ebuf)
|
---|
712 | {
|
---|
713 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
714 | quotearg_colon (listed_incremental_option),
|
---|
715 | lineno,
|
---|
716 | _("Invalid time stamp")));
|
---|
717 | sec = TYPE_MINIMUM (time_t);
|
---|
718 | }
|
---|
719 | else
|
---|
720 | nsec = u;
|
---|
721 | }
|
---|
722 | else
|
---|
723 | {
|
---|
724 | /* pre-1 incremental format does not contain nanoseconds */
|
---|
725 | nsec = 0;
|
---|
726 | }
|
---|
727 | }
|
---|
728 | newer_mtime_option.tv_sec = sec;
|
---|
729 | newer_mtime_option.tv_nsec = nsec;
|
---|
730 |
|
---|
731 |
|
---|
732 | while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
|
---|
733 | {
|
---|
734 | dev_t dev;
|
---|
735 | ino_t ino;
|
---|
736 | bool nfs = buf[0] == '+';
|
---|
737 | char *strp = buf + nfs;
|
---|
738 | struct timespec mtime;
|
---|
739 |
|
---|
740 | lineno++;
|
---|
741 |
|
---|
742 | if (buf[n - 1] == '\n')
|
---|
743 | buf[n - 1] = '\0';
|
---|
744 |
|
---|
745 | if (version == 1)
|
---|
746 | {
|
---|
747 | errno = 0;
|
---|
748 | u = strtoumax (strp, &ebuf, 10);
|
---|
749 | if (!errno && TYPE_MAXIMUM (time_t) < u)
|
---|
750 | errno = ERANGE;
|
---|
751 | if (errno || strp == ebuf || *ebuf != ' ')
|
---|
752 | {
|
---|
753 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
754 | quotearg_colon (listed_incremental_option), lineno,
|
---|
755 | _("Invalid modification time (seconds)")));
|
---|
756 | sec = (time_t) -1;
|
---|
757 | }
|
---|
758 | else
|
---|
759 | sec = u;
|
---|
760 | strp = ebuf;
|
---|
761 |
|
---|
762 | errno = 0;
|
---|
763 | u = strtoumax (strp, &ebuf, 10);
|
---|
764 | if (!errno && BILLION <= u)
|
---|
765 | errno = ERANGE;
|
---|
766 | if (errno || strp == ebuf || *ebuf != ' ')
|
---|
767 | {
|
---|
768 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
769 | quotearg_colon (listed_incremental_option), lineno,
|
---|
770 | _("Invalid modification time (nanoseconds)")));
|
---|
771 | nsec = -1;
|
---|
772 | }
|
---|
773 | else
|
---|
774 | nsec = u;
|
---|
775 | mtime.tv_sec = sec;
|
---|
776 | mtime.tv_nsec = nsec;
|
---|
777 | strp = ebuf;
|
---|
778 | }
|
---|
779 | else
|
---|
780 | memset (&mtime, 0, sizeof mtime);
|
---|
781 |
|
---|
782 | errno = 0;
|
---|
783 | u = strtoumax (strp, &ebuf, 10);
|
---|
784 | if (!errno && TYPE_MAXIMUM (dev_t) < u)
|
---|
785 | errno = ERANGE;
|
---|
786 | if (errno || strp == ebuf || *ebuf != ' ')
|
---|
787 | {
|
---|
788 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
789 | quotearg_colon (listed_incremental_option), lineno,
|
---|
790 | _("Invalid device number")));
|
---|
791 | dev = (dev_t) -1;
|
---|
792 | }
|
---|
793 | else
|
---|
794 | dev = u;
|
---|
795 | strp = ebuf;
|
---|
796 |
|
---|
797 | errno = 0;
|
---|
798 | u = strtoumax (strp, &ebuf, 10);
|
---|
799 | if (!errno && TYPE_MAXIMUM (ino_t) < u)
|
---|
800 | errno = ERANGE;
|
---|
801 | if (errno || strp == ebuf || *ebuf != ' ')
|
---|
802 | {
|
---|
803 | ERROR ((0, errno, "%s:%ld: %s",
|
---|
804 | quotearg_colon (listed_incremental_option), lineno,
|
---|
805 | _("Invalid inode number")));
|
---|
806 | ino = (ino_t) -1;
|
---|
807 | }
|
---|
808 | else
|
---|
809 | ino = u;
|
---|
810 | strp = ebuf;
|
---|
811 |
|
---|
812 | strp++;
|
---|
813 | unquote_string (strp);
|
---|
814 | note_directory (strp, mtime, dev, ino, nfs, false, NULL);
|
---|
815 | }
|
---|
816 | free (buf);
|
---|
817 | }
|
---|
818 |
|
---|
819 | /* Read a nul-terminated string from FP and store it in STK.
|
---|
820 | Store the number of bytes read (including nul terminator) in PCOUNT.
|
---|
821 |
|
---|
822 | Return the last character read or EOF on end of file. */
|
---|
823 | static int
|
---|
824 | read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
|
---|
825 | {
|
---|
826 | int c;
|
---|
827 | size_t i;
|
---|
828 |
|
---|
829 | for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
|
---|
830 | obstack_1grow (stk, c);
|
---|
831 | obstack_1grow (stk, 0);
|
---|
832 |
|
---|
833 | *pcount = i;
|
---|
834 | return c;
|
---|
835 | }
|
---|
836 |
|
---|
837 | /* Read from file FP a nul-terminated string and convert it to
|
---|
838 | intmax_t. Return the resulting value in PVAL. Assume '-' has
|
---|
839 | already been read.
|
---|
840 |
|
---|
841 | Throw a fatal error if the string cannot be converted or if the
|
---|
842 | converted value is less than MIN_VAL. */
|
---|
843 |
|
---|
844 | static void
|
---|
845 | read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
|
---|
846 | {
|
---|
847 | int c;
|
---|
848 | size_t i;
|
---|
849 | char buf[INT_BUFSIZE_BOUND (intmax_t)];
|
---|
850 | char *ep;
|
---|
851 | buf[0] = '-';
|
---|
852 |
|
---|
853 | for (i = 1; ISDIGIT (c = getc (fp)); i++)
|
---|
854 | {
|
---|
855 | if (i == sizeof buf - 1)
|
---|
856 | FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
|
---|
857 | buf[i] = c;
|
---|
858 | }
|
---|
859 |
|
---|
860 | if (c < 0)
|
---|
861 | {
|
---|
862 | if (ferror (fp))
|
---|
863 | FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
|
---|
864 | else
|
---|
865 | FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
|
---|
866 | }
|
---|
867 |
|
---|
868 | buf[i] = 0;
|
---|
869 | errno = 0;
|
---|
870 | *pval = strtoimax (buf, &ep, 10);
|
---|
871 | if (c || errno || *pval < min_val)
|
---|
872 | FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
|
---|
873 | }
|
---|
874 |
|
---|
875 | /* Read from file FP a nul-terminated string and convert it to
|
---|
876 | uintmax_t. Return the resulting value in PVAL. Assume C has
|
---|
877 | already been read.
|
---|
878 |
|
---|
879 | Throw a fatal error if the string cannot be converted or if the
|
---|
880 | converted value exceeds MAX_VAL.
|
---|
881 |
|
---|
882 | Return the last character read or EOF on end of file. */
|
---|
883 |
|
---|
884 | static int
|
---|
885 | read_unsigned_num (int c, FILE *fp, uintmax_t max_val, uintmax_t *pval)
|
---|
886 | {
|
---|
887 | size_t i;
|
---|
888 | char buf[UINTMAX_STRSIZE_BOUND], *ep;
|
---|
889 |
|
---|
890 | for (i = 0; ISDIGIT (c); i++)
|
---|
891 | {
|
---|
892 | if (i == sizeof buf - 1)
|
---|
893 | FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
|
---|
894 | buf[i] = c;
|
---|
895 | c = getc (fp);
|
---|
896 | }
|
---|
897 |
|
---|
898 | if (c < 0)
|
---|
899 | {
|
---|
900 | if (ferror (fp))
|
---|
901 | FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
|
---|
902 | else if (i == 0)
|
---|
903 | return c;
|
---|
904 | else
|
---|
905 | FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
|
---|
906 | }
|
---|
907 |
|
---|
908 | buf[i] = 0;
|
---|
909 | errno = 0;
|
---|
910 | *pval = strtoumax (buf, &ep, 10);
|
---|
911 | if (c || errno || max_val < *pval)
|
---|
912 | FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
|
---|
913 | return c;
|
---|
914 | }
|
---|
915 |
|
---|
916 | /* Read from file FP a nul-terminated string and convert it to
|
---|
917 | uintmax_t. Return the resulting value in PVAL.
|
---|
918 |
|
---|
919 | Throw a fatal error if the string cannot be converted or if the
|
---|
920 | converted value exceeds MAX_VAL.
|
---|
921 |
|
---|
922 | Return the last character read or EOF on end of file. */
|
---|
923 |
|
---|
924 | static int
|
---|
925 | read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
|
---|
926 | {
|
---|
927 | return read_unsigned_num (getc (fp), fp, max_val, pval);
|
---|
928 | }
|
---|
929 |
|
---|
930 | /* Read from FP two NUL-terminated strings representing a struct
|
---|
931 | timespec. Return the resulting value in PVAL.
|
---|
932 |
|
---|
933 | Throw a fatal error if the string cannot be converted. */
|
---|
934 |
|
---|
935 | static void
|
---|
936 | read_timespec (FILE *fp, struct timespec *pval)
|
---|
937 | {
|
---|
938 | int c = getc (fp);
|
---|
939 | intmax_t i;
|
---|
940 | uintmax_t u;
|
---|
941 |
|
---|
942 | if (c == '-')
|
---|
943 | {
|
---|
944 | read_negative_num (fp, TYPE_MINIMUM (time_t), &i);
|
---|
945 | c = 0;
|
---|
946 | pval->tv_sec = i;
|
---|
947 | }
|
---|
948 | else
|
---|
949 | {
|
---|
950 | c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u);
|
---|
951 | pval->tv_sec = u;
|
---|
952 | }
|
---|
953 |
|
---|
954 | if (c || read_num (fp, BILLION - 1, &u))
|
---|
955 | FATAL_ERROR ((0, 0, "%s: %s",
|
---|
956 | quotearg_colon (listed_incremental_option),
|
---|
957 | _("Unexpected EOF in snapshot file")));
|
---|
958 | pval->tv_nsec = u;
|
---|
959 | }
|
---|
960 |
|
---|
961 | /* Read incremental snapshot format 2 */
|
---|
962 | static void
|
---|
963 | read_incr_db_2 ()
|
---|
964 | {
|
---|
965 | uintmax_t u;
|
---|
966 | struct obstack stk;
|
---|
967 |
|
---|
968 | obstack_init (&stk);
|
---|
969 |
|
---|
970 | read_timespec (listed_incremental_stream, &newer_mtime_option);
|
---|
971 |
|
---|
972 | for (;;)
|
---|
973 | {
|
---|
974 | struct timespec mtime;
|
---|
975 | dev_t dev;
|
---|
976 | ino_t ino;
|
---|
977 | bool nfs;
|
---|
978 | char *name;
|
---|
979 | char *content;
|
---|
980 | size_t s;
|
---|
981 |
|
---|
982 | if (read_num (listed_incremental_stream, 1, &u))
|
---|
983 | return; /* Normal return */
|
---|
984 |
|
---|
985 | nfs = u;
|
---|
986 |
|
---|
987 | read_timespec (listed_incremental_stream, &mtime);
|
---|
988 |
|
---|
989 | if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u))
|
---|
990 | break;
|
---|
991 | dev = u;
|
---|
992 |
|
---|
993 | if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u))
|
---|
994 | break;
|
---|
995 | ino = u;
|
---|
996 |
|
---|
997 | if (read_obstack (listed_incremental_stream, &stk, &s))
|
---|
998 | break;
|
---|
999 |
|
---|
1000 | name = obstack_finish (&stk);
|
---|
1001 |
|
---|
1002 | while (read_obstack (listed_incremental_stream, &stk, &s) == 0 && s > 1)
|
---|
1003 | ;
|
---|
1004 | if (getc (listed_incremental_stream) != 0)
|
---|
1005 | FATAL_ERROR ((0, 0, "%s: %s",
|
---|
1006 | quotearg_colon (listed_incremental_option),
|
---|
1007 | _("Missing record terminator")));
|
---|
1008 |
|
---|
1009 | content = obstack_finish (&stk);
|
---|
1010 | note_directory (name, mtime, dev, ino, nfs, false, content);
|
---|
1011 | obstack_free (&stk, content);
|
---|
1012 | }
|
---|
1013 | FATAL_ERROR ((0, 0, "%s: %s",
|
---|
1014 | quotearg_colon (listed_incremental_option),
|
---|
1015 | _("Unexpected EOF in snapshot file")));
|
---|
1016 | }
|
---|
1017 |
|
---|
1018 | /* Read incremental snapshot file (directory file).
|
---|
1019 | If the file has older incremental version, make sure that it is processed
|
---|
1020 | correctly and that tar will use the most conservative backup method among
|
---|
1021 | possible alternatives (i.e. prefer ALL_CHILDREN over CHANGED_CHILDREN,
|
---|
1022 | etc.) This ensures that the snapshots are updated to the recent version
|
---|
1023 | without any loss of data. */
|
---|
1024 | void
|
---|
1025 | read_directory_file (void)
|
---|
1026 | {
|
---|
1027 | int fd;
|
---|
1028 | char *buf = 0;
|
---|
1029 | size_t bufsize;
|
---|
1030 |
|
---|
1031 | /* Open the file for both read and write. That way, we can write
|
---|
1032 | it later without having to reopen it, and don't have to worry if
|
---|
1033 | we chdir in the meantime. */
|
---|
1034 | fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
|
---|
1035 | if (fd < 0)
|
---|
1036 | {
|
---|
1037 | open_error (listed_incremental_option);
|
---|
1038 | return;
|
---|
1039 | }
|
---|
1040 |
|
---|
1041 | listed_incremental_stream = fdopen (fd, "r+");
|
---|
1042 | if (! listed_incremental_stream)
|
---|
1043 | {
|
---|
1044 | open_error (listed_incremental_option);
|
---|
1045 | close (fd);
|
---|
1046 | return;
|
---|
1047 | }
|
---|
1048 |
|
---|
1049 | if (0 < getline (&buf, &bufsize, listed_incremental_stream))
|
---|
1050 | {
|
---|
1051 | char *ebuf;
|
---|
1052 | uintmax_t incremental_version;
|
---|
1053 |
|
---|
1054 | if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
|
---|
1055 | {
|
---|
1056 | ebuf = buf + sizeof PACKAGE_NAME - 1;
|
---|
1057 | if (*ebuf++ != '-')
|
---|
1058 | ERROR((1, 0, _("Bad incremental file format")));
|
---|
1059 | for (; *ebuf != '-'; ebuf++)
|
---|
1060 | if (!*ebuf)
|
---|
1061 | ERROR((1, 0, _("Bad incremental file format")));
|
---|
1062 |
|
---|
1063 | incremental_version = strtoumax (ebuf + 1, NULL, 10);
|
---|
1064 | }
|
---|
1065 | else
|
---|
1066 | incremental_version = 0;
|
---|
1067 |
|
---|
1068 | switch (incremental_version)
|
---|
1069 | {
|
---|
1070 | case 0:
|
---|
1071 | case 1:
|
---|
1072 | read_incr_db_01 (incremental_version, buf);
|
---|
1073 | break;
|
---|
1074 |
|
---|
1075 | case TAR_INCREMENTAL_VERSION:
|
---|
1076 | read_incr_db_2 ();
|
---|
1077 | break;
|
---|
1078 |
|
---|
1079 | default:
|
---|
1080 | ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
|
---|
1081 | incremental_version));
|
---|
1082 | }
|
---|
1083 |
|
---|
1084 | }
|
---|
1085 |
|
---|
1086 | if (ferror (listed_incremental_stream))
|
---|
1087 | read_error (listed_incremental_option);
|
---|
1088 | if (buf)
|
---|
1089 | free (buf);
|
---|
1090 | }
|
---|
1091 |
|
---|
1092 | /* Output incremental data for the directory ENTRY to the file DATA.
|
---|
1093 | Return nonzero if successful, preserving errno on write failure. */
|
---|
1094 | static bool
|
---|
1095 | write_directory_file_entry (void *entry, void *data)
|
---|
1096 | {
|
---|
1097 | struct directory const *directory = entry;
|
---|
1098 | FILE *fp = data;
|
---|
1099 |
|
---|
1100 | if (DIR_IS_FOUND (directory))
|
---|
1101 | {
|
---|
1102 | char buf[UINTMAX_STRSIZE_BOUND];
|
---|
1103 | char *s;
|
---|
1104 |
|
---|
1105 | s = DIR_IS_NFS (directory) ? "1" : "0";
|
---|
1106 | fwrite (s, 2, 1, fp);
|
---|
1107 | s = (TYPE_SIGNED (time_t)
|
---|
1108 | ? imaxtostr (directory->mtime.tv_sec, buf)
|
---|
1109 | : umaxtostr (directory->mtime.tv_sec, buf));
|
---|
1110 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1111 | s = umaxtostr (directory->mtime.tv_nsec, buf);
|
---|
1112 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1113 | s = umaxtostr (directory->device_number, buf);
|
---|
1114 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1115 | s = umaxtostr (directory->inode_number, buf);
|
---|
1116 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1117 |
|
---|
1118 | fwrite (directory->name, strlen (directory->name) + 1, 1, fp);
|
---|
1119 | if (directory->contents)
|
---|
1120 | {
|
---|
1121 | char *p;
|
---|
1122 | for (p = directory->contents; *p; p += strlen (p) + 1)
|
---|
1123 | {
|
---|
1124 | if (strchr ("YND", *p))
|
---|
1125 | fwrite (p, strlen (p) + 1, 1, fp);
|
---|
1126 | }
|
---|
1127 | }
|
---|
1128 | fwrite ("\0\0", 2, 1, fp);
|
---|
1129 | }
|
---|
1130 |
|
---|
1131 | return ! ferror (fp);
|
---|
1132 | }
|
---|
1133 |
|
---|
1134 | void
|
---|
1135 | write_directory_file (void)
|
---|
1136 | {
|
---|
1137 | FILE *fp = listed_incremental_stream;
|
---|
1138 | char buf[UINTMAX_STRSIZE_BOUND];
|
---|
1139 | char *s;
|
---|
1140 |
|
---|
1141 | if (! fp)
|
---|
1142 | return;
|
---|
1143 |
|
---|
1144 | if (fseek (fp, 0L, SEEK_SET) != 0)
|
---|
1145 | seek_error (listed_incremental_option);
|
---|
1146 | if (sys_truncate (fileno (fp)) != 0)
|
---|
1147 | truncate_error (listed_incremental_option);
|
---|
1148 |
|
---|
1149 | fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
|
---|
1150 | TAR_INCREMENTAL_VERSION);
|
---|
1151 |
|
---|
1152 | s = (TYPE_SIGNED (time_t)
|
---|
1153 | ? imaxtostr (start_time.tv_sec, buf)
|
---|
1154 | : umaxtostr (start_time.tv_sec, buf));
|
---|
1155 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1156 | s = umaxtostr (start_time.tv_nsec, buf);
|
---|
1157 | fwrite (s, strlen (s) + 1, 1, fp);
|
---|
1158 |
|
---|
1159 | if (! ferror (fp) && directory_table)
|
---|
1160 | hash_do_for_each (directory_table, write_directory_file_entry, fp);
|
---|
1161 |
|
---|
1162 | if (ferror (fp))
|
---|
1163 | write_error (listed_incremental_option);
|
---|
1164 | if (fclose (fp) != 0)
|
---|
1165 | close_error (listed_incremental_option);
|
---|
1166 | }
|
---|
1167 |
|
---|
1168 | |
---|
1169 |
|
---|
1170 | /* Restoration of incremental dumps. */
|
---|
1171 |
|
---|
1172 | static void
|
---|
1173 | get_gnu_dumpdir (struct tar_stat_info *stat_info)
|
---|
1174 | {
|
---|
1175 | size_t size;
|
---|
1176 | size_t copied;
|
---|
1177 | union block *data_block;
|
---|
1178 | char *to;
|
---|
1179 | char *archive_dir;
|
---|
1180 |
|
---|
1181 | size = stat_info->stat.st_size;
|
---|
1182 |
|
---|
1183 | archive_dir = xmalloc (size);
|
---|
1184 | to = archive_dir;
|
---|
1185 |
|
---|
1186 | set_next_block_after (current_header);
|
---|
1187 | mv_begin (stat_info);
|
---|
1188 |
|
---|
1189 | for (; size > 0; size -= copied)
|
---|
1190 | {
|
---|
1191 | mv_size_left (size);
|
---|
1192 | data_block = find_next_block ();
|
---|
1193 | if (!data_block)
|
---|
1194 | ERROR ((1, 0, _("Unexpected EOF in archive")));
|
---|
1195 | copied = available_space_after (data_block);
|
---|
1196 | if (copied > size)
|
---|
1197 | copied = size;
|
---|
1198 | memcpy (to, data_block->buffer, copied);
|
---|
1199 | to += copied;
|
---|
1200 | set_next_block_after ((union block *)
|
---|
1201 | (data_block->buffer + copied - 1));
|
---|
1202 | }
|
---|
1203 |
|
---|
1204 | mv_end ();
|
---|
1205 |
|
---|
1206 | stat_info->dumpdir = archive_dir;
|
---|
1207 | stat_info->skipped = true; /* For skip_member() and friends
|
---|
1208 | to work correctly */
|
---|
1209 | }
|
---|
1210 |
|
---|
1211 | /* Return T if STAT_INFO represents a dumpdir archive member.
|
---|
1212 | Note: can invalidate current_header. It happens if flush_archive()
|
---|
1213 | gets called within get_gnu_dumpdir() */
|
---|
1214 | bool
|
---|
1215 | is_dumpdir (struct tar_stat_info *stat_info)
|
---|
1216 | {
|
---|
1217 | if (stat_info->is_dumpdir && !stat_info->dumpdir)
|
---|
1218 | get_gnu_dumpdir (stat_info);
|
---|
1219 | return stat_info->is_dumpdir;
|
---|
1220 | }
|
---|
1221 |
|
---|
1222 | static bool
|
---|
1223 | dumpdir_ok (char *dumpdir)
|
---|
1224 | {
|
---|
1225 | char *p;
|
---|
1226 | int has_tempdir = 0;
|
---|
1227 | int expect = 0;
|
---|
1228 |
|
---|
1229 | for (p = dumpdir; *p; p += strlen (p) + 1)
|
---|
1230 | {
|
---|
1231 | if (expect && *p != expect)
|
---|
1232 | {
|
---|
1233 | ERROR ((0, 0,
|
---|
1234 | _("Malformed dumpdir: expected '%c' but found %#3o"),
|
---|
1235 | expect, *p));
|
---|
1236 | return false;
|
---|
1237 | }
|
---|
1238 | switch (*p)
|
---|
1239 | {
|
---|
1240 | case 'X':
|
---|
1241 | if (has_tempdir)
|
---|
1242 | {
|
---|
1243 | ERROR ((0, 0,
|
---|
1244 | _("Malformed dumpdir: 'X' duplicated")));
|
---|
1245 | return false;
|
---|
1246 | }
|
---|
1247 | else
|
---|
1248 | has_tempdir = 1;
|
---|
1249 | break;
|
---|
1250 |
|
---|
1251 | case 'R':
|
---|
1252 | if (p[1] == 0)
|
---|
1253 | {
|
---|
1254 | if (!has_tempdir)
|
---|
1255 | {
|
---|
1256 | ERROR ((0, 0,
|
---|
1257 | _("Malformed dumpdir: empty name in 'R'")));
|
---|
1258 | return false;
|
---|
1259 | }
|
---|
1260 | else
|
---|
1261 | has_tempdir = 0;
|
---|
1262 | }
|
---|
1263 | expect = 'T';
|
---|
1264 | break;
|
---|
1265 |
|
---|
1266 | case 'T':
|
---|
1267 | if (expect != 'T')
|
---|
1268 | {
|
---|
1269 | ERROR ((0, 0,
|
---|
1270 | _("Malformed dumpdir: 'T' not preceeded by 'R'")));
|
---|
1271 | return false;
|
---|
1272 | }
|
---|
1273 | if (p[1] == 0 && !has_tempdir)
|
---|
1274 | {
|
---|
1275 | ERROR ((0, 0,
|
---|
1276 | _("Malformed dumpdir: empty name in 'T'")));
|
---|
1277 | return false;
|
---|
1278 | }
|
---|
1279 | expect = 0;
|
---|
1280 | break;
|
---|
1281 |
|
---|
1282 | case 'N':
|
---|
1283 | case 'Y':
|
---|
1284 | case 'D':
|
---|
1285 | break;
|
---|
1286 |
|
---|
1287 | default:
|
---|
1288 | /* FIXME: bail out? */
|
---|
1289 | break;
|
---|
1290 | }
|
---|
1291 | }
|
---|
1292 |
|
---|
1293 | if (expect)
|
---|
1294 | {
|
---|
1295 | ERROR ((0, 0,
|
---|
1296 | _("Malformed dumpdir: expected '%c' but found end of data"),
|
---|
1297 | expect));
|
---|
1298 | return false;
|
---|
1299 | }
|
---|
1300 |
|
---|
1301 | if (has_tempdir)
|
---|
1302 | WARN ((0, 0, _("Malformed dumpdir: 'X' never used")));
|
---|
1303 |
|
---|
1304 | return true;
|
---|
1305 | }
|
---|
1306 |
|
---|
1307 | /* Examine the directories under directory_name and delete any
|
---|
1308 | files that were not there at the time of the back-up. */
|
---|
1309 | static bool
|
---|
1310 | try_purge_directory (char const *directory_name)
|
---|
1311 | {
|
---|
1312 | char *current_dir;
|
---|
1313 | char *cur, *arc, *p;
|
---|
1314 | char *temp_stub = NULL;
|
---|
1315 |
|
---|
1316 | if (!is_dumpdir (¤t_stat_info))
|
---|
1317 | return false;
|
---|
1318 |
|
---|
1319 | current_dir = savedir (directory_name);
|
---|
1320 |
|
---|
1321 | if (!current_dir)
|
---|
1322 | /* The directory doesn't exist now. It'll be created. In any
|
---|
1323 | case, we don't have to delete any files out of it. */
|
---|
1324 | return false;
|
---|
1325 |
|
---|
1326 | /* Verify if dump directory is sane */
|
---|
1327 | if (!dumpdir_ok (current_stat_info.dumpdir))
|
---|
1328 | return false;
|
---|
1329 |
|
---|
1330 | /* Process renames */
|
---|
1331 | for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
|
---|
1332 | {
|
---|
1333 | if (*arc == 'X')
|
---|
1334 | {
|
---|
1335 | #define TEMP_DIR_TEMPLATE "tar.XXXXXX"
|
---|
1336 | size_t len = strlen (arc + 1);
|
---|
1337 | temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
|
---|
1338 | memcpy (temp_stub, arc + 1, len);
|
---|
1339 | temp_stub[len] = '/';
|
---|
1340 | memcpy (temp_stub + len + 1, TEMP_DIR_TEMPLATE,
|
---|
1341 | sizeof TEMP_DIR_TEMPLATE);
|
---|
1342 | if (!mkdtemp (temp_stub))
|
---|
1343 | {
|
---|
1344 | ERROR ((0, errno,
|
---|
1345 | _("Cannot create temporary directory using template %s"),
|
---|
1346 | quote (temp_stub)));
|
---|
1347 | free (temp_stub);
|
---|
1348 | free (current_dir);
|
---|
1349 | return false;
|
---|
1350 | }
|
---|
1351 | }
|
---|
1352 | else if (*arc == 'R')
|
---|
1353 | {
|
---|
1354 | char *src, *dst;
|
---|
1355 | src = arc + 1;
|
---|
1356 | arc += strlen (arc) + 1;
|
---|
1357 | dst = arc + 1;
|
---|
1358 |
|
---|
1359 | if (*src == 0)
|
---|
1360 | src = temp_stub;
|
---|
1361 | else if (*dst == 0)
|
---|
1362 | dst = temp_stub;
|
---|
1363 |
|
---|
1364 | if (!rename_directory (src, dst))
|
---|
1365 | {
|
---|
1366 | free (temp_stub);
|
---|
1367 | free (current_dir);
|
---|
1368 | /* FIXME: Make sure purge_directory(dst) will return
|
---|
1369 | immediately */
|
---|
1370 | return false;
|
---|
1371 | }
|
---|
1372 | }
|
---|
1373 | }
|
---|
1374 |
|
---|
1375 | free (temp_stub);
|
---|
1376 |
|
---|
1377 | /* Process deletes */
|
---|
1378 | p = NULL;
|
---|
1379 | for (cur = current_dir; *cur; cur += strlen (cur) + 1)
|
---|
1380 | {
|
---|
1381 | const char *entry;
|
---|
1382 | struct stat st;
|
---|
1383 | if (p)
|
---|
1384 | free (p);
|
---|
1385 | p = new_name (directory_name, cur);
|
---|
1386 |
|
---|
1387 | if (deref_stat (false, p, &st))
|
---|
1388 | {
|
---|
1389 | if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
|
---|
1390 | dirs and check it here? */
|
---|
1391 | {
|
---|
1392 | stat_diag (p);
|
---|
1393 | WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
|
---|
1394 | quotearg_colon (p)));
|
---|
1395 | }
|
---|
1396 | continue;
|
---|
1397 | }
|
---|
1398 |
|
---|
1399 | if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
|
---|
1400 | || (*entry == 'D' && !S_ISDIR (st.st_mode))
|
---|
1401 | || (*entry == 'Y' && S_ISDIR (st.st_mode)))
|
---|
1402 | {
|
---|
1403 | if (one_file_system_option && st.st_dev != root_device)
|
---|
1404 | {
|
---|
1405 | WARN ((0, 0,
|
---|
1406 | _("%s: directory is on a different device: not purging"),
|
---|
1407 | quotearg_colon (p)));
|
---|
1408 | continue;
|
---|
1409 | }
|
---|
1410 |
|
---|
1411 | if (! interactive_option || confirm ("delete", p))
|
---|
1412 | {
|
---|
1413 | if (verbose_option)
|
---|
1414 | fprintf (stdlis, _("%s: Deleting %s\n"),
|
---|
1415 | program_name, quote (p));
|
---|
1416 | if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION))
|
---|
1417 | {
|
---|
1418 | int e = errno;
|
---|
1419 | ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
|
---|
1420 | }
|
---|
1421 | }
|
---|
1422 | }
|
---|
1423 | }
|
---|
1424 | free (p);
|
---|
1425 |
|
---|
1426 | free (current_dir);
|
---|
1427 | return true;
|
---|
1428 | }
|
---|
1429 |
|
---|
1430 | void
|
---|
1431 | purge_directory (char const *directory_name)
|
---|
1432 | {
|
---|
1433 | if (!try_purge_directory (directory_name))
|
---|
1434 | skip_member ();
|
---|
1435 | }
|
---|
1436 |
|
---|
1437 | void
|
---|
1438 | list_dumpdir (char *buffer, size_t size)
|
---|
1439 | {
|
---|
1440 | while (size)
|
---|
1441 | {
|
---|
1442 | switch (*buffer)
|
---|
1443 | {
|
---|
1444 | case 'Y':
|
---|
1445 | case 'N':
|
---|
1446 | case 'D':
|
---|
1447 | case 'R':
|
---|
1448 | case 'T':
|
---|
1449 | case 'X':
|
---|
1450 | fprintf (stdlis, "%c ", *buffer);
|
---|
1451 | buffer++;
|
---|
1452 | size--;
|
---|
1453 | break;
|
---|
1454 |
|
---|
1455 | case 0:
|
---|
1456 | fputc ('\n', stdlis);
|
---|
1457 | buffer++;
|
---|
1458 | size--;
|
---|
1459 | break;
|
---|
1460 |
|
---|
1461 | default:
|
---|
1462 | fputc (*buffer, stdlis);
|
---|
1463 | buffer++;
|
---|
1464 | size--;
|
---|
1465 | }
|
---|
1466 | }
|
---|
1467 | }
|
---|