]> Dogcows Code - chaz/tar/blob - src/incremen.c
(sort_obstack): Fixed typo in the comment
[chaz/tar] / src / incremen.c
1 /* GNU dump extensions to tar.
2
3 Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
4 2003 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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #include "system.h"
21 #include <getline.h>
22 #include <hash.h>
23 #include <quotearg.h>
24 #include "common.h"
25 #define obstack_chunk_alloc xmalloc
26 #define obstack_chunk_free free
27 #include <obstack.h>
28
29 /* Incremental dump specialities. */
30
31 /* Which child files to save under a directory. */
32 enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
33
34 /* Directory attributes. */
35 struct directory
36 {
37 dev_t device_number; /* device number for directory */
38 ino_t inode_number; /* inode number for directory */
39 enum children children;
40 bool nfs;
41 bool found;
42 char name[1]; /* path name of directory */
43 };
44
45 static Hash_table *directory_table;
46
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)
50 #else
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)
53 #endif
54
55 /* Calculate the hash of a directory. */
56 static unsigned
57 hash_directory (void const *entry, unsigned n_buckets)
58 {
59 struct directory const *directory = entry;
60 return hash_string (directory->name, n_buckets);
61 }
62
63 /* Compare two directories for equality. */
64 static bool
65 compare_directories (void const *entry1, void const *entry2)
66 {
67 struct directory const *directory1 = entry1;
68 struct directory const *directory2 = entry2;
69 return strcmp (directory1->name, directory2->name) == 0;
70 }
71
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)
78 {
79 size_t size = offsetof (struct directory, name) + strlen (name) + 1;
80 struct directory *directory = xmalloc (size);
81
82 directory->device_number = dev;
83 directory->inode_number = ino;
84 directory->children = CHANGED_CHILDREN;
85 directory->nfs = nfs;
86 directory->found = found;
87 strcpy (directory->name, name);
88
89 if (! ((directory_table
90 || (directory_table = hash_initialize (0, 0, hash_directory,
91 compare_directories, 0)))
92 && hash_insert (directory_table, directory)))
93 xalloc_die ();
94
95 return directory;
96 }
97
98 /* Return a directory entry for a given path NAME, or zero if none found. */
99 static struct directory *
100 find_directory (char *name)
101 {
102 if (! directory_table)
103 return 0;
104 else
105 {
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);
110 }
111 }
112
113 static int
114 compare_dirents (const void *first, const void *second)
115 {
116 return strcmp ((*(char *const *) first) + 1,
117 (*(char *const *) second) + 1);
118 }
119
120 /* Recursively scan the given PATH. */
121 static void
122 scan_path (struct obstack *stk, char *path, dev_t device)
123 {
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;
132
133 if (! dirp)
134 {
135 savedir_error (path);
136 }
137 errno = 0;
138
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);
145
146 directory = find_directory (path);
147 children = directory ? directory->children : CHANGED_CHILDREN;
148
149 if (dirp && children != NO_CHILDREN)
150 for (entry = dirp;
151 (entrylen = strlen (entry)) != 0;
152 entry += entrylen + 1)
153 {
154 if (name_buffer_size <= entrylen + name_length)
155 {
156 do
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);
160 }
161 strcpy (name_buffer + name_length, entry);
162
163 if (excluded_name (name_buffer))
164 obstack_1grow (stk, 'N');
165 else
166 {
167 struct stat stat_data;
168
169 if (deref_stat (dereference_option, name_buffer, &stat_data))
170 {
171 stat_diag (name_buffer);
172 continue;
173 }
174
175 if (S_ISDIR (stat_data.st_mode))
176 {
177 bool nfs = NFS_FILE_STAT (stat_data);
178
179 if (directory = find_directory (name_buffer), directory)
180 {
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. */
187
188 if (! (((directory->nfs & nfs)
189 || directory->device_number == stat_data.st_dev)
190 && directory->inode_number == stat_data.st_ino))
191 {
192 if (verbose_option)
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;
199 }
200 directory->found = 1;
201 }
202 else
203 {
204 if (verbose_option)
205 WARN ((0, 0, _("%s: Directory is new"),
206 quotearg_colon (name_buffer)));
207 directory = note_directory (name_buffer,
208 stat_data.st_dev,
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))
215 ? ALL_CHILDREN
216 : CHANGED_CHILDREN);
217 }
218
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;
223
224 obstack_1grow (stk, 'D');
225 }
226
227 else if (one_file_system_option && device != stat_data.st_dev)
228 obstack_1grow (stk, 'N');
229
230 #ifdef S_ISHIDDEN
231 else if (S_ISHIDDEN (stat_data.st_mode))
232 {
233 obstack_1grow (stk, 'D');
234 obstack_grow (stk, entry, entrylen);
235 obstack_grow (stk, "A", 2);
236 continue;
237 }
238 #endif
239
240 else
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');
246 else
247 obstack_1grow (stk, 'Y');
248 }
249
250 obstack_grow (stk, entry, entrylen + 1);
251 }
252
253 obstack_grow (stk, "\000\000", 2);
254
255 free (name_buffer);
256 if (dirp)
257 free (dirp);
258 }
259
260 /* Sort the contents of the obstack, and convert it to the char * */
261 static char *
262 sort_obstack (struct obstack *stk)
263 {
264 char *pointer = obstack_finish (stk);
265 size_t counter;
266 char *cursor;
267 char *buffer;
268 char **array;
269 char **array_cursor;
270
271 counter = 0;
272 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
273 counter++;
274
275 if (!counter)
276 return NULL;
277
278 array = obstack_alloc (stk, sizeof (char *) * (counter + 1));
279
280 array_cursor = array;
281 for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
282 *array_cursor++ = cursor;
283 *array_cursor = 0;
284
285 qsort (array, counter, sizeof (char *), compare_dirents);
286
287 buffer = xmalloc (cursor - pointer + 2);
288
289 cursor = buffer;
290 for (array_cursor = array; *array_cursor; array_cursor++)
291 {
292 char *string = *array_cursor;
293
294 while ((*cursor++ = *string++))
295 continue;
296 }
297 *cursor = '\0';
298 return buffer;
299 }
300
301 char *
302 get_directory_contents (char *path, dev_t device)
303 {
304 struct obstack stk;
305 char *buffer;
306
307 obstack_init (&stk);
308 scan_path (&stk, path, device);
309 buffer = sort_obstack (&stk);
310 obstack_free (&stk, NULL);
311 return buffer;
312 }
313
314 \f
315
316 static FILE *listed_incremental_stream;
317
318 void
319 read_directory_file (void)
320 {
321 int fd;
322 FILE *fp;
323 char *buf = 0;
324 size_t bufsize;
325
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);
330 if (fd < 0)
331 {
332 open_error (listed_incremental_option);
333 return;
334 }
335
336 fp = fdopen (fd, "r+");
337 if (! fp)
338 {
339 open_error (listed_incremental_option);
340 close (fd);
341 return;
342 }
343
344 listed_incremental_stream = fp;
345
346 if (0 < getline (&buf, &bufsize, fp))
347 {
348 char *ebuf;
349 int n;
350 long lineno = 1;
351 unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
352 time_t t = u;
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")));
359 else
360 newer_mtime_option = t;
361
362 while (0 < (n = getline (&buf, &bufsize, fp)))
363 {
364 dev_t dev;
365 ino_t ino;
366 bool nfs = buf[0] == '+';
367 char *strp = buf + nfs;
368
369 lineno++;
370
371 if (buf[n - 1] == '\n')
372 buf[n - 1] = '\0';
373
374 errno = 0;
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")));
384 strp = ebuf;
385
386 errno = 0;
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")));
396 strp = ebuf;
397
398 strp++;
399 unquote_string (strp);
400 note_directory (strp, dev, ino, nfs, 0);
401 }
402 }
403
404 if (ferror (fp))
405 read_error (listed_incremental_option);
406 if (buf)
407 free (buf);
408 }
409
410 /* Output incremental data for the directory ENTRY to the file DATA.
411 Return nonzero if successful, preserving errno on write failure. */
412 static bool
413 write_directory_file_entry (void *entry, void *data)
414 {
415 struct directory const *directory = entry;
416 FILE *fp = data;
417
418 if (directory->found)
419 {
420 int e;
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);
426 e = errno;
427 if (str)
428 free (str);
429 errno = e;
430 }
431
432 return ! ferror (fp);
433 }
434
435 void
436 write_directory_file (void)
437 {
438 FILE *fp = listed_incremental_stream;
439
440 if (! fp)
441 return;
442
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);
447
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);
451 if (ferror (fp))
452 write_error (listed_incremental_option);
453 if (fclose (fp) != 0)
454 close_error (listed_incremental_option);
455 }
456 \f
457 /* Restoration of incremental dumps. */
458
459 void
460 gnu_restore (char const *directory_name)
461 {
462 char *archive_dir;
463 char *current_dir;
464 char *cur, *arc;
465 size_t size;
466 size_t copied;
467 union block *data_block;
468 char *to;
469
470 current_dir = savedir (directory_name);
471
472 if (!current_dir)
473 {
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. */
476
477 skip_member ();
478 return;
479 }
480
481 size = current_stat_info.stat.st_size;
482 if (size != current_stat_info.stat.st_size)
483 xalloc_die ();
484 archive_dir = xmalloc (size);
485 to = archive_dir;
486 for (; size > 0; size -= copied)
487 {
488 data_block = find_next_block ();
489 if (!data_block)
490 {
491 ERROR ((0, 0, _("Unexpected EOF in archive")));
492 break; /* FIXME: What happens then? */
493 }
494 copied = available_space_after (data_block);
495 if (copied > size)
496 copied = size;
497 memcpy (to, data_block->buffer, copied);
498 to += copied;
499 set_next_block_after ((union block *)
500 (data_block->buffer + copied - 1));
501 }
502
503 for (cur = current_dir; *cur; cur += strlen (cur) + 1)
504 {
505 for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
506 {
507 arc++;
508 if (!strcmp (arc, cur))
509 break;
510 }
511 if (*arc == '\0')
512 {
513 char *p = new_name (directory_name, cur);
514 if (! interactive_option || confirm ("delete", p))
515 {
516 if (verbose_option)
517 fprintf (stdlis, _("%s: Deleting %s\n"),
518 program_name, quote (p));
519 if (! remove_any_file (p, 1))
520 {
521 int e = errno;
522 ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
523 }
524 }
525 free (p);
526 }
527
528 }
529 free (current_dir);
530 free (archive_dir);
531 }
This page took 0.072081 seconds and 5 git commands to generate.