/* 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
{
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;
/* 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.
}
/* 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)
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)
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;
}
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)))
{
{
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,
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
size_t count;
size_t written;
int interdir_made = 0;
+ 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. */
else
{
do
- fd = open_output_file (file_name, typeflag);
+ fd = open_output_file (file_name, typeflag, mode ^ invert_permissions);
while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
if (fd < 0)
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);
{
int status;
int interdir_made = 0;
+ 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;
do
- status = mknod (file_name, current_stat_info.stat.st_mode,
+ status = mknod (file_name, mode ^ invert_permissions,
current_stat_info.stat.st_rdev);
while (status && maybe_recoverable (file_name, &interdir_made));
if (status != 0)
mknod_error (file_name);
else
- set_stat (file_name, ¤t_stat_info, NULL, 0,
+ set_stat (file_name, ¤t_stat_info, NULL, invert_permissions,
ARCHIVED_PERMSTATUS, typeflag);
return status;
}
{
int status;
int interdir_made = 0;
+ 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)))
+ while ((status = mkfifo (file_name, mode)) != 0)
if (!maybe_recoverable (file_name, &interdir_made))
break;
if (status == 0)
- set_stat (file_name, ¤t_stat_info, NULL, 0,
+ set_stat (file_name, ¤t_stat_info, NULL, invert_permissions,
ARCHIVED_PERMSTATUS, typeflag);
else
mkfifo_error (file_name);