diff options
Diffstat (limited to 'src/copy.c')
| -rw-r--r-- | src/copy.c | 65 |
1 files changed, 33 insertions, 32 deletions
diff --git a/src/copy.c b/src/copy.c index 178a6404b..0a8b0e41d 100644 --- a/src/copy.c +++ b/src/copy.c @@ -123,17 +123,12 @@ static char const *top_level_dst_name; static inline int utimens_symlink (char const *file, struct timespec const *timespec) { - int err = 0; - -#if HAVE_UTIMENSAT - err = utimensat (AT_FDCWD, file, timespec, AT_SYMLINK_NOFOLLOW); + int err = lutimens (file, timespec); /* When configuring on a system with new headers and libraries, and running on one with a kernel that is old enough to lack the syscall, utimensat fails with ENOSYS. Ignore that. */ if (err && errno == ENOSYS) err = 0; -#endif - return err; } @@ -360,11 +355,11 @@ set_owner (const struct cp_options *x, char const *dst_name, int dest_desc, group. Avoid the window by first changing to a restrictive temporary mode if necessary. */ - if (!new_dst && (x->preserve_mode | x->move_mode | x->set_mode)) + if (!new_dst && (x->preserve_mode || x->move_mode || x->set_mode)) { mode_t old_mode = dst_sb->st_mode; mode_t new_mode = - (x->preserve_mode | x->move_mode ? src_sb->st_mode : x->mode); + (x->preserve_mode || x->move_mode ? src_sb->st_mode : x->mode); mode_t restrictive_temp_mode = old_mode & new_mode & S_IRWXU; if ((USE_ACL @@ -834,6 +829,24 @@ copy_reg (char const *src_name, char const *dst_name, } } + /* To allow copying xattrs on read-only files, temporarily chmod u+rw. + This workaround is required as an inode permission check is done + by xattr_permission() in fs/xattr.c of the GNU/Linux kernel tree. */ + if (x->preserve_xattr) + { + bool access_changed = false; + + if (!(sb.st_mode & S_IWUSR) && geteuid() != 0) + access_changed = fchmod_or_lchmod (dest_desc, dst_name, 0600) == 0; + + if (!copy_attr_by_fd (src_name, source_desc, dst_name, dest_desc, x) + && x->require_preserve_xattr) + return_val = false; + + if (access_changed) + fchmod_or_lchmod (dest_desc, dst_name, dst_mode & ~omitted_permissions); + } + if (x->preserve_ownership && ! SAME_OWNER_AND_GROUP (*src_sb, sb)) { switch (set_owner (x, dst_name, dest_desc, src_sb, *new_dst, &sb)) @@ -850,11 +863,6 @@ copy_reg (char const *src_name, char const *dst_name, set_author (dst_name, dest_desc, src_sb); - if (x->preserve_xattr && ! copy_attr_by_fd (src_name, source_desc, - dst_name, dest_desc, x) - && x->require_preserve_xattr) - return_val = false; - if (x->preserve_mode || x->move_mode) { if (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) != 0 @@ -1671,7 +1679,9 @@ copy_internal (char const *src_name, char const *dst_name, } else { - bool link_failed = (link (earlier_file, dst_name) != 0); + /* We want to guarantee that symlinks are not followed. */ + bool link_failed = (linkat (AT_FDCWD, earlier_file, AT_FDCWD, + dst_name, 0) != 0); /* If the link failed because of an existing destination, remove that file and then call link again. */ @@ -1684,7 +1694,8 @@ copy_internal (char const *src_name, char const *dst_name, } if (x->verbose) printf (_("removed %s\n"), quote (dst_name)); - link_failed = (link (earlier_file, dst_name) != 0); + link_failed = (linkat (AT_FDCWD, earlier_file, AT_FDCWD, + dst_name, 0) != 0); } if (link_failed) @@ -1974,25 +1985,15 @@ copy_internal (char const *src_name, char const *dst_name, } } - /* POSIX 2008 states that it is implementation-defined whether - link() on a symlink creates a hard-link to the symlink, or only - to the referent (effectively dereferencing the symlink) (POSIX - 2001 required the latter behavior, although many systems provided - the former). Yet cp, invoked with `--link --no-dereference', - should not follow the link. We can approximate the desired - behavior by skipping this hard-link creating block and instead - copying the symlink, via the `S_ISLNK'- copying code below. - LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know - how link() behaves, so we use the fallback case for safety. - - FIXME - use a gnulib linkat emulation for more fine-tuned - emulation, particularly when LINK_FOLLOWS_SYMLINKS is -1. */ + /* cp, invoked with `--link --no-dereference', should not follow the + link; we guarantee this with gnulib's linkat module (on systems + where link(2) follows the link, gnulib creates a symlink with + identical contents, which is good enough for our purposes). */ else if (x->hard_link - && (!LINK_FOLLOWS_SYMLINKS - || !S_ISLNK (src_mode) - || x->dereference != DEREF_NEVER)) + && (!S_ISLNK (src_mode) + || x->dereference != DEREF_NEVER)) { - if (link (src_name, dst_name)) + if (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, 0)) { error (0, errno, _("cannot create link %s"), quote (dst_name)); goto un_backup; |
