From cbc06bb6390c1ad4a227723078ecd7f7689251cc Mon Sep 17 00:00:00 2001 From: Lasse Collin Date: Sat, 10 Nov 2007 01:07:37 +0200 Subject: "cp -p" tries to preserve GID even if preserving the UID fails. * NEWS: Mention this new feature. * src/copy.c (set_owner): Try to preserve just the GID, when initial fchown/lchown fails. * src/cp.c (re_protect): Likewise. --- ChangeLog | 8 ++++++++ NEWS | 3 +++ src/copy.c | 19 ++++++++++++++++++- src/cp.c | 19 ++++++++++++------- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index b34ad8da1..3caaaa36f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-11-24 Jim Meyering + + "cp -p" tries to preserve GID even if preserving the UID fails. + * NEWS: Mention this new feature. + * src/copy.c (set_owner): Try to preserve just the GID, + when initial fchown/lchown fails. + * src/cp.c (re_protect): Likewise. + 2007-11-23 Jim Meyering * src/runcon.c (main): Remove unused parameter, "envp". diff --git a/NEWS b/NEWS index 14fb3cd8b..03e16a5d1 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,9 @@ GNU coreutils NEWS -*- outline -*- Add SELinux support (FIXME: add details here) + cp -p tries to preserve the GID of a file even if preserving the UID + is not possible. + uniq accepts a new option: --zero-terminated (-z). As with the sort option of the same name, this makes uniq consume and produce NUL-terminated lines rather than newline-terminated lines. diff --git a/src/copy.c b/src/copy.c index 280fbf8d9..4dec5166e 100644 --- a/src/copy.c +++ b/src/copy.c @@ -172,7 +172,8 @@ copy_dir (char const *src_name_in, char const *dst_name_in, bool new_dst, safety prefer lchown if the system supports it since no symbolic links should be involved. DEST_DESC must refer to the same file as DEST_NAME if defined. - Return 1 if the syscall succeeds, 0 if it fails but it's OK + Upon failure to set both UID and GID, try to set only the GID. + Return 1 if the initial syscall succeeds, 0 if it fails but it's OK not to preserve ownership, -1 otherwise. */ static int @@ -183,11 +184,27 @@ set_owner (const struct cp_options *x, char const *dst_name, int dest_desc, { if (fchown (dest_desc, uid, gid) == 0) return 1; + if (errno == EPERM || errno == EINVAL) + { + /* We've failed to set *both*. Now, try to set just the group + ID, but ignore any failure here, and don't change errno. */ + int saved_errno = errno; + (void) fchown (dest_desc, -1, gid); + errno = saved_errno; + } } else { if (lchown (dst_name, uid, gid) == 0) return 1; + if (errno == EPERM || errno == EINVAL) + { + /* We've failed to set *both*. Now, try to set just the group + ID, but ignore any failure here, and don't change errno. */ + int saved_errno = errno; + (void) lchown (dst_name, -1, gid); + errno = saved_errno; + } } if (! chown_failure_ok (x)) diff --git a/src/cp.c b/src/cp.c index 5859f8c1d..599498dd7 100644 --- a/src/cp.c +++ b/src/cp.c @@ -316,13 +316,18 @@ re_protect (char const *const_dst_name, size_t src_offset, if (x->preserve_ownership) { - if (lchown (dst_name, p->st.st_uid, p->st.st_gid) != 0 - && ! chown_failure_ok (x)) - { - error (0, errno, _("failed to preserve ownership for %s"), - quote (dst_name)); - return false; - } + if (lchown (dst_name, p->st.st_uid, p->st.st_gid) != 0) + { + if (! chown_failure_ok (x)) + { + error (0, errno, _("failed to preserve ownership for %s"), + quote (dst_name)); + return false; + } + /* Failing to preserve ownership is OK. Still, try to preserve + the group, but ignore the possible error. */ + (void) lchown (dst_name, -1, p->st.st_gid); + } } if (x->preserve_mode) -- cgit v1.2.3-70-g09d2