+/* If restoring permissions, restore the mode for FILE_NAME from
+ information given in *STAT_INFO (where *CUR_INFO gives
+ the current status if CUR_INFO is nonzero); otherwise invert the
+ INVERT_PERMISSIONS bits from the file's current permissions.
+ PERMSTATUS specifies the status of the file's permissions.
+ TYPEFLAG specifies the type of the file. */
+static void
+set_mode (char const *file_name,
+ struct stat const *stat_info,
+ struct stat const *cur_info,
+ mode_t invert_permissions, enum permstatus permstatus,
+ char typeflag)
+{
+ mode_t mode;
+
+ 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
+ directories inherit the setgid bits from their parents, so we
+ we must set directories' modes explicitly. */
+ if (permstatus == ARCHIVED_PERMSTATUS
+ && ! (mode & ~ MODE_RWX)
+ && typeflag != DIRTYPE
+ && typeflag != GNUTYPE_DUMPDIR)
+ return;
+ }
+ else if (! invert_permissions)
+ return;
+ else
+ {
+ /* We must inspect a directory's current permissions, since the
+ directory may have inherited its setgid bit from its parent.
+
+ INVERT_PERMISSIONS happens to be nonzero only for directories
+ that we created, so there's no point optimizing this code for
+ other cases. */
+ struct stat st;
+ if (! cur_info)
+ {
+ if (stat (file_name, &st) != 0)
+ {
+ stat_error (file_name);
+ return;
+ }
+ cur_info = &st;
+ }
+ mode = cur_info->st_mode ^ invert_permissions;
+ }
+
+ if (chmod (file_name, mode) != 0)
+ chmod_error_details (file_name, mode);
+}
+
+/* Check time after successfully setting FILE_NAME's time stamp to T. */
+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)
+ {
+ struct timespec now;
+ gettime (&now);
+ if (timespec_cmp (now, t) < 0)
+ {
+ char buf[TIMESPEC_STRSIZE_BOUND];
+ struct timespec diff;
+ diff.tv_sec = t.tv_sec - now.tv_sec;
+ diff.tv_nsec = t.tv_nsec - now.tv_nsec;
+ if (diff.tv_nsec < 0)
+ {
+ 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)));
+ }
+ }
+}
+
+/* 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.
+ If not restoring permissions, invert the
+ INVERT_PERMISSIONS bits from the file's current permissions.
+ PERMSTATUS specifies the status of the file's permissions.
+ TYPEFLAG specifies the type of the file. */
+
+/* FIXME: About proper restoration of symbolic link attributes, we still do
+ not have it right. Pretesters' reports tell us we need further study and
+ probably more configuration. For now, just use lchown if it exists, and
+ punt for the rest. Sigh! */
+
+static void
+set_stat (char const *file_name,
+ struct tar_stat_info const *st,
+ struct stat const *cur_info,
+ mode_t invert_permissions, enum permstatus permstatus,
+ char typeflag)
+{
+ if (typeflag != SYMTYPE)
+ {
+ /* We do the utime before the chmod because some versions of utime are
+ broken and trash the modes of the file. */
+
+ if (! touch_option && permstatus != INTERDIR_PERMSTATUS)
+ {
+ /* We set the accessed time to `now', which is really the time we
+ started extracting files, unless incremental_option is used, in
+ which case .st_atime is used. */
+
+ /* FIXME: incremental_option should set ctime too, but how? */
+
+ struct timespec ts[2];
+ if (incremental_option)
+ ts[0] = st->atime;
+ else
+ ts[0] = start_time;
+ ts[1] = st->mtime;
+
+ if (utimens (file_name, ts) != 0)
+ utime_error (file_name);
+ else
+ {
+ check_time (file_name, ts[0]);
+ check_time (file_name, ts[1]);
+ }
+ }