/* Read incremental snapshot format 2 */ static void read_incr_db_2 (void) { struct obstack stk; char offbuf[INT_BUFSIZE_BOUND (off_t)]; obstack_init (&stk); read_timespec (listed_incremental_stream, &newer_mtime_option); for (;;) { intmax_t i; struct timespec mtime; dev_t dev; ino_t ino; bool nfs; char *name; char *content; size_t s; if (! read_num (listed_incremental_stream, "nfs", 0, 1, &i)) return; /* Normal return */ nfs = i; read_timespec (listed_incremental_stream, &mtime); if (! read_num (listed_incremental_stream, "dev", TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t), &i)) break; dev = i; if (! read_num (listed_incremental_stream, "ino", TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t), &i)) break; ino = i; if (read_obstack (listed_incremental_stream, &stk, &s)) break; name = obstack_finish (&stk); while (read_obstack (listed_incremental_stream, &stk, &s) == 0 && s > 1) ; if (getc (listed_incremental_stream) != 0) FATAL_ERROR ((0, 0, _("%s: byte %s: %s"), quotearg_colon (listed_incremental_option), offtostr (ftello (listed_incremental_stream), offbuf), _("Missing record terminator"))); content = obstack_finish (&stk); note_directory (name, mtime, dev, ino, nfs, false, content); obstack_free (&stk, content); } FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); }
/* Read incremental snapshot format 2 */ static void read_incr_db_2 () { uintmax_t u; struct obstack stk; obstack_init (&stk); read_timespec (listed_incremental_stream, &newer_mtime_option); for (;;) { struct timespec mtime; dev_t dev; ino_t ino; bool nfs; char *name; char *content; size_t s; if (read_num (listed_incremental_stream, 1, &u)) return; /* Normal return */ nfs = u; read_timespec (listed_incremental_stream, &mtime); if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u)) break; dev = u; if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u)) break; ino = u; if (read_obstack (listed_incremental_stream, &stk, &s)) break; name = obstack_finish (&stk); while (read_obstack (listed_incremental_stream, &stk, &s) == 0 && s > 1) ; if (getc (listed_incremental_stream) != 0) FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Missing record terminator"))); content = obstack_finish (&stk); note_directory (name, mtime, dev, ino, nfs, false, content); obstack_free (&stk, content); } FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); }
/* Read incremental snapshot formats 0 and 1 */ static void read_incr_db_01 (int version, const char *initbuf) { int n; uintmax_t u; time_t sec; long int nsec; char *buf = NULL; size_t bufsize = 0; char *ebuf; long lineno = 1; if (version == 1) { if (getline (&buf, &bufsize, listed_incremental_stream) <= 0) { read_error (listed_incremental_option); free (buf); return; } ++lineno; } else { buf = strdup (initbuf); bufsize = strlen (buf) + 1; } sec = TYPE_MINIMUM (time_t); nsec = -1; errno = 0; u = strtoumax (buf, &ebuf, 10); if (!errno && TYPE_MAXIMUM (time_t) < u) errno = ERANGE; if (errno || buf == ebuf) ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); else { sec = u; if (version == 1 && *ebuf) { char const *buf_ns = ebuf + 1; errno = 0; u = strtoumax (buf_ns, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || buf_ns == ebuf) { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); sec = TYPE_MINIMUM (time_t); } else nsec = u; } else { /* pre-1 incremental format does not contain nanoseconds */ nsec = 0; } } newer_mtime_option.tv_sec = sec; newer_mtime_option.tv_nsec = nsec; while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream))) { dev_t dev; ino_t ino; bool nfs = buf[0] == '+'; char *strp = buf + nfs; struct timespec mtime; lineno++; if (buf[n - 1] == '\n') buf[n - 1] = '\0'; if (version == 1) { errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (time_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (seconds)"))); sec = (time_t) -1; } else sec = u; strp = ebuf; errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (nanoseconds)"))); nsec = -1; } else nsec = u; mtime.tv_sec = sec; mtime.tv_nsec = nsec; strp = ebuf; } else memset (&mtime, 0, sizeof mtime); errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (dev_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid device number"))); dev = (dev_t) -1; } else dev = u; strp = ebuf; errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && TYPE_MAXIMUM (ino_t) < u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid inode number"))); ino = (ino_t) -1; } else ino = u; strp = ebuf; strp++; unquote_string (strp); note_directory (strp, mtime, dev, ino, nfs, false, NULL); } free (buf); }
static struct directory * procdir (const char *name_buffer, struct stat *stat_data, dev_t device, int flag, char *entry) { struct directory *directory; bool nfs = NFS_FILE_STAT (*stat_data); if ((directory = find_directory (name_buffer)) != NULL) { if (DIR_IS_INITED (directory)) { if (flag & PD_FORCE_INIT) { assign_string (&directory->name, name_buffer); } else { *entry = 'N'; /* Avoid duplicating this directory */ return directory; } } if (strcmp (directory->name, name_buffer)) { *entry = 'N'; return directory; } /* With NFS, the same file can have two different devices if an NFS directory is mounted in multiple locations, which is relatively common when automounting. To avoid spurious incremental redumping of directories, consider all NFS devices as equal, relying on the i-node to establish differences. */ if (! ((!check_device_option || (DIR_IS_NFS (directory) && nfs) || directory->device_number == stat_data->st_dev) && directory->inode_number == stat_data->st_ino)) { /* FIXME: find_directory_meta ignores nfs */ struct directory *d = find_directory_meta (stat_data->st_dev, stat_data->st_ino); if (d) { if (strcmp (d->name, name_buffer)) { WARNOPT (WARN_RENAME_DIRECTORY, (0, 0, _("%s: Directory has been renamed from %s"), quotearg_colon (name_buffer), quote_n (1, d->name))); directory->orig = d; DIR_SET_FLAG (directory, DIRF_RENAMED); dirlist_replace_prefix (d->name, name_buffer); } directory->children = CHANGED_CHILDREN; } else { WARNOPT (WARN_RENAME_DIRECTORY, (0, 0, _("%s: Directory has been renamed"), quotearg_colon (name_buffer))); directory->children = ALL_CHILDREN; directory->device_number = stat_data->st_dev; directory->inode_number = stat_data->st_ino; } if (nfs) DIR_SET_FLAG (directory, DIRF_NFS); } else directory->children = CHANGED_CHILDREN; DIR_SET_FLAG (directory, DIRF_FOUND); } else { struct directory *d = find_directory_meta (stat_data->st_dev, stat_data->st_ino); directory = note_directory (name_buffer, get_stat_mtime(stat_data), stat_data->st_dev, stat_data->st_ino, nfs, true, NULL); if (d) { if (strcmp (d->name, name_buffer)) { WARNOPT (WARN_RENAME_DIRECTORY, (0, 0, _("%s: Directory has been renamed from %s"), quotearg_colon (name_buffer), quote_n (1, d->name))); directory->orig = d; DIR_SET_FLAG (directory, DIRF_RENAMED); dirlist_replace_prefix (d->name, name_buffer); } directory->children = CHANGED_CHILDREN; } else { DIR_SET_FLAG (directory, DIRF_NEW); WARNOPT (WARN_NEW_DIRECTORY, (0, 0, _("%s: Directory is new"), quotearg_colon (name_buffer))); directory->children = (listed_incremental_option || (OLDER_STAT_TIME (*stat_data, m) || (after_date_option && OLDER_STAT_TIME (*stat_data, c)))) ? ALL_CHILDREN : CHANGED_CHILDREN; } } /* If the directory is on another device and --one-file-system was given, omit it... */ if (one_file_system_option && device != stat_data->st_dev /* ... except if it was explicitely given in the command line */ && !is_individual_file (name_buffer)) /* FIXME: WARNOPT (WARN_XDEV, (0, 0, _("%s: directory is on a different filesystem; not dumped"), quotearg_colon (directory->name))); */ directory->children = NO_CHILDREN; else if (flag & PD_FORCE_CHILDREN) { directory->children = PD_CHILDREN(flag); if (directory->children == NO_CHILDREN) *entry = 'N'; } DIR_SET_FLAG (directory, DIRF_INIT); if (directory->children != NO_CHILDREN) { const char *tag_file_name; switch (check_exclusion_tags (name_buffer, &tag_file_name)) { case exclusion_tag_all: /* This warning can be duplicated by code in dump_file0, but only in case when the topmost directory being archived contains an exclusion tag. */ exclusion_tag_warning (name_buffer, tag_file_name, _("directory not dumped")); *entry = 'N'; directory->children = NO_CHILDREN; break; case exclusion_tag_contents: exclusion_tag_warning (name_buffer, tag_file_name, _("contents not dumped")); directory->children = NO_CHILDREN; break; case exclusion_tag_under: exclusion_tag_warning (name_buffer, tag_file_name, _("contents not dumped")); directory->tagfile = tag_file_name; break; case exclusion_tag_none: break; } } return directory; }
static void read_directory_file (void) { char *strp; FILE *fp; char buf[512]; /* FIXME: use a symbol */ static char *path = NULL; if (path == NULL) path = xmalloc (PATH_MAX); time (&time_now); if (listed_incremental_option[0] != '/' #if DOSWIN /* The case of DOSWIN absolute file name with a drive letter. */ && !(listed_incremental_option[0] && listed_incremental_option[1] == ':') #endif ) { char *current_directory = xgetcwd (); if (!current_directory) FATAL_ERROR ((0, 0, _("Could not get current directory"))); listed_incremental_option = concat_with_slash (current_directory, listed_incremental_option); } fp = fopen (listed_incremental_option, "r"); if (fp == 0 && errno != ENOENT) { ERROR ((0, errno, _("Cannot open %s"), listed_incremental_option)); return; } if (!fp) return; fgets (buf, sizeof (buf), fp); /* FIXME: Using newer_ctime_option as a first time flag looks fairly dubious to me! So, using -N with incremental might be buggy just because of the next few lines. I saw a few unexplained, almost harsh advices from FSF people about *not* using -N with incremental dumps, and here might lie (part of) the reason. */ if (!newer_ctime_option) { time_option_threshold = atol (buf); newer_ctime_option = true; } while (fgets (buf, sizeof (buf), fp)) { dev_t device_number; ino_t inode_number; strp = &buf[strlen (buf)]; if (strp[-1] == '\n') strp[-1] = '\0'; /* FIXME: For files ending with an incomplete line, maybe a NUL might be missing, here... */ strp = buf; device_number = atol (strp); while (ISDIGIT (*strp)) strp++; inode_number = atol (strp); while (ISSPACE (*strp)) strp++; while (ISDIGIT (*strp)) strp++; strp++; unquote_string (strp); note_directory (strp, device_number, inode_number, NULL); } if (fclose (fp) == EOF) ERROR ((0, errno, "%s", listed_incremental_option)); }
char * get_directory_contents (char *path, dev_t device) { struct accumulator *accumulator; /* Recursively scan the given PATH. */ { DIR *dirp = opendir (path); /* for scanning directory */ struct dirent *entry; /* directory entry being scanned */ char *name_buffer; /* directory, `/', and directory member */ size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */ size_t name_length; /* used length in name_buffer */ struct directory *directory; /* for checking if already already seen */ bool all_children; if (dirp == NULL) { ERROR ((0, errno, _("Cannot open directory %s"), path)); return NULL; } errno = 0; /* FIXME: errno should be read-only */ name_buffer_size = strlen (path) + NAME_FIELD_SIZE; name_buffer = xmalloc (name_buffer_size + 2); strcpy (name_buffer, path); if (path[strlen (path) - 1] != '/') strcat (name_buffer, "/"); name_length = strlen (name_buffer); directory = find_directory (path); all_children = directory ? directory->all_new : false; accumulator = new_accumulator (); while (entry = readdir (dirp), entry) { struct stat stat_info; /* Skip `.' and `..'. */ if (is_dot_or_dotdot (entry->d_name)) continue; if (NAMLEN (entry) + name_length >= name_buffer_size) { while (NAMLEN (entry) + name_length >= name_buffer_size) name_buffer_size += NAME_FIELD_SIZE; name_buffer = (char *) xrealloc (name_buffer, name_buffer_size + 2); } strcpy (name_buffer + name_length, entry->d_name); if (dereference_option #ifdef AIX ? statx (name_buffer, &stat_info, STATSIZE, STX_HIDDEN) : statx (name_buffer, &stat_info, STATSIZE, STX_HIDDEN | STX_LINK) #else ? stat (name_buffer, &stat_info) : lstat (name_buffer, &stat_info) #endif ) { ERROR ((0, errno, _("Cannot stat %s"), name_buffer)); continue; } if ((one_file_system_option && device != stat_info.st_dev) || (exclude_option && check_exclude (name_buffer))) add_to_accumulator (accumulator, "N", (size_t) 1); #ifdef AIX else if (S_ISHIDDEN (stat_info.st_mode)) { add_to_accumulator (accumulator, "D", (size_t) 1); strcat (entry->d_name, "A"); entry->d_namlen++; } #endif else if (S_ISDIR (stat_info.st_mode)) { if (directory = find_directory (name_buffer), directory) { /* The same file can have two different devices if an NFS directory is mounted in multiple locations, which is relatively common when automounting. For avoiding spurious incremental redumping of directories, we have to plainly consider all NFS devices as equal, relying on the i-node only to establish differences. Devices having the high bit set usually are NFS devices. */ /* FIXME: Göran Uddeborg <*****@*****.**> says, on 1996-09-20, that SunOS 5/Solaris 2 uses unsigned long for the device number type. */ if ((((short) directory->device_number >= 0 || (short) stat_info.st_dev >= 0) && directory->device_number != stat_info.st_dev) || directory->inode_number != stat_info.st_ino) { if (verbose_option) WARN ((0, 0, _("Directory %s has been renamed"), name_buffer)); directory->all_new = true; directory->device_number = stat_info.st_dev; directory->inode_number = stat_info.st_ino; } directory->dir_text = ""; } else { if (verbose_option) WARN ((0, 0, _("Directory %s is new"), name_buffer)); note_directory (name_buffer, stat_info.st_dev, stat_info.st_ino, ""); directory = find_directory (name_buffer); directory->all_new = true; } if (all_children && directory) directory->all_new = true; add_to_accumulator (accumulator, "D", (size_t) 1); } else if (all_children || FILE_IS_NEW_ENOUGH (&stat_info)) add_to_accumulator (accumulator, "Y", (size_t) 1); else add_to_accumulator (accumulator, "N", (size_t) 1); add_to_accumulator (accumulator, entry->d_name, NAMLEN (entry) + 1); } add_to_accumulator (accumulator, "\000\000", (size_t) 2); free (name_buffer); closedir (dirp); } /* Sort the contents of the directory, now that we have it all. */ { char *pointer = get_accumulator (accumulator); size_t counter; char *cursor; char *buffer; char **array; char **array_cursor; counter = 0; for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1) counter++; if (counter == 0) { delete_accumulator (accumulator); return NULL; } array = (char **) xmalloc (sizeof (char *) * (counter + 1)); array_cursor = array; for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1) *array_cursor++ = cursor; *array_cursor = NULL; qsort ((voidstar) array, counter, sizeof (char *), compare_dirents); buffer = (char *) xmalloc ((size_t) (cursor - pointer + 2)); cursor = buffer; for (array_cursor = array; *array_cursor; array_cursor++) { char *string = *array_cursor; while ((*cursor++ = *string++)) continue; } *cursor = '\0'; delete_accumulator (accumulator); free (array); return buffer; } }
/* Read incremental snapshot formats 0 and 1 */ static void read_incr_db_01 (int version, const char *initbuf) { int n; uintmax_t u; char *buf = NULL; size_t bufsize = 0; char *ebuf; long lineno = 1; if (version == 1) { if (getline (&buf, &bufsize, listed_incremental_stream) <= 0) { read_error (listed_incremental_option); free (buf); return; } ++lineno; } else { buf = strdup (initbuf); bufsize = strlen (buf) + 1; } newer_mtime_option = decode_timespec (buf, &ebuf, false); if (! valid_timespec (newer_mtime_option)) ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); else { if (version == 1 && *ebuf) { char const *buf_ns = ebuf + 1; errno = 0; u = strtoumax (buf_ns, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || buf_ns == ebuf) { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid time stamp"))); newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t); newer_mtime_option.tv_nsec = -1; } else newer_mtime_option.tv_nsec = u; } } while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream))) { dev_t dev; ino_t ino; bool nfs = buf[0] == '+'; char *strp = buf + nfs; struct timespec mtime; lineno++; if (buf[n - 1] == '\n') buf[n - 1] = '\0'; if (version == 1) { mtime = decode_timespec (strp, &ebuf, false); strp = ebuf; if (!valid_timespec (mtime) || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time"))); errno = 0; u = strtoumax (strp, &ebuf, 10); if (!errno && BILLION <= u) errno = ERANGE; if (errno || strp == ebuf || *ebuf != ' ') { ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid modification time (nanoseconds)"))); mtime.tv_nsec = -1; } else mtime.tv_nsec = u; strp = ebuf; } else mtime.tv_sec = mtime.tv_nsec = 0; dev = strtosysint (strp, &ebuf, TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t)); strp = ebuf; if (errno || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid device number"))); ino = strtosysint (strp, &ebuf, TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t)); strp = ebuf; if (errno || *strp != ' ') ERROR ((0, errno, "%s:%ld: %s", quotearg_colon (listed_incremental_option), lineno, _("Invalid inode number"))); strp++; unquote_string (strp); note_directory (strp, mtime, dev, ino, nfs, false, NULL); } free (buf); }
static void read_directory_file (void) { dev_t device_number; ino_t inode_number; char *strp; FILE *fp; char buf[512]; static char *path = NULL; if (path == NULL) path = xmalloc (PATH_MAX); time (&time_now); if (listed_incremental_option[0] != PATHSEP) { #if HAVE_GETCWD if (!getcwd (path, PATH_MAX)) FATAL_ERROR ((0, 0, _("Could not get current directory"))); #else char *getwd (); if (!getwd (path)) FATAL_ERROR ((0, 0, _("Could not get current directory: %s"), path)); #endif if (strlen (path) + 1 + strlen (listed_incremental_option) + 1 > PATH_MAX) ERROR ((TAREXIT_FAILURE, 0, _("File name %s/%s too long"), path, listed_incremental_option)); strcat (path, "/"); strcat (path, listed_incremental_option); listed_incremental_option = path; } fp = fopen (listed_incremental_option, "r"); if (fp == 0 && errno != ENOENT) { ERROR ((0, errno, _("Cannot open %s"), listed_incremental_option)); return; } if (!fp) return; fgets (buf, sizeof (buf), fp); /* FIXME: Using after_date_option as a first time flag looks fairly dubious to me! So, using -N with incremental might be buggy just because of the next few lines. I saw a few unexplained, almost harsh advices, from other GNU people, about *not* using -N with incremental dumps, and here might lie (part of) the reason. */ if (!after_date_option) { newer_mtime_option = atol (buf); after_date_option = 1; } while (fgets (buf, sizeof (buf), fp)) { strp = &buf[strlen (buf)]; if (strp[-1] == '\n') strp[-1] = '\0'; /* FIXME: For files ending with an incomplete line, maybe a NUL might be missing, here... */ strp = buf; device_number = atol (strp); while (ISDIGIT (*strp)) strp++; inode_number = atol (strp); while (ISSPACE (*strp)) strp++; while (ISDIGIT (*strp)) strp++; strp++; unquote_string (strp); note_directory (strp, device_number, inode_number, NULL); } if (fclose (fp) == EOF) ERROR ((0, errno, "%s", listed_incremental_option)); }