Subject: cp -P and cp -R
To: None <tech-userlevel@netbsd.org>
From: Jan Schaumann <jschauma@netmeister.org>
List: tech-userlevel
Date: 07/03/2006 13:33:58
--dc+cDN39EJAMEtIO
Content-Type: multipart/mixed; boundary="n8g4imXOkfNTN/H1"
Content-Disposition: inline


--n8g4imXOkfNTN/H1
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi,

I've noticed a discrepancy between the cp(1) manual page and it's actual
usage.  The manual page states:

-R	[...] Created directories have the same mode as the corre-
	sponding source directory, unmodified by the process' umask.

This does however not seem to be the case:

(lapdog) umask
077
(lapdog) ls -la dir1
total 7
drwx------   3 jschauma  wheel   512 Jul  3 13:17 .
drwxrwxrwt  30 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     7 Jul  3 13:17 file
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog) cp -R dir1 dir2
(lapdog) ls -la dir2
total 7
drwx------   3 jschauma  wheel   512 Jul  3 13:18 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     7 Jul  3 13:18 file
drwx------   2 jschauma  wheel   512 Jul  3 13:18 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:18 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:18 target
(lapdog)=20

Note that dir2/subdir1 was created with permissions modified by my
umask.  This is because the variable 'dne' is reset to 0 during file
traversal before the directory is visited the second time.



Another thing I noticed is that there currently is no way to copy a file
to a symbolic link (ie overwriting the symbolic link).  One can copy a
symbolic link to a file (ie turning the file into a symbolic link with
the same target) by using '-R', but that is non-intuitive.

Ie:

(lapdog) ls -la
total 7
drwx------   3 jschauma  wheel   512 Jul  3 13:17 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     7 Jul  3 13:17 file
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog) cp symlink file
(lapdog) ls -la
total 7
drwx------   3 jschauma  wheel   512 Jul  3 13:17 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     6 Jul  3 13:22 file
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog) cp -R symlink file
(lapdog) ls -la
total 6
drwx------   3 jschauma  wheel   512 Jul  3 13:22 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
lrwx------   1 jschauma  wheel     6 Jul  3 13:22 file -> target
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog)=20

It seemed to me that using '-P' (no symbolic links are followed) could
also be used when '-R' is _not_ specified to allow a user to copy a file
over a symbolic link or a symbolic link over a file, like so:

(lapdog) ls -la
total 7
drwx------   3 jschauma  wheel   512 Jul  3 13:24 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     6 Jul  3 13:24 file
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog) /usr/src/bin/cp/obj/cp -P symlink file
(lapdog) ls -la
total 6
drwx------   3 jschauma  wheel   512 Jul  3 13:24 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
lrwx------   1 jschauma  wheel     6 Jul  3 13:24 file -> target
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
lrwx------   1 jschauma  wheel     6 Jul  3 13:17 symlink -> target
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog) rm file
(lapdog) echo hello > file
(lapdog) /usr/src/bin/cp/obj/cp -P file symlink
(lapdog) ls -la
total 8
drwx------   3 jschauma  wheel   512 Jul  3 13:27 .
drwxrwxrwt  31 root      wheel  1536 Jul  3 13:18 ..
-rw-------   1 jschauma  wheel     6 Jul  3 13:27 file
drwxrwxrwx   2 jschauma  wheel   512 Jul  3 13:17 subdir1
-rw-------   1 jschauma  wheel     6 Jul  3 13:27 symlink
-rw-------   1 jschauma  wheel     6 Jul  3 13:17 target
(lapdog)=20


The attached patch fixes those problem, but I have to admit I'm not
entirely sure if this goes against old conventions or if this just adds
confusion.  Any thoughts?

-Jan

--=20
In the beginning the Universe was created.  This has made a lot=20
of people very angry and been widely regarded as a bad move.

--n8g4imXOkfNTN/H1
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="cp.diff"
Content-Transfer-Encoding: quoted-printable

Index: cp.1
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/bin/cp/cp.1,v
retrieving revision 1.26
diff -b -u -r1.26 cp.1
--- cp.1	10 Sep 2005 21:51:57 -0000	1.26
+++ cp.1	3 Jul 2006 17:09:19 -0000
@@ -42,18 +42,18 @@
 .Nm
 .Oo
 .Fl R
-.Op Fl H | Fl L | Fl P
+.Op Fl H | Fl L
 .Oc
 .Op Fl f | i
-.Op Fl Npv
+.Op Fl NpPv
 .Ar source_file target_file
 .Nm cp
 .Oo
 .Fl R
-.Op Fl H | Fl L | Fl P
+.Op Fl H | Fl L
 .Oc
 .Op Fl f | i
-.Op Fl Npv
+.Op Fl NpPv
 .Ar source_file ... target_directory
 .Sh DESCRIPTION
 In the first synopsis form, the
@@ -105,9 +105,7 @@
 .Fl p ,
 don't copy file flags.
 .It Fl P
-If the
-.Fl R
-option is specified, no symbolic links are followed.
+No symbolic links are followed.
 .It Fl p
 Causes
 .Nm
@@ -138,7 +136,7 @@
 .Nm
 to create special files rather than copying them as normal files.
 Created directories have the same mode as the corresponding source
-directory, unmodified by the process' umask.
+directory, unmodified by the process's umask.
 .It Fl v
 Cause
 .Nm
@@ -178,16 +176,17 @@
 flag is set, in which case symbolic links are not followed, by default.
 The
 .Fl H
-or
+and
 .Fl L
 flags (in conjunction with the
 .Fl R
-flag) cause symbolic links to be followed as described above.
+flag), as well as the
+.Fl P
+flag cause symbolic links to be followed as described above.
 The
 .Fl H ,
-.Fl L
 and
-.Fl P
+.Fl L
 options are ignored unless the
 .Fl R
 option is specified.
Index: cp.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/bin/cp/cp.c,v
retrieving revision 1.41
diff -b -u -r1.41 cp.c
--- cp.c	17 Mar 2006 06:22:30 -0000	1.41
+++ cp.c	3 Jul 2006 17:09:19 -0000
@@ -85,7 +85,7 @@
 PATH_T to =3D { to.p_path, empty };
=20
 uid_t myuid;
-int Rflag, fflag, iflag, pflag, rflag, vflag, Nflag;
+int Hflag, Lflag, Rflag, Pflag, fflag, iflag, pflag, rflag, vflag, Nflag;
 mode_t myumask;
=20
 enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@@ -99,7 +99,7 @@
 {
 	struct stat to_stat, tmp_stat;
 	enum op type;
-	int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash;
+	int ch, fts_options, r, have_trailing_slash;
 	char *target, **src;
=20
 	(void)setlocale(LC_ALL, "");
@@ -168,6 +168,7 @@
 		fts_options &=3D ~FTS_PHYSICAL;
 		fts_options |=3D FTS_LOGICAL;
 	}
+
 	if (Rflag) {
 		if (Hflag)
 			fts_options |=3D FTS_COMFOLLOW;
@@ -175,7 +176,7 @@
 			fts_options &=3D ~FTS_PHYSICAL;
 			fts_options |=3D FTS_LOGICAL;
 		}
-	} else {
+	} else if (!Pflag) {
 		fts_options &=3D ~FTS_PHYSICAL;
 		fts_options |=3D FTS_LOGICAL | FTS_COMFOLLOW;
 	}
@@ -213,7 +214,10 @@
 	 *
 	 * In (2), the real target is not directory, but "directory/source".
 	 */
+	if (rflag || (Rflag && (Lflag || Hflag)))
 	r =3D stat(to.p_path, &to_stat);
+	else
+		r =3D lstat(to.p_path, &to_stat);
 	if (r =3D=3D -1 && errno !=3D ENOENT) {
 		err(EXIT_FAILURE, "%s", to.p_path);
 		/* NOTREACHED */
@@ -282,10 +286,11 @@
 	struct stat to_stat;
 	FTS *ftsp;
 	FTSENT *curr;
-	int base, dne, rval;
+	int base, dne, rval, sval;
 	size_t nlen;
 	char *p, *target_mid;
=20
+	dne =3D 0;
 	base =3D 0;	/* XXX gcc -Wuninitialized (see comment below) */
=20
 	if ((ftsp =3D fts_open(argv, fts_options, mastercmp)) =3D=3D NULL)
@@ -370,8 +375,10 @@
 			STRIP_TRAILING_SLASH(to);
 		}
=20
+		sval =3D (rflag || (Rflag && (Lflag || Hflag))) ?
+				stat(to.p_path, &to_stat) : lstat(to.p_path, &to_stat);
 		/* Not an error but need to remember it happened */
-		if (stat(to.p_path, &to_stat) =3D=3D -1)
+		if (sval =3D=3D -1)
 			dne =3D 1;
 		else {
 			if (to_stat.st_dev =3D=3D curr->fts_statp->st_dev &&
@@ -390,6 +397,7 @@
 				rval =3D 1;
 				continue;
 			}
+			if (!S_ISDIR(curr->fts_statp->st_mode))
 			dne =3D 0;
 		}
=20
Index: extern.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/bin/cp/extern.h,v
retrieving revision 1.12
diff -b -u -r1.12 extern.h
--- extern.h	15 Oct 2005 18:22:18 -0000	1.12
+++ extern.h	3 Jul 2006 17:09:19 -0000
@@ -42,7 +42,7 @@
=20
 extern PATH_T to;
 extern uid_t myuid;
-extern int fflag, iflag, pflag, Nflag;
+extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, pflag, Nflag;
 extern mode_t myumask;
=20
 #include <sys/cdefs.h>
Index: utils.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/src/bin/cp/utils.c,v
retrieving revision 1.29
diff -b -u -r1.29 utils.c
--- utils.c	15 Oct 2005 18:22:18 -0000	1.29
+++ utils.c	3 Jul 2006 17:09:19 -0000
@@ -74,7 +74,7 @@
 {
 	static char buf[MAXBSIZE];
 	struct stat to_stat, *fs;
-	int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
+	int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount;
 	char *p;
 =09
 	if ((from_fd =3D open(entp->fts_path, O_RDONLY, 0)) =3D=3D -1) {
@@ -82,7 +82,9 @@
 		return (1);
 	}
=20
+	to_fd =3D -1;
 	fs =3D entp->fts_statp;
+	tolnk =3D ((Rflag && !(Lflag || Hflag)) || Pflag);
=20
 	/*
 	 * If the file exists and we're interactive, verify with the user.
@@ -93,6 +95,9 @@
 	 * modified by the umask.)
 	 */
 	if (!dne) {
+		struct stat sb;
+		int sval;
+
 		if (iflag) {
 			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
 			checkch =3D ch =3D getchar();
@@ -103,13 +108,21 @@
 				return (0);
 			}
 		}
-		/* overwrite existing destination file name */
+
+		sval =3D tolnk ?
+			lstat(to.p_path, &sb) : stat(to.p_path, &sb);
+		if (sval =3D=3D -1) {
+			warn("stat: %s", to.p_path);
+			return (1);
+		}
+
+		if (!(tolnk && S_ISLNK(sb.st_mode)))
 		to_fd =3D open(to.p_path, O_WRONLY | O_TRUNC, 0);
 	} else
 		to_fd =3D open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
 		    fs->st_mode & ~(S_ISUID | S_ISGID));
=20
-	if (to_fd =3D=3D -1 && fflag) {
+	if (to_fd =3D=3D -1 && (fflag || tolnk)) {
 		/*
 		 * attempt to remove existing destination file name and
 		 * create a new file

--n8g4imXOkfNTN/H1--

--dc+cDN39EJAMEtIO
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (NetBSD)

iD8DBQFEqVUGfFtkr68iakwRAsztAJ9Yw3pFppnhc0srMgnoT68ogiLPjgCgu4iC
SRIYle7V9q7L8Tzpeb93va8=
=5UzJ
-----END PGP SIGNATURE-----

--dc+cDN39EJAMEtIO--