Subject: [PATCH] cp: preserve "capabilities" when also preserving file ownership

From: Pádraig Brady <[email-masked]>
Date: Fri, 16 Apr 2010 09:41:00 +0100

`sudo cp -a non-root-file copy` would not preserve capabilities.
The attached fixes this and passes all tests.

cheers,
Pádraig.
>From 01fb46436854d197f3b3f0bbd196631ba088c73c Mon Sep 17 00:00:00 2001
From: =?utf-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Fri, 16 Apr 2010 08:39:11 +0100
Subject: [PATCH] cp: preserve "capabilities" when also preserving file ownership

* src/copy.c (copy_reg): Copy xattrs _after_ setting file ownership
so that capabilities are not cleared when setting ownership.
* tests/cp/capability: A new root test.
* tests/Makefile.am (root_tests): Reference the new test.
* NEWS: Mention the fix.
---
 NEWS                |    2 +
 src/copy.c          |   30 ++++++++++++++------------
 tests/Makefile.am   |    1 +
 tests/cp/capability |   56 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 75 insertions(+), 14 deletions(-)
 create mode 100755 tests/cp/capability

diff --git a/NEWS b/NEWS
index 2be9633..8714d1e 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  cp now preserves "capabilities" when also preserving file ownership.
+
   ls --color once again honors the 'NORMAL' dircolors directive.
   [bug introduced in coreutils-6.11]
 
diff --git a/src/copy.c b/src/copy.c
index 0fa148e..4e70c21 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -826,6 +826,22 @@ copy_reg (char const *src_name, char const *dst_name,
         }
     }
 
+  /* We set ownership before xattrs as changing owners will
+     clear capabilities.  */
+  if (x->preserve_ownership && ! SAME_OWNER_AND_GROUP (*src_sb, sb))
+    {
+      switch (set_owner (x, dst_name, dest_desc, src_sb, *new_dst, &sb))
+        {
+        case -1:
+          return_val = false;
+          goto close_src_and_dst_desc;
+
+        case 0:
+          src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
+          break;
+        }
+    }
+
   /* 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.  */
@@ -844,20 +860,6 @@ copy_reg (char const *src_name, char const *dst_name,
         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))
-        {
-        case -1:
-          return_val = false;
-          goto close_src_and_dst_desc;
-
-        case 0:
-          src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
-          break;
-        }
-    }
-
   set_author (dst_name, dest_desc, src_sb);
 
   if (x->preserve_mode || x->move_mode)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index db1610d..a943ff3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,6 +23,7 @@ root_tests =					\
   cp/preserve-gid				\
   cp/special-bits				\
   cp/cp-mv-enotsup-xattr			\
+  cp/capability					\
   dd/skip-seek-past-dev				\
   install/install-C-root			\
   ls/capability					\
diff --git a/tests/cp/capability b/tests/cp/capability
new file mode 100755
index 0000000..d575dbc
--- /dev/null
+++ b/tests/cp/capability
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Ensure cp --preserves copies capabilities
+
+# Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  ls --version
+fi
+
+. $srcdir/test-lib.sh
+require_root_
+
+grep '^#define HAVE_CAP 1' $CONFIG_HEADER > /dev/null \
+  || skip_test_ "configured without libcap support"
+
+(setcap --help) 2>&1 |grep 'usage: setcap' > /dev/null \
+  || skip_test_ "setcap utility not found"
+(getcap --help) 2>&1 |grep 'usage: getcap' > /dev/null \
+  || skip_test_ "getcap utility not found"
+
+# Don't let a different umask perturb the results.
+umask 22
+
+touch file || framework_failure
+chown $NON_ROOT_USERNAME file || framework_failure
+
+setcap 'cap_net_bind_service=ep' file ||
+  skip_test_ "setcap doesn't work"
+getcap file | grep cap_net_bind_service >/dev/null ||
+  skip_test_ "getcap doesn't work"
+
+cp --preserve=xattr file copy1 || fail=1
+
+# Before coreutils 8.5 the capabilities would not be preserved,
+# as the owner was set _after_ copying xattrs, thus clearing any capabilities.
+cp --preserve=all   file copy2 || fail=1
+
+for file in copy1 copy2; do
+  getcap $file | grep cap_net_bind_service >/dev/null || fail=1
+done
+
+Exit $fail
-- 
1.6.2.5