switch (e)
{
case ELOOP:
+ /* With open ("symlink", O_NOFOLLOW|...), POSIX says errno == ELOOP,
+ but FreeBSD through at least 8.1 uses errno == EMLINK. */
+ case EMLINK:
if (! regular
|| old_files_option != OVERWRITE_OLD_FILES || dereference_option)
break;
}
}
+ /* If O_NOFOLLOW is needed but does not work, check for a symlink
+ separately. There's a race condition, but that cannot be avoided
+ on hosts lacking O_NOFOLLOW. */
+ if (! O_NOFOLLOW && overwriting_old_files && ! dereference_option)
+ {
+ struct stat st;
+ if (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0
+ && S_ISLNK (st.st_mode))
+ {
+ errno = ELOOP;
+ return -1;
+ }
+ }
+
fd = openat (chdir_fd, file_name, openflag, mode);
if (0 <= fd)
{
extract_node (char *file_name, int typeflag)
{
bool interdir_made = false;
- mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
+ mode_t mode = (current_stat_info.stat.st_mode & (MODE_RWX | S_IFBLK | S_IFCHR)
& ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
while (mknodat (chdir_fd, file_name, mode, current_stat_info.stat.st_rdev)