[PATCH] Support symlinks in scp of openssh 2

Chip Salzenberg chip at valinux.com
Fri Aug 18 21:37:06 EST 2000


I'm fond of the "-a" (archive) option of cp, and I'm a heavy user of
scp, so I guess it's inevitable that I would eventually add support
for "-a" to scp.  :-)  Actually, it's a "-L" flag for preserving
symlinks, and a "-a" flag that is shorthand for "-Lpr".

Please let me know if I'm not doing this right....  I made a great
effort to limit the number of code lines changed, so as to minimize
the difficulty of understanding and accepting this patch.


Index: scp.1
--- scp.1.prev
+++ scp.1	Fri Aug 18 04:24:46 2000
@@ -20,5 +20,5 @@
 .Sh SYNOPSIS
 .Nm scp
-.Op Fl pqrvC46
+.Op Fl aLpqrvC46
 .Op Fl P Ar port
 .Op Fl c Ar cipher
@@ -69,4 +69,9 @@
 .It Fl o Ar ssh_options
 specify options to be passed to ssh.  For example: "-o UsePriviledgePort=no"
+.It Fl a
+Archival copy.  Shorthand for "-Lpr".
+.It Fl L
+Preserves symbolic links as such, instead of following them and
+copying their targets.
 .It Fl p
 Preserves modification times, access times, and modes from the

Index: scp.c
--- scp.c.prev
+++ scp.c	Fri Aug 18 04:14:40 2000
@@ -253,5 +253,5 @@ struct passwd *pwd;
 uid_t userid;
 int errs, remin, remout;
-int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+int linkflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
 
 #define	CMDNEEDS	64
@@ -280,5 +280,5 @@ main(argc, argv)
 	memset(sshoptions,0,sizeof(sshoptions));
 	sshoptionsend = sshoptions;
-	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:o:S:q46")) != EOF)
+	while ((ch = getopt(argc, argv, "adfLprtvBCc:i:P:o:S:q46")) != EOF)
 		switch (ch) {
 		/* User-visible flags. */
@@ -289,4 +289,12 @@ main(argc, argv)
 			IPv6 = 1;
 			break;
+		case 'a':
+			linkflag = 1;
+			pflag = 1;
+			iamrecursive = 1;
+			break;
+		case 'L':
+			linkflag = 1;
+			break;
 		case 'p':
 			pflag = 1;
@@ -549,12 +557,24 @@ source(argc, argv)
 		name = argv[indx];
 		statbytes = 0;
-		if ((fd = open(name, O_RDONLY, 0)) < 0)
-			goto syserr;
-		if (fstat(fd, &stb) < 0) {
+		if (linkflag) {
+			fd = -1;
+			result = lstat(name, &stb);
+		}
+		else {
+			if ((fd = open(name, O_RDONLY, 0)) < 0)
+				goto syserr;
+			result = fstat(fd, &stb);
+		}
+		if (result < 0) {
 syserr:			run_err("%s: %s", name, strerror(errno));
 			goto next;
 		}
 		switch (stb.st_mode & S_IFMT) {
+		case S_IFLNK:
+			/* readlink later */
+			break;
 		case S_IFREG:
+			if (fd < 0 && (fd = open(name, O_RDONLY, 0)) < 0)
+				goto syserr;
 			break;
 		case S_IFDIR:
@@ -586,6 +606,7 @@ syserr:			run_err("%s: %s", name, strerr
 		}
 #define	FILEMODEMASK	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
-		(void) sprintf(buf, "C%04o %lu %s\n",
-			     (unsigned int) (stb.st_mode & FILEMODEMASK),
+		(void) sprintf(buf, "%c%04o %lu %s\n",
+			       ((stb.st_mode & S_IFMT) == S_IFLNK) ? 'L' : 'C',
+			       (unsigned int) (stb.st_mode & FILEMODEMASK),
 			       (unsigned long) stb.st_size,
 			       last);
@@ -609,5 +630,8 @@ next:			(void) close(fd);
 				amt = stb.st_size - i;
 			if (!haderr) {
-				result = atomicio(read, fd, bp, amt);
+				if ((stb.st_mode & S_IFMT) == S_IFLNK)
+					result = readlink(name, bp, amt);
+				else
+					result = atomicio(read, fd, bp, amt);
 				if (result != amt)
 					haderr = result >= 0 ? EIO : errno;
@@ -625,5 +649,5 @@ next:			(void) close(fd);
 			progressmeter(1);
 
-		if (close(fd) < 0 && !haderr)
+		if (fd >= 0 && close(fd) < 0 && !haderr)
 			haderr = errno;
 		if (!haderr)
@@ -775,5 +799,5 @@ sink(argc, argv)
 			continue;
 		}
-		if (*cp != 'C' && *cp != 'D') {
+		if (*cp != 'C' && *cp != 'D' && *cp != 'L') {
 			/*
 			 * Check for the case "rcp remote:foo\* local:bar".
@@ -816,5 +840,5 @@ sink(argc, argv)
 			np = targ;
 		curfile = cp;
-		exists = stat(np, &stb) == 0;
+		exists = (buf[0] == 'L' ? lstat : stat)(np, &stb) == 0;
 		if (buf[0] == 'D') {
 			int mod_flag = pflag;
@@ -845,9 +869,14 @@ sink(argc, argv)
 			continue;
 		}
-		omode = mode;
-		mode |= S_IWRITE;
-		if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
-bad:			run_err("%s: %s", np, strerror(errno));
-			continue;
+		if (buf[0] == 'L')
+			ofd = omode = -1;
+		else {
+			omode = mode;
+			mode |= S_IWRITE;
+			if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC,
+					mode)) < 0) {
+bad:				run_err("%s: %s", np, strerror(errno));
+				continue;
+			}
 		}
 		(void) atomicio(write, remout, "", 1);
@@ -891,4 +920,12 @@ bad:			run_err("%s: %s", np, strerror(er
 		if (showprogress)
 			progressmeter(1);
+		if (buf[0] == 'L') {
+			if (size >= PIPE_BUF)
+				SCREWUP("symlink bigger than PIPE_BUF");
+			bp[size] = '\0';
+			wrerr = (symlink(bp, np) < 0) ? YES : NO;
+			wrerrno = errno;
+			goto done;
+		}
 		if (count != 0 && wrerr == NO &&
 		    (j = atomicio(write, ofd, bp, count)) != count) {
@@ -917,5 +954,4 @@ bad:			run_err("%s: %s", np, strerror(er
 			wrerrno = errno;
 		}
-		(void) response();
 		if (setimes && wrerr == NO) {
 			setimes = 0;
@@ -926,4 +962,5 @@ bad:			run_err("%s: %s", np, strerror(er
 			}
 		}
+done:		(void) response();
 		switch (wrerr) {
 		case YES:

-- 
Chip Salzenberg              - a.k.a. -              <chip at valinux.com>
"I wanted to play hopscotch with the impenetrable mystery of existence,
    but he stepped in a wormhole and had to go in early."  // MST3K





More information about the openssh-unix-dev mailing list