summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--NEWS3
-rw-r--r--src/copy.c19
-rw-r--r--src/cp.c19
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 <meyering@redhat.com>
+
+ "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 <meyering@redhat.com>
* 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)