/* 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 void register_individual_file (char const *name) { struct stat st; if (deref_stat (dereference_option, name, &st) != 0) return; /* Will be complained about later */ if (S_ISDIR (st.st_mode)) return; hash_string_insert (&individual_file_table, name); }
void update_parent_directory (const char *name) { struct directory *directory; char *p; p = dir_name (name); directory = find_directory (p); if (directory) { struct stat st; if (deref_stat (dereference_option, p, &st) != 0) stat_diag (name); else directory->mtime = get_stat_mtime (&st); } free (p); }
void update_parent_directory (const char *name) { struct directory *directory; char *p; p = dir_name (name); directory = find_directory (p); if (directory) { struct stat st; if (deref_stat (dereference_option, p, &st) != 0) { if (errno != ENOENT) stat_diag (directory->name); /* else: should have been already reported */ } else directory->mtime = get_stat_mtime (&st); } free (p); }
/* Recursively scan the given directory DIR. DEVICE is the device number where DIR resides (for --one-file-system). If CMDLINE is true, the directory name was explicitly listed in the command line. Unless *PDIR is NULL, store there a pointer to the struct directory describing DIR. */ struct directory * scan_directory (char *dir, dev_t device, bool cmdline) { char *dirp = savedir (dir); /* for scanning directory */ namebuf_t nbuf; char *tmp; struct stat stat_data; struct directory *directory; char ch; if (! dirp) savedir_error (dir); tmp = xstrdup (dir); zap_slashes (tmp); if (deref_stat (dereference_option, tmp, &stat_data)) { dir_removed_diag (tmp, cmdline, stat_diag); free (tmp); free (dirp); return NULL; } directory = procdir (tmp, &stat_data, device, (cmdline ? PD_FORCE_INIT : 0), &ch); free (tmp); nbuf = namebuf_create (dir); if (dirp && directory->children != NO_CHILDREN) { char *entry; /* directory entry being scanned */ dumpdir_iter_t itr; makedumpdir (directory, dirp); for (entry = dumpdir_first (directory->dump, 1, &itr); entry; entry = dumpdir_next (itr)) { char *full_name = namebuf_name (nbuf, entry + 1); if (*entry == 'I') /* Ignored entry */ *entry = 'N'; else if (excluded_name (full_name)) *entry = 'N'; else { if (deref_stat (dereference_option, full_name, &stat_data)) { file_removed_diag (full_name, false, stat_diag); *entry = 'N'; continue; } if (S_ISDIR (stat_data.st_mode)) { int pd_flag = 0; if (!recursion_option) pd_flag |= PD_FORCE_CHILDREN | NO_CHILDREN; else if (directory->children == ALL_CHILDREN) pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN; *entry = 'D'; procdir (full_name, &stat_data, device, pd_flag, entry); } else if (one_file_system_option && device != stat_data.st_dev) *entry = 'N'; else if (*entry == 'Y') /* New entry, skip further checks */; /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */ else if (OLDER_STAT_TIME (stat_data, m) && (!after_date_option || OLDER_STAT_TIME (stat_data, c))) *entry = 'N'; else *entry = 'Y'; } } free (itr); } namebuf_free (nbuf); if (dirp) free (dirp); 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 collect_and_sort_names (void) { struct name *name; struct name *next_name, *prev_name; int num_names; struct stat statbuf; Hash_table *nametab; name_gather (); if (!namelist) addname (".", 0, false, NULL); if (listed_incremental_option) { switch (chdir_count ()) { case 0: break; case 1: if (namelist->change_dir == 0) USAGE_ERROR ((0, 0, _("Using -C option inside file list is not " "allowed with --listed-incremental"))); break; default: USAGE_ERROR ((0, 0, _("Only one -C option is allowed with " "--listed-incremental"))); } read_directory_file (); } num_names = 0; for (name = namelist; name; name = name->next, num_names++) { if (name->found_count || name->directory) continue; if (name->matching_flags & EXCLUDE_WILDCARDS) /* NOTE: EXCLUDE_ANCHORED is not relevant here */ /* FIXME: just skip regexps for now */ continue; chdir_do (name->change_dir); if (name->name[0] == 0) continue; if (deref_stat (dereference_option, name->name, &statbuf) != 0) { stat_diag (name->name); continue; } if (S_ISDIR (statbuf.st_mode)) { name->found_count++; add_hierarchy_to_namelist (name, statbuf.st_dev, true); } } namelist = merge_sort (namelist, num_names, compare_names); num_names = 0; nametab = hash_initialize (0, 0, name_hash, name_compare, NULL); for (name = namelist; name; name = next_name) { next_name = name->next; name->caname = normalize_filename (name->name); if (prev_name) { struct name *p = hash_lookup (nametab, name); if (p) { /* Keep the one listed in the command line */ if (!name->parent) { if (p->child) rebase_child_list (p->child, name); /* FIXME: remove_directory (p->caname); ? */ remname (p); free_name (p); num_names--; } else { if (name->child) rebase_child_list (name->child, p); /* FIXME: remove_directory (name->caname); ? */ remname (name); free_name (name); continue; } } } name->found_count = 0; if (!hash_insert (nametab, name)) xalloc_die (); prev_name = name; num_names++; } nametail = prev_name; hash_free (nametab); namelist = merge_sort (namelist, num_names, compare_names_found); if (listed_incremental_option) { for (name = namelist; name && name->name[0] == 0; name++) ; if (name) append_incremental_renames (name->directory); } }
/* Implement the 'r' (add files to end of archive), and 'u' (add files to end of archive if they aren't there, or are more up to date than the version in the archive) commands. */ void update_archive (void) { enum read_header previous_status = HEADER_STILL_UNREAD; bool found_end = false; name_gather (); open_archive (ACCESS_UPDATE); xheader_write_global (); while (!found_end) { enum read_header status = read_header (false); switch (status) { case HEADER_STILL_UNREAD: case HEADER_SUCCESS_EXTENDED: abort (); case HEADER_SUCCESS: { struct name *name; decode_header (current_header, ¤t_stat_info, ¤t_format, 0); archive_format = current_format; if (subcommand_option == UPDATE_SUBCOMMAND && (name = name_scan (current_stat_info.file_name)) != NULL) { struct stat s; chdir_do (name->change_dir); if (deref_stat (dereference_option, current_stat_info.file_name, &s) == 0 && s.st_mtime <= current_stat_info.stat.st_mtime) add_avoided_name (current_stat_info.file_name); } skip_member (); break; } case HEADER_ZERO_BLOCK: current_block = current_header; found_end = true; break; case HEADER_END_OF_FILE: found_end = true; break; case HEADER_FAILURE: set_next_block_after (current_header); switch (previous_status) { case HEADER_STILL_UNREAD: WARN ((0, 0, _("This does not look like a tar archive"))); /* Fall through. */ case HEADER_SUCCESS: case HEADER_ZERO_BLOCK: ERROR ((0, 0, _("Skipping to next header"))); /* Fall through. */ case HEADER_FAILURE: break; case HEADER_END_OF_FILE: case HEADER_SUCCESS_EXTENDED: abort (); } break; } tar_stat_destroy (¤t_stat_info); xheader_destroy (&extended_header); previous_status = status; } reset_eof (); time_to_start_writing = true; output_start = current_block->buffer; { char *file_name; while ((file_name = name_from_list ()) != NULL) { if (excluded_name (file_name)) continue; if (interactive_option && !confirm ("add", file_name)) continue; if (subcommand_option == CAT_SUBCOMMAND) append_file (file_name); else dump_file (file_name, 1, (dev_t) 0); } } write_eot (); close_archive (); names_notfound (); }
/* Recursively scan the given directory. */ static const char * scan_directory (char *dir, dev_t device) { char *dirp = savedir (dir); /* for scanning directory */ 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 stat stat_data; struct directory *directory; if (! dirp) savedir_error (dir); name_buffer_size = strlen (dir) + NAME_FIELD_SIZE; name_buffer = xmalloc (name_buffer_size + 2); strcpy (name_buffer, dir); if (! ISSLASH (dir[strlen (dir) - 1])) strcat (name_buffer, "/"); name_length = strlen (name_buffer); if (deref_stat (dereference_option, name_buffer, &stat_data)) { stat_diag (name_buffer); /* FIXME: used to be children = CHANGED_CHILDREN; but changed to: */ free (name_buffer); free (dirp); return NULL; } directory = procdir (name_buffer, &stat_data, device, 0, NULL); if (dirp && directory->children != NO_CHILDREN) { char *entry; /* directory entry being scanned */ size_t entrylen; /* length of directory entry */ dumpdir_iter_t itr; makedumpdir (directory, dirp); for (entry = dumpdir_first (directory->dump, 1, &itr); entry; entry = dumpdir_next (itr)) { entrylen = strlen (entry); if (name_buffer_size <= entrylen - 1 + name_length) { do name_buffer_size += NAME_FIELD_SIZE; while (name_buffer_size <= entrylen - 1 + name_length); name_buffer = xrealloc (name_buffer, name_buffer_size + 2); } strcpy (name_buffer + name_length, entry + 1); if (*entry == 'I') /* Ignored entry */ *entry = 'N'; else if (excluded_name (name_buffer)) *entry = 'N'; else { if (deref_stat (dereference_option, name_buffer, &stat_data)) { stat_diag (name_buffer); *entry = 'N'; continue; } if (S_ISDIR (stat_data.st_mode)) { int pd_flag = (verbose_option ? PD_VERBOSE : 0); if (!recursion_option) pd_flag |= PD_FORCE_CHILDREN | NO_CHILDREN; else if (directory->children == ALL_CHILDREN) pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN; *entry = 'D'; procdir (name_buffer, &stat_data, device, pd_flag, entry); } else if (one_file_system_option && device != stat_data.st_dev) *entry = 'N'; else if (*entry == 'Y') /* New entry, skip further checks */; /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */ else if (OLDER_STAT_TIME (stat_data, m) && (!after_date_option || OLDER_STAT_TIME (stat_data, c))) *entry = 'N'; else *entry = 'Y'; } } free (itr); } free (name_buffer); if (dirp) free (dirp); return directory->dump ? directory->dump->contents : NULL; }