int remove_any_file (const char *path, int recurse) { struct L_STAT stat_buffer; if (L_LSTAT (path, &stat_buffer) < 0) return 0; if (S_ISDIR (stat_buffer.st_mode)) if (recurse) { DIR *dirp = opendir (path); struct dirent *dp; if (dirp == NULL) return 0; while (dp = readdir (dirp), dp && !is_dot_or_dotdot (dp->d_name)) { char *path_buffer = new_name (path, dp->d_name); if (!remove_any_file (path_buffer, 1)) { int saved_errno = errno; free (path_buffer); closedir (dirp); errno = saved_errno; /* FIXME: errno should be read-only */ return 0; } free (path_buffer); } closedir (dirp); return rmdir (path) >= 0; } else { /* FIXME: Saving errno might not be needed anymore, now that extract_archive tests for the special case before recovery. */ int saved_errno = errno; if (rmdir (path) >= 0) return 1; errno = saved_errno; /* FIXME: errno should be read-only */ return 0; } return unlink (path) >= 0; }
/* 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 incremental_restore (int skipcrud) { char *current_dir; char *archive_dir; struct accumulator *accumulator; char *p; DIR *dirp; struct dirent *d; char *cur, *arc; long size; size_t size_to_copy; union block *data_block; char *to; #define CURRENT_FILE_NAME (skipcrud + current.name) dirp = opendir (CURRENT_FILE_NAME); if (!dirp) { /* The directory doesn't exist now. It'll be created. In any case, we don't have to delete any files out of it. */ skip_file (current.stat.st_size); return; } accumulator = new_accumulator (); while (d = readdir (dirp), d) { if (is_dot_or_dotdot (d->d_name)) continue; add_to_accumulator (accumulator, d->d_name, NAMLEN (d) + 1); } closedir (dirp); add_to_accumulator (accumulator, "", (size_t) 1); current_dir = get_accumulator (accumulator); archive_dir = (char *) xmalloc ((size_t) current.stat.st_size); to = archive_dir; for (size = current.stat.st_size; size > 0; size -= size_to_copy) { data_block = find_next_block (); if (!data_block) { ERROR ((0, 0, _("Unexpected end of file in archive"))); break; /* FIXME: What happens then? */ } size_to_copy = available_space_after (data_block); if (size_to_copy > size) size_to_copy = size; memcpy (to, data_block->buffer, size_to_copy); to += size_to_copy; set_next_block_after ((union block *) (data_block->buffer + size_to_copy - 1)); } for (cur = current_dir; *cur; cur += strlen (cur) + 1) { for (arc = archive_dir; *arc; arc += strlen (arc) + 1) { arc++; if (!strcmp (arc, cur)) break; } if (*arc == '\0') { p = concat_with_slash (CURRENT_FILE_NAME, cur); if (interactive_option && !confirm (_("delete %s?"), p)) { free (p); continue; } if (verbose_option) { if (checkpoint_option) flush_progress_dots (); fprintf (stdlis, _("%s: Deleting %s\n"), program_name, p); } if (!remove_any_file (p, true)) ERROR ((0, errno, _("Error while deleting %s"), p)); free (p); } } delete_accumulator (accumulator); free (archive_dir); #undef CURRENT_FILE_NAME }
/* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory, then if OPTION is RECURSIVE_REMOVE_OPTION is set remove FILE_NAME recursively; otherwise, remove it only if it is empty. If FILE_NAME is a directory that cannot be removed (e.g., because it is nonempty) and if OPTION is WANT_DIRECTORY_REMOVE_OPTION, then return -1. Return 0 on error, with errno set; if FILE_NAME is obviously the working directory return zero with errno set to zero. */ int remove_any_file (const char *file_name, enum remove_option option) { /* Try unlink first if we cannot unlink directories, as this saves us a system call in the common case where we're removing a non-directory. */ bool try_unlink_first = cannot_unlink_dir (); if (try_unlink_first) { if (unlinkat (chdir_fd, file_name, 0) == 0) return 1; /* POSIX 1003.1-2001 requires EPERM when attempting to unlink a directory without appropriate privileges, but many Linux kernels return the more-sensible EISDIR. */ if (errno != EPERM && errno != EISDIR) return 0; } if (safer_rmdir (file_name) == 0) return 1; switch (errno) { case ENOTDIR: return !try_unlink_first && unlinkat (chdir_fd, file_name, 0) == 0; case 0: case EEXIST: #if defined ENOTEMPTY && ENOTEMPTY != EEXIST case ENOTEMPTY: #endif switch (option) { case ORDINARY_REMOVE_OPTION: break; case WANT_DIRECTORY_REMOVE_OPTION: return -1; case RECURSIVE_REMOVE_OPTION: { char *directory = tar_savedir (file_name, 0); char const *entry; size_t entrylen; if (! directory) return 0; for (entry = directory; (entrylen = strlen (entry)) != 0; entry += entrylen + 1) { char *file_name_buffer = new_name (file_name, entry); int r = remove_any_file (file_name_buffer, RECURSIVE_REMOVE_OPTION); int e = errno; free (file_name_buffer); if (! r) { free (directory); errno = e; return 0; } } free (directory); return safer_rmdir (file_name) == 0; } } break; } return 0; }