Erase the source file open; specify a tempfile name option

Wayne Davison wayne at blorf.net
Sat May 12 16:26:13 EST 2001


I'm curious how to go about submitting a suggestion that affects both
the original BSD version and the portable release.  A few days ago I
sent off a BSD-relative patch to openssh at openssh.com.  Is this the right
thing to do?  I didn't hear anything back, but it's only been 3 days, so
I'm probably just being too antsy.

In the meantime, maybe someone else out there would like to check this
out.  Appended is a version of my patch for the latest portable code
(relative to the CVS version).  It adds two new options to scp that I
find useful:

  -E         Erase the source file after a successful copy.
  -T file    Use "file" as a temporary file that gets renamed into
             each actual destination file.

I implemented this patch in order to be able to move input files from
one system to another, while also not having the files show up in the
destination directory until each one was complete (there's a program
reading the input dir that expects any file in the destination dir to
be fully written).  These two new options make this easy.  Yes, the
user must specify a -T option that is on the same file system as the
destination dir or the rename will fail (with an appropriate error and
without erasing the source file).

I haven't changed any documentation yet.  I'd be glad to if this gets
accepted.

Enjoy,

..wayne..

---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
Index: scp.c
@@ -105,6 +105,7 @@

 /* Returns width of the terminal (for progress meter calculations). */
 int getttywidth(void);
+
 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc);

 /* Struct for addargs */
@@ -206,9 +207,11 @@
 uid_t userid;
 int errs, remin, remout;
 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+int eraseflag;
+char *tmpfn = "";

-#define	CMDNEEDS	64
-char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
+char *cmd;
+int cmdlen;

 int response(void);
 void rsource(char *, struct stat *);
@@ -236,7 +239,7 @@
 	addargs(&args, "-oFallBackToRsh no");

 	fflag = tflag = 0;
-	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != -1)
+	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:ET:")) != -1)
 		switch (ch) {
 		/* User-visible flags. */
 		case '4':
@@ -270,6 +273,12 @@
 		case 'q':
 			showprogress = 0;
 			break;
+		case 'E':
+			eraseflag = 1;
+			break;
+		case 'T':
+			tmpfn = xstrdup(optarg);
+			break;

 		/* Server options. */
 		case 'd':
@@ -319,8 +328,11 @@

 	remin = remout = -1;
 	/* Command to be executed on remote system using "ssh". */
-	(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
+	cmdlen = strlen(tmpfn) + 64;
+	cmd = xmalloc(cmdlen);
+	(void) snprintf(cmd, cmdlen, "scp%s%s%s%s%s%s%s",
 	    verbose_mode ? " -v" : "",
+	    eraseflag ? " -E" : "", *tmpfn ? " -T" : "", tmpfn,
 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
 	    targetshouldbedirectory ? " -d" : "");

@@ -370,7 +382,7 @@
 			host = strchr(argv[i], '@');
 			len = strlen(ssh_program) + strlen(argv[i]) +
 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
-			    strlen(thost) + strlen(targ) + CMDNEEDS + 32;
+			    strlen(thost) + strlen(targ) + cmdlen + 32;
 			bp = xmalloc(len);
 			if (host) {
 				*host++ = 0;
@@ -403,7 +415,7 @@
 			(void) xfree(bp);
 		} else {	/* local to remote */
 			if (remin == -1) {
-				len = strlen(targ) + CMDNEEDS + 20;
+				len = strlen(targ) + cmdlen + 20;
 				bp = xmalloc(len);
 				(void) snprintf(bp, len, "%s -t %s", cmd, targ);
 				host = cleanhostname(thost);
@@ -428,7 +440,8 @@
 	char *bp, *host, *src, *suser;

 	for (i = 0; i < argc - 1; i++) {
-		if (!(src = colon(argv[i]))) {	/* Local to local. */
+		src = colon(argv[i]);
+		if (!src && !eraseflag && !*tmpfn) { /* Local to local w/cp */
 			len = strlen(_PATH_CP) + strlen(argv[i]) +
 			    strlen(argv[argc - 1]) + 20;
 			bp = xmalloc(len);
@@ -441,23 +454,30 @@
 				++errs;
 			(void) xfree(bp);
 			continue;
+		}
+		if (src) {
+			*src++ = 0;
+			if (*src == 0)
+				src = ".";
+			if ((host = strchr(argv[i], '@')) == NULL) {
+				host = argv[i];
+				suser = NULL;
+			} else {
+				*host++ = 0;
+				suser = argv[i];
+				if (*suser == '\0')
+					suser = pwd->pw_name;
+				else if (!okname(suser))
+					continue;
+			}
+			host = cleanhostname(host);
 		}
-		*src++ = 0;
-		if (*src == 0)
-			src = ".";
-		if ((host = strchr(argv[i], '@')) == NULL) {
-			host = argv[i];
+		else {
+			src = argv[i];
+			host = "localhost";
 			suser = NULL;
-		} else {
-			*host++ = 0;
-			suser = argv[i];
-			if (*suser == '\0')
-				suser = pwd->pw_name;
-			else if (!okname(suser))
-				continue;
 		}
-		host = cleanhostname(host);
-		len = strlen(src) + CMDNEEDS + 20;
+		len = strlen(src) + cmdlen + 20;
 		bp = xmalloc(len);
 		(void) snprintf(bp, len, "%s -f %s", cmd, src);
 		if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
@@ -582,7 +602,10 @@
 			(void) atomicio(write, remout, "", 1);
 		else
 			run_err("%s: %s", name, strerror(haderr));
-		(void) response();
+		if (response() == 0 && eraseflag && !haderr) {
+			if (unlink(name) < 0)
+				run_err("%s: %s", name, strerror(errno));
+		}
 	}
 }

@@ -656,7 +679,7 @@
 	int amt, count, exists, first, mask, mode, ofd, omode;
 	off_t size;
 	int setimes, targisdir, wrerrno = 0;
-	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+	char ch, *cp, *np, *targ, *dest, *why, *vect[1], buf[2048];
 	struct timeval tv[2];

 #define	atime	tv[0]
@@ -770,6 +793,7 @@
 			np = namebuf;
 		} else
 			np = targ;
+		dest = *tmpfn? tmpfn : np;
 		curfile = cp;
 		exists = stat(np, &stb) == 0;
 		if (buf[0] == 'D') {
@@ -804,8 +828,8 @@
 		}
 		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));
+		if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
+bad:			run_err("%s: %s", dest, strerror(errno));
 			continue;
 		}
 		(void) atomicio(write, remout, "", 1);
@@ -861,7 +885,7 @@
 		}
 #if 0
 		if (ftruncate(ofd, size)) {
-			run_err("%s: truncate: %s", np, strerror(errno));
+			run_err("%s: truncate: %s", dest, strerror(errno));
 			wrerr = DISPLAYED;
 		}
 #endif
@@ -870,19 +894,19 @@
 #ifdef HAVE_FCHMOD
 				if (fchmod(ofd, omode))
 #else /* HAVE_FCHMOD */
-				if (chmod(np, omode))
+				if (chmod(dest, omode))
 #endif /* HAVE_FCHMOD */
 					run_err("%s: set mode: %s",
-					    np, strerror(errno));
+					    dest, strerror(errno));
 		} else {
 			if (!exists && omode != mode)
 #ifdef HAVE_FCHMOD
 				if (fchmod(ofd, omode & ~mask))
 #else /* HAVE_FCHMOD */
-				if (chmod(np, omode & ~mask))
+				if (chmod(dest, omode & ~mask))
 #endif /* HAVE_FCHMOD */
 					run_err("%s: set mode: %s",
-					    np, strerror(errno));
+					    dest, strerror(errno));
 		}
 		if (close(ofd) == -1) {
 			wrerr = YES;
@@ -891,15 +915,21 @@
 		(void) response();
 		if (setimes && wrerr == NO) {
 			setimes = 0;
-			if (utimes(np, tv) < 0) {
+			if (utimes(dest, tv) < 0) {
 				run_err("%s: set times: %s",
-				    np, strerror(errno));
+				    dest, strerror(errno));
 				wrerr = DISPLAYED;
 			}
 		}
+		if (*tmpfn && rename(tmpfn, np) < 0) {
+			wrerr = YES;
+			wrerrno = errno;
+		}
 		switch (wrerr) {
 		case YES:
-			run_err("%s: %s", np, strerror(wrerrno));
+			if (*tmpfn)
+				unlink(tmpfn);
+			run_err("%s: %s", dest, strerror(wrerrno));
 			break;
 		case NO:
 			(void) atomicio(write, remout, "", 1);
@@ -949,8 +979,8 @@
 void
 usage()
 {
-	(void) fprintf(stderr, "usage: scp "
-	    "[-pqrvBC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2\n"
+	(void) fprintf(stderr, "usage: scp [-pqrvBCE46] "
+	    "[-S ssh] [-P port] [-c cipher] [-i id] [-T tmp] f1 f2\n"
 	    "   or: scp [options] f1 ... fn directory\n");
 	exit(1);
 }
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---




More information about the openssh-unix-dev mailing list