X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=653e2a0d2f879f4bf95442275928ba69a4d129cb;hb=3b219f943e6b0b14c8f30176951b9339ae3ebaf4;hp=408208392ab64a69afd7a116c4c12960117ba5f2;hpb=8300c820d552b6608d1d1abd37b4ff4aef5a8a05;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 4082083..653e2a0 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,13 +1,13 @@ /* Extract files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any later + Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include "common.h" @@ -37,7 +37,8 @@ enum permstatus /* This file may have existed already; its permissions are unknown. */ UNKNOWN_PERMSTATUS, - /* This file was created using the permissions from the archive. */ + /* This file was created using the permissions from the archive, + except with S_IRWXG | S_IRWXO masked out if 0 < same_owner_option. */ ARCHIVED_PERMSTATUS, /* This is an intermediate directory; the archive did not specify @@ -143,18 +144,22 @@ set_mode (char const *file_name, char typeflag) { mode_t mode; + int chmod_errno; if (0 < same_permissions_option && permstatus != INTERDIR_PERMSTATUS) { mode = stat_info->st_mode; - /* If we created the file and it has a usual mode, then its mode - is normally set correctly already. But on many hosts, some + /* If we created the file and it has a mode that we set already + with O_CREAT, then its mode is often set correctly already. + But if we are changing ownership, the mode's group and and + other permission bits were omitted originally, so it's less + likely that the mode is OK now. Also, on many hosts, some directories inherit the setgid bits from their parents, so we we must set directories' modes explicitly. */ - if (permstatus == ARCHIVED_PERMSTATUS - && ! (mode & ~ MODE_RWX) + if ((permstatus == ARCHIVED_PERMSTATUS + && ! (mode & ~ (0 < same_owner_option ? S_IRWXU : MODE_RWX))) && typeflag != DIRTYPE && typeflag != GNUTYPE_DUMPDIR) return; @@ -182,8 +187,24 @@ set_mode (char const *file_name, mode = cur_info->st_mode ^ invert_permissions; } - if (chmod (file_name, mode) != 0) - chmod_error_details (file_name, mode); + chmod_errno = chmod (file_name, mode) == 0 ? 0 : errno; + if (chmod_errno == EPERM && (mode & S_ISUID) != 0) + { + /* On Solaris, chmod may fail if we don't have PRIV_ALL, because + setuid-root files would otherwise be a backdoor. See + http://opensolaris.org/jive/thread.jspa?threadID=95826 + (2009-09-03). */ + if (priv_set_restore_linkdir () == 0) + { + chmod_errno = chmod (file_name, mode) == 0 ? 0 : errno; + priv_set_remove_linkdir (); + } + } + if (chmod_errno) + { + errno = chmod_errno; + chmod_error_details (file_name, mode); + } } /* Check time after successfully setting FILE_NAME's time stamp to T. */ @@ -191,9 +212,10 @@ static void check_time (char const *file_name, struct timespec t) { if (t.tv_sec <= 0) - WARN ((0, 0, _("%s: implausibly old time stamp %s"), - file_name, tartime (t, true))); - else if (timespec_cmp (start_time, t) < 0) + WARNOPT (WARN_TIMESTAMP, + (0, 0, _("%s: implausibly old time stamp %s"), + file_name, tartime (t, true))); + else if (timespec_cmp (volume_start_time, t) < 0) { struct timespec now; gettime (&now); @@ -208,8 +230,9 @@ check_time (char const *file_name, struct timespec t) diff.tv_nsec += BILLION; diff.tv_sec--; } - WARN ((0, 0, _("%s: time stamp %s is %s s in the future"), - file_name, tartime (t, true), code_timespec (diff, buf))); + WARNOPT (WARN_TIMESTAMP, + (0, 0, _("%s: time stamp %s is %s s in the future"), + file_name, tartime (t, true), code_timespec (diff, buf))); } } } @@ -217,7 +240,7 @@ check_time (char const *file_name, struct timespec t) /* Restore stat attributes (owner, group, mode and times) for FILE_NAME, using information given in *ST. If CUR_INFO is nonzero, *CUR_INFO is the - file's currernt status. + file's current status. If not restoring permissions, invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. @@ -265,11 +288,11 @@ set_stat (char const *file_name, } /* Some systems allow non-root users to give files away. Once this - done, it is not possible anymore to change file permissions, so we - have to set permissions prior to possibly giving files away. */ - - set_mode (file_name, &st->stat, cur_info, - invert_permissions, permstatus, typeflag); + done, it is not possible anymore to change file permissions. + However, setting file permissions now would be incorrect, since + they would apply to the wrong user, and there would be a race + condition. So, don't use systems that allow non-root users to + give files away. */ } if (0 < same_owner_option && permstatus != INTERDIR_PERMSTATUS) @@ -278,29 +301,36 @@ set_stat (char const *file_name, the symbolic link itself. In this case, a mere chown would change the attributes of the file the symbolic link is pointing to, and should be avoided. */ + int chown_result = 1; if (typeflag == SYMTYPE) { #if HAVE_LCHOWN - if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) - chown_error_details (file_name, - st->stat.st_uid, st->stat.st_gid); + chown_result = lchown (file_name, st->stat.st_uid, st->stat.st_gid); #endif } else { - if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) - chown_error_details (file_name, - st->stat.st_uid, st->stat.st_gid); - - /* On a few systems, and in particular, those allowing to give files - away, changing the owner or group destroys the suid or sgid bits. - So let's attempt setting these bits once more. */ - if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) - set_mode (file_name, &st->stat, 0, - invert_permissions, permstatus, typeflag); + chown_result = chown (file_name, st->stat.st_uid, st->stat.st_gid); } + + if (chown_result == 0) + { + /* Changing the owner can flip st_mode bits in some cases, so + ignore cur_info if it might be obsolete now. */ + if (cur_info + && cur_info->st_mode & S_IXUGO + && cur_info->st_mode & (S_ISUID | S_ISGID)) + cur_info = NULL; + } + else if (chown_result < 0) + chown_error_details (file_name, + st->stat.st_uid, st->stat.st_gid); } + + if (typeflag != SYMTYPE) + set_mode (file_name, &st->stat, cur_info, + invert_permissions, permstatus, typeflag); } /* Remember to restore stat attributes (owner, group, mode and times) @@ -374,7 +404,8 @@ repair_delayed_set_stat (char const *dir, data->atime = current_stat_info.atime; data->mtime = current_stat_info.mtime; data->invert_permissions = - (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode)); + ((current_stat_info.stat.st_mode ^ st.st_mode) + & MODE_RWX & ~ current_umask); data->permstatus = ARCHIVED_PERMSTATUS; return; } @@ -392,13 +423,12 @@ static int make_directories (char *file_name) { char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); - char *cursor; /* points into the file name */ + char *cursor; /* points into the file name */ int did_something = 0; /* did we do anything yet? */ int mode; int invert_permissions; int status; - for (cursor = cursor0; *cursor; cursor++) { if (! ISSLASH (*cursor)) @@ -463,9 +493,13 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) if (stat (file_name, &st)) { - stat_warn (file_name); - /* Be on the safe side: if the file does exist assume it is newer */ - return errno != ENOENT; + if (errno != ENOENT) + { + stat_warn (file_name); + /* Be on the safe side: if the file does exist assume it is newer */ + return true; + } + return false; } if (!S_ISDIR (st.st_mode) && tar_timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) @@ -475,17 +509,24 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) return false; } +#define RECOVER_NO 0 +#define RECOVER_OK 1 +#define RECOVER_SKIP 2 + /* Attempt repairing what went wrong with the extraction. Delete an already existing file or create missing intermediate directories. - Return nonzero if we somewhat increased our chances at a successful - extraction. errno is properly restored on zero return. */ + Return RECOVER_OK if we somewhat increased our chances at a successful + extraction, RECOVER_NO if there are no chances, and RECOVER_SKIP if the + caller should skip extraction of that member. The value of errno is + properly restored on returning RECOVER_NO. */ + static int -maybe_recoverable (char *file_name, int *interdir_made) +maybe_recoverable (char *file_name, bool *interdir_made) { int e = errno; if (*interdir_made) - return 0; + return RECOVER_NO; switch (errno) { @@ -495,13 +536,13 @@ maybe_recoverable (char *file_name, int *interdir_made) switch (old_files_option) { case KEEP_OLD_FILES: - return 0; + return RECOVER_SKIP; case KEEP_NEWER_FILES: if (file_newer_p (file_name, ¤t_stat_info)) { errno = e; - return 0; + return RECOVER_NO; } /* FALL THROUGH */ @@ -511,7 +552,7 @@ maybe_recoverable (char *file_name, int *interdir_made) { int r = remove_any_file (file_name, ORDINARY_REMOVE_OPTION); errno = EEXIST; - return r; + return r > 0 ? RECOVER_OK : RECOVER_NO; } case UNLINK_FIRST_OLD_FILES: @@ -523,15 +564,15 @@ maybe_recoverable (char *file_name, int *interdir_made) if (! make_directories (file_name)) { errno = ENOENT; - return 0; + return RECOVER_NO; } - *interdir_made = 1; - return 1; + *interdir_made = true; + return RECOVER_OK; default: /* Just say we can't do anything about it... */ - return 0; + return RECOVER_NO; } } @@ -582,13 +623,13 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) if (! skip_this_one) { - struct tar_stat_info st; - st.stat.st_mode = data->mode; - st.stat.st_uid = data->uid; - st.stat.st_gid = data->gid; - st.atime = data->atime; - st.mtime = data->mtime; - set_stat (data->file_name, &st, cur_info, + struct tar_stat_info sb; + sb.stat.st_mode = data->mode; + sb.stat.st_uid = data->uid; + sb.stat.st_gid = data->gid; + sb.atime = data->atime; + sb.mtime = data->mtime; + set_stat (data->file_name, &sb, cur_info, data->invert_permissions, data->permstatus, DIRTYPE); } @@ -606,19 +647,17 @@ extract_dir (char *file_name, int typeflag) { int status; mode_t mode; - int interdir_made = 0; + bool interdir_made = false; /* Save 'root device' to avoid purging mount points. */ if (one_file_system_option && root_device == 0) { struct stat st; - char *dir = xgetcwd (); - if (deref_stat (true, dir, &st)) - stat_diag (dir); + if (stat (".", &st) != 0) + stat_diag ("."); else root_device = st.st_dev; - free (dir); } if (incremental_option) @@ -627,8 +666,9 @@ extract_dir (char *file_name, int typeflag) else if (typeflag == GNUTYPE_DUMPDIR) skip_member (); - mode = (current_stat_info.stat.st_mode | - (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; + mode = current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR); + if (0 < same_owner_option || current_stat_info.stat.st_mode & ~ MODE_RWX) + mode &= S_IRWXU; while ((status = mkdir (file_name, mode))) { @@ -647,6 +687,7 @@ extract_dir (char *file_name, int typeflag) } if (S_ISDIR (st.st_mode)) { + status = 0; mode = st.st_mode; break; } @@ -654,13 +695,21 @@ extract_dir (char *file_name, int typeflag) errno = EEXIST; } - if (maybe_recoverable (file_name, &interdir_made)) - continue; - - if (errno != EEXIST) + switch (maybe_recoverable (file_name, &interdir_made)) { - mkdir_error (file_name); - return 1; + case RECOVER_OK: + continue; + + case RECOVER_SKIP: + break; + + case RECOVER_NO: + if (errno != EEXIST) + { + mkdir_error (file_name); + return 1; + } + break; } break; } @@ -671,7 +720,8 @@ extract_dir (char *file_name, int typeflag) { if (status == 0) delay_set_stat (file_name, ¤t_stat_info, - MODE_RWX & (mode ^ current_stat_info.stat.st_mode), + ((mode ^ current_stat_info.stat.st_mode) + & MODE_RWX & ~ current_umask), ARCHIVED_PERMSTATUS); else /* For an already existing directory, invert_perms must be 0 */ delay_set_stat (file_name, ¤t_stat_info, @@ -683,14 +733,13 @@ extract_dir (char *file_name, int typeflag) static int -open_output_file (char *file_name, int typeflag) +open_output_file (char *file_name, int typeflag, mode_t mode) { int fd; int openflag = (O_WRONLY | O_BINARY | O_CREAT | (old_files_option == OVERWRITE_OLD_FILES ? O_TRUNC : O_EXCL)); - mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; #if O_CTG /* Contiguous files (on the Masscomp) have to specify the size in @@ -709,7 +758,8 @@ open_output_file (char *file_name, int typeflag) if (!conttype_diagnosed) { conttype_diagnosed = 1; - WARN ((0, 0, _("Extracting contiguous files as regular files"))); + WARNOPT (WARN_CONTIGUOUS_CAST, + (0, 0, _("Extracting contiguous files as regular files"))); } } fd = open (file_name, openflag, mode); @@ -728,7 +778,10 @@ extract_file (char *file_name, int typeflag) int status; size_t count; size_t written; - int interdir_made = 0; + bool interdir_made = false; + mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; /* FIXME: deal with protection issues. */ @@ -745,18 +798,24 @@ extract_file (char *file_name, int typeflag) } else { + int recover = RECOVER_NO; do - fd = open_output_file (file_name, typeflag); - while (fd < 0 && maybe_recoverable (file_name, &interdir_made)); + fd = open_output_file (file_name, typeflag, mode ^ invert_permissions); + while (fd < 0 + && (recover = maybe_recoverable (file_name, &interdir_made)) + == RECOVER_OK); if (fd < 0) { + skip_member (); + if (recover == RECOVER_SKIP) + return 0; open_error (file_name); return 1; } } - mv_begin (¤t_stat_info); + mv_begin_read (¤t_stat_info); if (current_stat_info.is_sparse) sparse_extract_file (fd, ¤t_stat_info, &size); else @@ -811,7 +870,7 @@ extract_file (char *file_name, int typeflag) if (to_command_option) sys_wait_command (); else - set_stat (file_name, ¤t_stat_info, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), typeflag); @@ -826,18 +885,28 @@ extract_file (char *file_name, int typeflag) process. */ static int -create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) +create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made) { int fd; struct stat st; while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0) - if (! maybe_recoverable (file_name, interdir_made)) - break; + { + switch (maybe_recoverable (file_name, interdir_made)) + { + case RECOVER_OK: + continue; + + case RECOVER_SKIP: + return 0; - if (fd < 0) - open_error (file_name); - else if (fstat (fd, &st) != 0) + case RECOVER_NO: + open_error (file_name); + return -1; + } + } + + if (fstat (fd, &st) != 0) { stat_error (file_name); close (fd); @@ -872,7 +941,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) if (h && ! h->after_links && strncmp (file_name, h->file_name, h->file_name_len) == 0 && ISSLASH (file_name[h->file_name_len]) - && (base_name (file_name) == file_name + h->file_name_len + 1)) + && (last_component (file_name) == file_name + h->file_name_len + 1)) { do { @@ -898,9 +967,11 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) static int extract_link (char *file_name, int typeflag) { - char const *link_name = safer_name_suffix (current_stat_info.link_name, - true, absolute_names_option); - int interdir_made = 0; + bool interdir_made = false; + char const *link_name; + int rc; + + link_name = current_stat_info.link_name; if (! absolute_names_option && contains_dot_dot (link_name)) return create_placeholder_file (file_name, false, &interdir_made); @@ -939,8 +1010,10 @@ extract_link (char *file_name, int typeflag) errno = e; } - while (maybe_recoverable (file_name, &interdir_made)); + while ((rc = maybe_recoverable (file_name, &interdir_made)) == RECOVER_OK); + if (rc == RECOVER_SKIP) + return 0; if (!(incremental_option && errno == EEXIST)) { link_error (link_name, file_name); @@ -953,23 +1026,29 @@ static int extract_symlink (char *file_name, int typeflag) { #ifdef HAVE_SYMLINK - int status; - int interdir_made = 0; + bool interdir_made = false; if (! absolute_names_option && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name) || contains_dot_dot (current_stat_info.link_name))) return create_placeholder_file (file_name, true, &interdir_made); - while ((status = symlink (current_stat_info.link_name, file_name))) - if (!maybe_recoverable (file_name, &interdir_made)) - break; + while (symlink (current_stat_info.link_name, file_name)) + switch (maybe_recoverable (file_name, &interdir_made)) + { + case RECOVER_OK: + continue; - if (status == 0) - set_stat (file_name, ¤t_stat_info, NULL, 0, 0, SYMTYPE); - else - symlink_error (current_stat_info.link_name, file_name); - return status; + case RECOVER_SKIP: + return 0; + + case RECOVER_NO: + symlink_error (current_stat_info.link_name, file_name); + return -1; + } + + set_stat (file_name, ¤t_stat_info, NULL, 0, 0, SYMTYPE); + return 0; #else static int warned_once; @@ -977,7 +1056,9 @@ extract_symlink (char *file_name, int typeflag) if (!warned_once) { warned_once = 1; - WARN ((0, 0, _("Attempting extraction of symbolic links as hard links"))); + WARNOPT (WARN_SYMBOLIC_CAST, + (0, 0, + _("Attempting extraction of symbolic links as hard links"))); } return extract_link (file_name, typeflag); #endif @@ -987,20 +1068,29 @@ extract_symlink (char *file_name, int typeflag) static int extract_node (char *file_name, int typeflag) { - int status; - int interdir_made = 0; + bool interdir_made = false; + mode_t mode = current_stat_info.stat.st_mode & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; + + while (mknod (file_name, mode ^ invert_permissions, + current_stat_info.stat.st_rdev)) + switch (maybe_recoverable (file_name, &interdir_made)) + { + case RECOVER_OK: + continue; - do - status = mknod (file_name, current_stat_info.stat.st_mode, - current_stat_info.stat.st_rdev); - while (status && maybe_recoverable (file_name, &interdir_made)); + case RECOVER_SKIP: + return 0; - if (status != 0) - mknod_error (file_name); - else - set_stat (file_name, ¤t_stat_info, NULL, 0, - ARCHIVED_PERMSTATUS, typeflag); - return status; + case RECOVER_NO: + mknod_error (file_name); + return -1; + } + + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, + ARCHIVED_PERMSTATUS, typeflag); + return 0; } #endif @@ -1009,29 +1099,38 @@ static int extract_fifo (char *file_name, int typeflag) { int status; - int interdir_made = 0; + bool interdir_made = false; + mode_t mode = current_stat_info.stat.st_mode & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; - while ((status = mkfifo (file_name, current_stat_info.stat.st_mode))) - if (!maybe_recoverable (file_name, &interdir_made)) - break; + while ((status = mkfifo (file_name, mode)) != 0) + switch (maybe_recoverable (file_name, &interdir_made)) + { + case RECOVER_OK: + continue; - if (status == 0) - set_stat (file_name, ¤t_stat_info, NULL, 0, - ARCHIVED_PERMSTATUS, typeflag); - else - mkfifo_error (file_name); - return status; + case RECOVER_SKIP: + return 0; + + case RECOVER_NO: + mkfifo_error (file_name); + return -1; + } + + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, + ARCHIVED_PERMSTATUS, typeflag); + return 0; } #endif static int -extract_mangle_wrapper (char *file_name, int typeflag) +extract_volhdr (char *file_name, int typeflag) { - extract_mangle (); + skip_member (); return 0; } - static int extract_failure (char *file_name, int typeflag) { @@ -1111,13 +1210,7 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) break; case GNUTYPE_VOLHDR: - if (verbose_option) - fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); - *fun = NULL; - break; - - case GNUTYPE_NAMES: - *fun = extract_mangle_wrapper; + *fun = extract_volhdr; break; case GNUTYPE_MULTIVOL: @@ -1134,9 +1227,10 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) break; default: - WARN ((0, 0, - _("%s: Unknown file type `%c', extracted as normal file"), - quotearg_colon (file_name), typeflag)); + WARNOPT (WARN_UNKNOWN_CAST, + (0, 0, + _("%s: Unknown file type `%c', extracted as normal file"), + quotearg_colon (file_name), typeflag)); *fun = extract_file; } @@ -1160,8 +1254,9 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) case KEEP_NEWER_FILES: if (file_newer_p (file_name, ¤t_stat_info)) { - WARN ((0, 0, _("Current %s is newer or same age"), - quote (file_name))); + WARNOPT (WARN_IGNORE_NEWER, + (0, 0, _("Current %s is newer or same age"), + quote (file_name))); return 0; } break; @@ -1178,51 +1273,38 @@ void extract_archive (void) { char typeflag; - char *file_name; tar_extractor_t fun; + fatal_exit_hook = extract_finish; + set_next_block_after (current_header); - decode_header (current_header, ¤t_stat_info, ¤t_format, 1); - if (interactive_option && !confirm ("extract", current_stat_info.file_name)) + if (!current_stat_info.file_name[0] + || (interactive_option + && !confirm ("extract", current_stat_info.file_name))) { skip_member (); return; } /* Print the block from current_header and current_stat. */ - if (verbose_option) - print_header (¤t_stat_info, -1); - - file_name = safer_name_suffix (current_stat_info.file_name, - false, absolute_names_option); - if (strip_name_components) - { - size_t prefix_len = stripped_prefix_len (file_name, - strip_name_components); - if (prefix_len == (size_t) -1) - { - skip_member (); - return; - } - file_name += prefix_len; - } + print_header (¤t_stat_info, current_header, -1); /* Restore stats for all non-ancestor directories, unless it is an incremental archive. (see NOTICE in the comment to delay_set_stat above) */ if (!delay_directory_restore_option) - apply_nonancestor_delayed_set_stat (file_name, 0); + apply_nonancestor_delayed_set_stat (current_stat_info.file_name, 0); /* Take a safety backup of a previously existing file. */ if (backup_option) - if (!maybe_backup_file (file_name, 0)) + if (!maybe_backup_file (current_stat_info.file_name, 0)) { int e = errno; ERROR ((0, e, _("%s: Was unable to backup this file"), - quotearg_colon (file_name))); + quotearg_colon (current_stat_info.file_name))); skip_member (); return; } @@ -1232,9 +1314,10 @@ extract_archive (void) typeflag = sparse_member_p (¤t_stat_info) ? GNUTYPE_SPARSE : current_header->header.typeflag; - if (prepare_to_extract (file_name, typeflag, &fun)) + if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun)) { - if (fun && (*fun) (file_name, typeflag) && backup_option) + if (fun && (*fun) (current_stat_info.file_name, typeflag) + && backup_option) undo_last_backup (); } else @@ -1323,17 +1406,35 @@ extract_finish (void) apply_nonancestor_delayed_set_stat ("", 1); } -void -fatal_exit (void) +bool +rename_directory (char *src, char *dst) { - extract_finish (); - error (TAREXIT_FAILURE, 0, _("Error is not recoverable: exiting now")); - abort (); -} + if (rename (src, dst)) + { + int e = errno; -void -xalloc_die (void) -{ - error (0, 0, "%s", _("memory exhausted")); - fatal_exit (); + switch (e) + { + case ENOENT: + if (make_directories (dst)) + { + if (rename (src, dst) == 0) + return true; + e = errno; + } + break; + + case EXDEV: + /* FIXME: Fall back to recursive copying */ + + default: + break; + } + + ERROR ((0, e, _("Cannot rename %s to %s"), + quote_n (0, src), + quote_n (1, dst))); + return false; + } + return true; }