The attached patch adds a new -q option to cp that allows ignoring file flags
whilst copying with the existing -p, (preserve), option.  Without -p, the new
-q option does nothing.

Rationale:

File flags are not widely used, but we use them on copies of local data that
is not expected to change, (and is archived elsewhere as immutable).

When copying data from such locations, it is often preferable to preserve the
original file ownership and timestamp, but currently using the -p option also
causes the file flags to be set on the new copy.

This is inconvenient if done by a regular user, as the uchg flag has to be
manually removed afterwards.  If done by root, the schg flag can only be
removed by booting into single-user mode.

The following patch avoids this inconvenience by allowing the user invoking cp
to skip copying of flags altogether whilst preserving the other file
attributes.

--- cp.1.dist   Mon Sep  2 18:18:41 2019
+++ cp.1        Thu Dec 22 08:56:17 2022
@@ -128,6 +128,10 @@
 and either the user ID or group ID cannot be preserved, neither
 the set-user-ID nor set-group-ID bits are preserved in the copy's
 permissions.
+.It Fl q
+Modify the behaviour of the
+.Fl p
+option such that file flags are not preserved.
 .It Fl R
 If
 .Ar source
--- cp.c.dist   Fri Jun 28 10:34:58 2019
+++ cp.c        Thu Dec 22 08:47:01 2022
@@ -71,7 +71,7 @@
 PATH_T to = { to.p_path, "" };
 
 uid_t myuid;
-int Rflag, fflag, iflag, pflag, rflag, vflag;
+int Rflag, fflag, iflag, pflag, qflag, rflag, vflag;
 mode_t myumask;
 
 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@@ -88,7 +88,7 @@
        char *target;
 
        Hflag = Lflag = Pflag = Rflag = 0;
-       while ((ch = getopt(argc, argv, "HLPRafiprv")) != -1)
+       while ((ch = getopt(argc, argv, "HLPRafipqrv")) != -1)
                switch (ch) {
                case 'H':
                        Hflag = 1;
@@ -121,6 +121,9 @@
                        break;
                case 'p':
                        pflag = 1;
+                       break;
+               case 'q':
+                       qflag = 1;
                        break;
                case 'r':
                        rflag = 1;
--- extern.h.dist       Sat Dec 26 15:11:43 2015
+++ extern.h    Thu Dec 22 08:47:22 2022
@@ -40,7 +40,7 @@
 
 extern PATH_T to;
 extern uid_t myuid;
-extern int fflag, iflag, pflag;
+extern int fflag, iflag, pflag, qflag;
 extern mode_t myumask;
 extern char *__progname;
 
--- utils.c.dist        Sun Nov 28 16:28:41 2021
+++ utils.c     Thu Dec 22 08:51:25 2022
@@ -314,12 +314,14 @@
         * if the server supports flags and we were trying to *remove* flags
         * on a file that we copied, i.e., that we didn't create.)
         */
-       errno = 0;
-       if (fd >= 0 ? fchflags(fd, fs->st_flags) :
-           chflagsat(AT_FDCWD, to.p_path, fs->st_flags, AT_SYMLINK_NOFOLLOW))
-               if (errno != EOPNOTSUPP || fs->st_flags != 0) {
-                       warn("chflags: %s", to.p_path);
-                       rval = 1;
+       if (qflag == 0) {
+               errno = 0;
+               if (fd >= 0 ? fchflags(fd, fs->st_flags) :
+                   chflagsat(AT_FDCWD, to.p_path, fs->st_flags, 
AT_SYMLINK_NOFOLLOW))
+                       if (errno != EOPNOTSUPP || fs->st_flags != 0) {
+                               warn("chflags: %s", to.p_path);
+                               rval = 1;
+                       }
                }
        return (rval);
 }

Reply via email to