> Hi,

Hello Christopher,

> I admit this is a very special case, but anyway this is what I hit:
> 
> $ touch blub; ln -s blub blab; ls -l; cp -f blub blab
> total 0
> lrwxr-xr-x  1 madroach  wheel  4 Oct 30 17:39 blab -> blub
> -rw-r--r--  1 madroach  wheel  0 Oct 30 17:39 blub
> cp: blab and blub are identical (not copied).

This is expected behaviour, blab and blub are indeed the same file and
so copy should be skipped.

From POSIX:

“If source_file references the same file as dest_file, cp may write a
diagnostic message to standard error; it shall do nothing more with
source_file and shall go on to any remaining files.”

> Now I had a look at our cp.c, our cp(1) and at POSIX:
> 
>  -f   For each existing destination pathname, remove it and
> create a new file, without prompting for confirmation, regardless of
> its permissions.  The -f option overrides any previous -i options.
> 
> POSIX says cp must always first try to open(2)(O_TRUNC) the
> destination and only if this fails AND -f was specified should it try
> unlink(2).
> 
> At least regarding this special case our cp behaves according to
> POSIX, not according to our manpage.
> 
> As long as we cannot change the POSIX standard I don't know whether
> our manpage should mention this or cp.c be changed to use lstat(),
> too. Maybe this is such a special case we could just ignore it...

If you're interested in adapting to POSIX, here's a proposition patch:

Index: bin/cp//cp.1
===================================================================
RCS file: /var/cvs/src/bin/cp/cp.1,v
retrieving revision 1.40
diff -u -r1.40 cp.1
--- bin/cp//cp.1        28 Jan 2019 18:58:42 -0000      1.40
+++ bin/cp//cp.1        1 Nov 2019 19:41:26 -0000
@@ -79,9 +79,8 @@
 Same as
 .Fl RpP .
 .It Fl f
-For each existing destination pathname, remove it and
-create a new file, without prompting for confirmation,
-regardless of its permissions.
+For each existing destination pathname which cannot be opened, remove
+it and create a new file, without prompting for confirmation.
 The
 .Fl f
 option overrides any previous
Index: bin/cp//utils.c
===================================================================
RCS file: /var/cvs/src/bin/cp/utils.c,v
retrieving revision 1.47
diff -u -r1.47 utils.c
--- bin/cp//utils.c     28 Jan 2019 18:58:42 -0000      1.47
+++ bin/cp//utils.c     1 Nov 2019 19:25:02 -0000
@@ -55,7 +55,7 @@
        static char *buf;
        static char *zeroes;
        struct stat to_stat, *fs;
-       int from_fd, rcount, rval, to_fd, wcount;
+       int from_fd, rcount, rval, to_fd, wcount, create = 1;
 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
        char *p;
 #endif
@@ -79,26 +79,24 @@
        fs = entp->fts_statp;
 
        /*
-        * In -f (force) mode, we always unlink the destination first
-        * if it exists.  Note that -i and -f are mutually exclusive.
-        */
-       if (exists && fflag)
-               (void)unlink(to.p_path);
-
-       /*
         * If the file DNE, set the mode to be the from file, minus setuid
         * bits, modified by the umask; arguably wrong, but it makes copying
         * executables work right and it's been that way forever.  (The
         * other choice is 666 or'ed with the execute bits on the from file
         * modified by the umask.)
         */
-       if (exists && !fflag) {
-               if (!copy_overwrite()) {
+       if (exists) {
+               /* Note that -i and -f are mutually exclusive. */
+               if (!fflag && !copy_overwrite()) {
                        (void)close(from_fd);
                        return 2;
                }
                to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
-       } else
+               if (to_fd != -1 || fflag && unlink(to.p_path) == -1)
+                       create = 0;
+       }
+
+       if (create)
                to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
                    fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID));
 

Reply via email to