/* Print the names of things in the namelist that were not matched. */ void names_notfound (void) { struct name const *cursor; for (cursor = namelist; cursor; cursor = cursor->next) if (!WASFOUND (cursor) && cursor->name[0]) { regex_usage_warning (cursor->name); if (cursor->found_count == 0) ERROR ((0, 0, _("%s: Not found in archive"), quotearg_colon (cursor->name))); else ERROR ((0, 0, _("%s: Required occurrence not found in archive"), quotearg_colon (cursor->name))); } /* Don't bother freeing the name list; we're about to exit. */ namelist = NULL; nametail = NULL; if (same_order_option) { const char *name; while ((name = name_next (1)) != NULL) { regex_usage_warning (name); ERROR ((0, 0, _("%s: Not found in archive"), quotearg_colon (name))); } } }
/* 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"))); }
static void read_timespec (FILE *fp, struct timespec *pval) { int c = getc (fp); intmax_t i; uintmax_t u; if (c == '-') { read_negative_num (fp, TYPE_MINIMUM (time_t), &i); c = 0; pval->tv_sec = i; } else { c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u); pval->tv_sec = u; } if (c || read_num (fp, BILLION - 1, &u)) FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); pval->tv_nsec = u; }
void symlink_error (char const *contents, char const *name) { int e = errno; ERROR ((0, e, _("%s: Cannot create symlink to %s"), quotearg_colon (name), quote_n (1, contents))); }
void link_error (char const *target, char const *source) { int e = errno; ERROR ((0, e, _("%s: Cannot hard link to %s"), quotearg_colon (source), quote_n (1, target))); }
void chown_error_details (char const *name, uid_t uid, gid_t gid) { int e = errno; ERROR ((0, e, _("%s: Cannot change ownership to uid %lu, gid %lu"), quotearg_colon (name), (unsigned long) uid, (unsigned long) gid)); }
void close_stdout (void) { bool prev_fail = ferror (stdout); bool none_pending = (0 == __fpending (stdout)); bool fclose_fail = fclose (stdout); if (prev_fail || fclose_fail) { int e = fclose_fail ? errno : 0; char const *write_error; /* If ferror returned zero, no data remains to be flushed, and we'd otherwise fail with EBADF due to a failed fclose, then assume that it's ok to ignore the fclose failure. That can happen when a program like cp is invoked like this `cp a b >&-' (i.e., with stdout closed) and doesn't generate any output (hence no previous error and nothing to be flushed). */ if (e == EBADF && !prev_fail && none_pending) return; write_error = _("write error"); if (file_name) error (exit_failure, e, "%s: %s", quotearg_colon (file_name), write_error); else error (exit_failure, e, "%s", write_error); } }
void close_stdin (void) { bool fail = false; /* There is no need to flush stdin if we can determine quickly that stdin's input buffer is empty; in this case we know that if stdin is seekable, fseeko (stdin, 0, SEEK_CUR) == lseek (0, 0, SEEK_CUR). */ //if (freadahead (stdin) > 0) { /* Only attempt flush if stdin is seekable, as fflush is entitled to fail on non-seekable streams. */ if (fseek (stdin, 0, SEEK_CUR) == 0 && fflush (stdin) != 0) fail = true; } if (close_stream (stdin) != 0) fail = true; if (fail) { /* Report failure, but defer exit until after closing stdout, since the failure report should still be flushed. */ char const *close_error = _("error closing file"); if (file_name) error (0, errno, "%s: %s", quotearg_colon (file_name), close_error); else error (0, errno, "%s", close_error); } close_stdout (); if (fail) _exit (exit_failure); }
/* Check if FILE_NAME already exists and make a backup of it right now. Return success (nonzero) only if the backup is either unneeded, or successful. For now, directories are considered to never need backup. If THIS_IS_THE_ARCHIVE is nonzero, this is the archive and so, we do not have to backup block or character devices, nor remote entities. */ bool maybe_backup_file (const char *file_name, bool this_is_the_archive) { struct stat file_stat; assign_string (&before_backup_name, file_name); /* A run situation may exist between Emacs or other GNU programs trying to make a backup for the same file simultaneously. If theoretically possible, real problems are unlikely. Doing any better would require a convention, GNU-wide, for all programs doing backups. */ assign_string (&after_backup_name, 0); /* Check if we really need to backup the file. */ if (this_is_the_archive && _remdev (file_name)) return true; if (deref_stat (file_name, &file_stat) != 0) { if (errno == ENOENT) return true; stat_error (file_name); return false; } if (S_ISDIR (file_stat.st_mode)) return true; if (this_is_the_archive && (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode))) return true; after_backup_name = find_backup_file_name (file_name, backup_type); if (! after_backup_name) xalloc_die (); if (renameat (chdir_fd, before_backup_name, chdir_fd, after_backup_name) == 0) { if (verbose_option) fprintf (stdlis, _("Renaming %s to %s\n"), quote_n (0, before_backup_name), quote_n (1, after_backup_name)); return true; } else { /* The backup operation failed. */ int e = errno; ERROR ((0, e, _("%s: Cannot rename to %s"), quotearg_colon (before_backup_name), quote_n (1, after_backup_name))); assign_string (&after_backup_name, 0); return false; } }
static char * use_quotearg_colon (const char *str, size_t *len) { char *p = (*len == SIZE_MAX ? quotearg_colon (str) : quotearg_colon_mem (str, *len)); *len = strlen (p); return p; }
static void ATTRIBUTE_NORETURN randread_error (void const *file_name) { if (file_name) error (exit_failure, errno, errno == 0 ? _("%s: end of file") : _("%s: read error"), quotearg_colon (file_name)); abort (); }
void seek_warn_details (char const *name, off_t offset) { char buf[UINTMAX_STRSIZE_BOUND]; int e = errno; WARN ((0, e, _("%s: Warning: Cannot seek to %s"), quotearg_colon (name), STRINGIFY_BIGINT (offset, buf))); }
void chmod_error_details (char const *name, mode_t mode) { int e = errno; char buf[10]; decode_mode (mode, buf); ERROR ((0, e, _("%s: Cannot change mode to %s"), quotearg_colon (name), buf)); }
/* Report a warning associated with the system call CALL and the optional file name NAME. */ void call_arg_warn (char const *call, char const *name) { int e = errno; /* TRANSLATORS: %s after `Cannot' is a function name, e.g. `Cannot open'. Directly translating this to another language will not work, first because %s itself is not translated. Translate it as `%s: Function %s failed'. */ WARN ((0, e, _("%s: Warning: Cannot %s"), quotearg_colon (name), call)); }
/* Catenate file FILE_NAME to the archive without creating a header for it. It had better be a tar file or the archive is screwed. */ static void append_file (char *file_name) { int handle = open (file_name, O_RDONLY | O_BINARY); struct stat stat_data; if (handle < 0) { open_error (file_name); return; } if (fstat (handle, &stat_data) != 0) stat_error (file_name); else { off_t bytes_left = stat_data.st_size; while (bytes_left > 0) { union block *start = find_next_block (); size_t buffer_size = available_space_after (start); size_t status; char buf[UINTMAX_STRSIZE_BOUND]; if (bytes_left < buffer_size) { buffer_size = bytes_left; status = buffer_size % BLOCKSIZE; if (status) memset (start->buffer + bytes_left, 0, BLOCKSIZE - status); } status = safe_read (handle, start->buffer, buffer_size); if (status == SAFE_READ_ERROR) read_fatal_details (file_name, stat_data.st_size - bytes_left, buffer_size); if (status == 0) FATAL_ERROR ((0, 0, ngettext ("%s: File shrank by %s byte", "%s: File shrank by %s bytes", bytes_left), quotearg_colon (file_name), STRINGIFY_BIGINT (bytes_left, buf))); bytes_left -= status; set_next_block_after (start + (status - 1) / BLOCKSIZE); } } if (close (handle) != 0) close_error (file_name); }
void read_fatal_details (char const *name, off_t offset, size_t size) { char buf[UINTMAX_STRSIZE_BOUND]; int e = errno; FATAL_ERROR ((0, e, ngettext ("%s: Read error at byte %s, reading %lu byte", "%s: Read error at byte %s, reading %lu bytes", size), quotearg_colon (name), STRINGIFY_BIGINT (offset, buf), (unsigned long) size)); }
FILE * xfopen (const char *name, const char *mode) { FILE *ptr; ptr = fopen (name, mode); if (!ptr) error (EXIT_FAILURE, get_errno (), _("%s: cannot open"), quotearg_colon (name)); return ptr; }
void file_removed_diag (const char *name, bool top_level, void (*diagfn) (char const *name)) { if (!top_level && errno == ENOENT) { WARNOPT (WARN_FILE_REMOVED, (0, 0, _("%s: File removed before we read it"), quotearg_colon (name))); set_exit_status (TAREXIT_DIFFERS); } else diagfn (name); }
/* Sigh about something that differs by writing a MESSAGE to stdlis, given MESSAGE is nonzero. Also set the exit status if not already. */ void report_difference (struct tar_stat_info *st, const char *fmt, ...) { if (fmt) { va_list ap; fprintf (stdlis, "%s: ", quotearg_colon (st->file_name)); va_start (ap, fmt); vfprintf (stdlis, fmt, ap); va_end (ap); fprintf (stdlis, "\n"); } set_exit_status (TAREXIT_DIFFERS); }
void close_stdout (void) { if (close_stream (stdout) != 0) { char const *write_error = _("write error"); if (file_name) error (0, errno, "%s: %s", quotearg_colon (file_name), write_error); else error (0, errno, "%s", write_error); _exit (exit_failure); } if (close_stream (stderr) != 0) _exit (exit_failure); }
static void read_timespec (FILE *fp, struct timespec *pval) { intmax_t s, ns; if (read_num (fp, "sec", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), &s) && read_num (fp, "nsec", 0, BILLION - 1, &ns)) { pval->tv_sec = s; pval->tv_nsec = ns; } else { FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (listed_incremental_option), _("Unexpected EOF in snapshot file"))); } }
void close_stdout( void ) { if ( close_stream( *(int*)(134628384) ) != 0 ) { char *write_error = gettext( "write error" ); if ( file_name != 0 ) { error( 0, *(int*)(__errno_location( )), "%s: %s", quotearg_colon( file_name ), write_error ); } error( 0, *(int*)(__errno_location( )), "%s", write_error ); _exit( exit_failure ); } if ( close_stream( *(int*)(134628356) ) != 0 ) { _exit( exit_failure ); } return; }
/* Try to restore the recently backed up file to its original name. This is usually only needed after a failed extraction. */ void undo_last_backup (void) { if (after_backup_name) { if (rename (after_backup_name, before_backup_name) != 0) { int e = errno; ERROR ((0, e, _("%s: Cannot rename to %s"), quotearg_colon (after_backup_name), quote_n (1, before_backup_name))); } if (verbose_option) fprintf (stdlis, _("Renaming %s back to %s\n"), quote_n (0, after_backup_name), quote_n (1, before_backup_name)); assign_string (&after_backup_name, 0); } }
void close_stdout_status (int status) { int e = ferror (stdout) ? 0 : -1; if (__fpending (stdout) == 0) return; if (fclose (stdout) != 0) e = errno; if (0 <= e) { char const *write_error = _("write error"); if (file_name) error (status, e, "%s: %s", quotearg_colon (file_name), write_error); else error (status, e, "%s", write_error); } }
void close_stdout (void) { int e = ferror (stdout) ? 0 : -1; /* If the stream's error bit is clear and there is nothing to flush, then return right away. */ if (e && __fpending (stdout) == 0) return; if (fclose (stdout) != 0) e = errno; if (0 <= e) { char const *write_error = _("write error"); if (file_name) error (exit_failure, e, "%s: %s", quotearg_colon (file_name), write_error); else error (exit_failure, e, "%s", write_error); } }
/* 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; }
/* Examine the directories under directory_name and delete any files that were not there at the time of the back-up. */ static bool try_purge_directory (char const *directory_name) { char *current_dir; char *cur, *arc, *p; char *temp_stub = NULL; struct dumpdir *dump; if (!is_dumpdir (¤t_stat_info)) return false; current_dir = savedir (directory_name); if (!current_dir) /* The directory doesn't exist now. It'll be created. In any case, we don't have to delete any files out of it. */ return false; /* Verify if dump directory is sane */ if (!dumpdir_ok (current_stat_info.dumpdir)) return false; /* Process renames */ for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1) { if (*arc == 'X') { #define TEMP_DIR_TEMPLATE "tar.XXXXXX" size_t len = strlen (arc + 1); temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE); memcpy (temp_stub, arc + 1, len); temp_stub[len] = '/'; memcpy (temp_stub + len + 1, TEMP_DIR_TEMPLATE, sizeof TEMP_DIR_TEMPLATE); if (!mkdtemp (temp_stub)) { ERROR ((0, errno, _("Cannot create temporary directory using template %s"), quote (temp_stub))); free (temp_stub); free (current_dir); return false; } } else if (*arc == 'R') { char *src, *dst; src = arc + 1; arc += strlen (arc) + 1; dst = arc + 1; /* Ensure that neither source nor destination are absolute file names (unless permitted by -P option), and that they do not contain dubious parts (e.g. ../). This is an extra safety precaution. Besides, it might be necessary to extract from archives created with tar versions prior to 1.19. */ if (*src) src = safer_name_suffix (src, false, absolute_names_option); if (*dst) dst = safer_name_suffix (dst, false, absolute_names_option); if (*src == 0) src = temp_stub; else if (*dst == 0) dst = temp_stub; if (!rename_directory (src, dst)) { free (temp_stub); free (current_dir); /* FIXME: Make sure purge_directory(dst) will return immediately */ return false; } } } free (temp_stub); /* Process deletes */ dump = dumpdir_create (current_stat_info.dumpdir); p = NULL; for (cur = current_dir; *cur; cur += strlen (cur) + 1) { const char *entry; struct stat st; if (p) free (p); p = new_name (directory_name, cur); if (deref_stat (false, p, &st)) { if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed dirs and check it here? */ { stat_diag (p); WARN ((0, 0, _("%s: Not purging directory: unable to stat"), quotearg_colon (p))); } continue; } if (!(entry = dumpdir_locate (dump, cur)) || (*entry == 'D' && !S_ISDIR (st.st_mode)) || (*entry == 'Y' && S_ISDIR (st.st_mode))) { if (one_file_system_option && st.st_dev != root_device) { WARN ((0, 0, _("%s: directory is on a different device: not purging"), quotearg_colon (p))); continue; } if (! interactive_option || confirm ("delete", p)) { if (verbose_option) fprintf (stdlis, _("%s: Deleting %s\n"), program_name, quote (p)); if (! remove_any_file (p, RECURSIVE_REMOVE_OPTION)) { int e = errno; ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p))); } } } } free (p); dumpdir_free (dump); free (current_dir); return true; }
void info_attach_exclist (struct tar_stat_info *dir) { struct excfile *file; struct exclist *head = NULL, *tail = NULL, *ent; struct vcs_ignore_file *vcsfile; if (dir->exclude_list) return; for (file = excfile_head; file; file = file->next) { if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0) { FILE *fp; struct exclude *ex = NULL; int fd = subfile_open (dir, file->name, O_RDONLY); if (fd == -1) { open_error (file->name); continue; } fp = fdopen (fd, "r"); if (!fp) { ERROR ((0, errno, _("%s: fdopen failed"), file->name)); close (fd); continue; } if (!ex) ex = new_exclude (); vcsfile = get_vcs_ignore_file (file->name); if (vcsfile->initfn) vcsfile->data = vcsfile->initfn (vcsfile->data); if (add_exclude_fp (vcsfile->addfn, ex, fp, EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n', vcsfile->data)) { int e = errno; FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name))); } fclose (fp); ent = xmalloc (sizeof (*ent)); ent->excluded = ex; ent->flags = file->flags == EXCL_DEFAULT ? file->flags : vcsfile->flags; ent->prev = tail; ent->next = NULL; if (tail) tail->next = ent; else head = ent; tail = ent; } } dir->exclude_list = head; }