Subject: bug#30534: cp - Possible bugs when not preserving mode (explicit) and when copying special files

From: Pádraig Brady <[email-masked]>
Date: Mon, 19 Feb 2018 19:26:46 -0800

On 19/02/18 02:22, Declercq Laurent wrote:
> I think that I did found at least two bugs in cp(1) command when the 
> --no-preserve=mode option is involved and when copying special file. I 
> describe each of them below.
> 
> 1. Mode set on special files seem to be wrong:
> 
> Original file to copy: prw-rw-rw- 1 root staff 0 févr. 18 18:59 spfile
> cp(1) command (run as root user): cp -r --no-preserve=mode spfile 
> spfile_copy
> 
> Current result:
> 
> prwxr-xr-x 1 root staff 0 févr. 18 22:01 spfile_copy
> 
> Expected result (considering UMASK 0022):
> 
> prw-r--r-- 1 root staff 0 févr. 18 22:01 spfile_copy
> 
> The current behavior is due to the fact that mode used is 0777 while 
> 0666 should be used for files.
> 
> Possible fix: Differentiate directories from files in the copy_internal 
> function.

Thanks for the clear details.
The attached should fix this up.

> 2. Non-permission bits are preserved, even when the --no-preserve=mode 
> option is involved.
> 
> Original file to copy: prwSrw-rw- 1 root staff 0 févr. 18 18:59 spfile
> cp(1) command (run as root user): cp -r --no-preserve=mode spfile 
> spfile_copy
> 
> Current result:
> 
> prwsr-xr-x 1 root staff 0 févr. 18 22:05 spfile_copy

I'm not seeing this. I get 'x' rather than 's' here (and '-' with the fix)

> Expected result (considering UMASK 0022 and without the first bug above):
> 
> prw-r--r-- 1 root staff 0 févr. 18 22:05 spfile_copy

thanks!
Pádraig

From 71e05111fa9dd6a4ad29630752d95289cb6d0274 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Mon, 19 Feb 2018 19:10:14 -0800
Subject: [PATCH] cp: set appropriate default permissions for special files

This issue was introduced in commit v8.19-145-g24ebca6

* src/copy.c (copy_internal): When setting default permissions
to use with --no-preserve=mode, only set executable bits for
directories or sockets.
* NEWS: Mention the fix.
* tests/cp/preserve-mode.sh: Add a test case.
Fixes https://bugs.gnu.org/30534
---
 NEWS                      |  5 +++++
 src/copy.c                |  6 ++++--
 tests/cp/preserve-mode.sh | 10 +++++++++-
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index 8a9e09e..5fa6928 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   that caused -u to sometimes override -n.
   [bug introduced with coreutils-7.1]
 
+  'cp -a --no-preserve=mode' now sets appropriate default permissions
+  for non regular files like fifos and character device nodes etc.
+  Previously it would have set executable bits on created special files.
+  [bug introduced with coreutils-8.20]
+
 
 * Noteworthy changes in release 8.29 (2017-12-27) [stable]
 
diff --git a/src/copy.c b/src/copy.c
index e050d41..233b498 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1378,7 +1378,7 @@ preserve_metadata:
     }
   else if (x->explicit_no_preserve_mode)
     {
-      if (set_acl (dst_name, dest_desc, 0666 & ~cached_umask ()) != 0)
+      if (set_acl (dst_name, dest_desc, MODE_RW_UGO & ~cached_umask ()) != 0)
         return_val = false;
     }
   else if (omitted_permissions)
@@ -2860,7 +2860,9 @@ copy_internal (char const *src_name, char const *dst_name,
     }
   else if (x->explicit_no_preserve_mode)
     {
-      if (set_acl (dst_name, -1, 0777 & ~cached_umask ()) != 0)
+      int default_permissions = S_ISDIR (src_mode) || S_ISSOCK (src_mode)
+                                ? S_IRWXUGO : MODE_RW_UGO;
+      if (set_acl (dst_name, -1, default_permissions & ~cached_umask ()) != 0)
         return false;
     }
   else
diff --git a/tests/cp/preserve-mode.sh b/tests/cp/preserve-mode.sh
index 1cd173a..3b0aca8 100755
--- a/tests/cp/preserve-mode.sh
+++ b/tests/cp/preserve-mode.sh
@@ -19,7 +19,7 @@
 . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
 print_ver_ cp
 
-get_mode() { ls -ld "$1" | cut -b-10; }
+get_mode() { stat -c%f "$1"; }
 
 rm -f a b c
 umask 0022
@@ -47,4 +47,12 @@ chmod 600 a
 cp --no-preserve=mode --preserve=all a b || fail=1
 test "$(get_mode a)" = "$(get_mode b)" || fail=1
 
+#fifo test
+if mkfifo fifo; then
+  cp -a --no-preserve=mode fifo fifo_copy || fail=1
+  #ensure default perms set appropriate for non regular files
+  #which wasn't done between v8.20 and 8.29 inclusive
+  test "$(get_mode fifo)" = "$(get_mode fifo_copy)" || fail=1
+fi
+
 Exit $fail
-- 
2.9.3