Bug 25017

Summary: cp -pRP does not preserve symlink ownership
Product: Base System Reporter: mkamm
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-STABLE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description mkamm 2001-02-11 22:50:02 UTC
I quote "man cp":

     -p    Cause cp to preserve in the copy as many of the modification time,
           access time, file flags, file mode, user ID, and group ID as al-
           lowed by permissions.

However no attempt is made by cp(1) to preserve ownership of copied symlinks.

How-To-Repeat: 
as root try:

# ln -s /COPYRIGHT copyright
# chown -h uucp:bin copyright
# ls -l copyright
lrwxr-xr-x  1 uucp  bin  10 11 Feb 01:57 copyright -> /COPYRIGHT
# cp -pR copyright copyright2
# ls -l copyright2
lrwxr-xr-x  1 root  mkamm  10 11 Feb 01:57 copyright2 -> /COPYRIGHT
Comment 1 Bruce Evans 2001-02-12 06:00:13 UTC
On Sun, 11 Feb 2001 mkamm@gmx.net wrote:

> >Description:
> 
> I quote "man cp":
> 
>      -p    Cause cp to preserve in the copy as many of the modification time,
>            access time, file flags, file mode, user ID, and group ID as al-
>            lowed by permissions.
> 
> However no attempt is made by cp(1) to preserve ownership of copied symlinks.

> >Fix:
> 
> Index: utils.c
> ===================================================================
> RCS file: /home/ncvs/src/bin/cp/utils.c,v
> retrieving revision 1.28
> diff -u -r1.28 utils.c
> --- utils.c	2000/10/10 01:48:18	1.28
> +++ utils.c	2001/02/11 00:52:13
> @@ -224,7 +224,7 @@
>  		warn("symlink: %s", link);
>  		return (1);
>  	}
> -	return (0);
> +	return (pflag ? setlink(p->fts_statp) : 0);
>  }

This should use a slightly modified version of setfile() (replace
`chown(...)' by `(S_ISLINK(...) ? chown : lchown)(...)', etc.  This
will also fix the non-preservation of modes and times for symlinks.

Bruce
Comment 2 Martin Kammerhofer 2001-02-15 13:25:04 UTC
Am 12.02.01 hat Bruce Evans folgendes geschrieben:

: 
: This should use a slightly modified version of setfile() (replace
: `chown(...)' by `(S_ISLINK(...) ? chown : lchown)(...)', etc.  This
: will also fix the non-preservation of modes and times for symlinks.
: 
: Bruce
: 

Below is a revised patch following this proposition:

Index: utils.c
===================================================================
RCS file: /home/ncvs/src/bin/cp/utils.c,v
retrieving revision 1.28
diff -u -r1.28 utils.c
--- utils.c	2000/10/10 01:48:18	1.28
+++ utils.c	2001/02/15 14:15:03
@@ -224,7 +224,7 @@
 		warn("symlink: %s", link);
 		return (1);
 	}
-	return (0);
+	return (pflag ? setfile(p->fts_statp, 0) : 0);
 }
 
 int
@@ -267,20 +267,21 @@
 {
 	static struct timeval tv[2];
 	struct stat ts;
-	int rval;
-	int gotstat;
+	int rval, gotstat, islink;
 
 	rval = 0;
+	islink = !fd && S_ISLNK(fs->st_mode);
 	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
 		       S_IRWXU | S_IRWXG | S_IRWXO;
 
 	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
 	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
-	if (utimes(to.p_path, tv)) {
+	if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
 		warn("utimes: %s", to.p_path);
 		rval = 1;
 	}
-	if (fd ? fstat(fd, &ts) : stat(to.p_path, &ts))
+	if (fd ? fstat(fd, &ts) :
+	    (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
 		gotstat = 0;
 	else {
 		gotstat = 1;
@@ -295,7 +296,8 @@
 	 */
 	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
 		if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
-		    chown(to.p_path, fs->st_uid, fs->st_gid)) {
+		    (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
+			      chown(to.p_path, fs->st_uid, fs->st_gid))) {
 			if (errno != EPERM) {
 				warn("chown: %s", to.p_path);
 				rval = 1;
@@ -304,14 +306,17 @@
 		}
 
 	if (!gotstat || fs->st_mode != ts.st_mode)
-		if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
-			warn("chown: %s", to.p_path);
+		if (fd ? fchmod(fd, fs->st_mode) :
+			(islink ? lchmod(to.p_path, fs->st_mode)
+				: chmod(to.p_path, fs->st_mode))) {
+			warn("chmod: %s", to.p_path);
 			rval = 1;
 		}
 
 	if (!gotstat || fs->st_flags != ts.st_flags)
-		if (fd ?
-		    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
+		if (fd ? fchflags(fd, fs->st_flags) :
+		    (islink ? (errno = ENOSYS)
+		            : chflags(to.p_path, fs->st_flags))) {
 			warn("chflags: %s", to.p_path);
 			rval = 1;
 		}
Comment 3 John-Mark Gurney freebsd_committer freebsd_triage 2003-06-22 08:02:39 UTC
State Changed
From-To: open->closed

fixed in rev1.41 of src/bin/cp/utils.c