Erase the source file open; specify a tempfile name option

Wayne Davison wayne at blorf.net
Tue May 15 05:49:35 EST 2001


On Mon, 14 May 2001, Kaelin Colclasure wrote:
> Hmmm, might it not be better to have the -T option generate a temporary
> file name for each file in the copy on its own?

Not for the purpose that I described in my email.  I need to be able to
specify a spot that is out of the way of the destination files because I
need the destination files to appear full-formed in their final spot
WITHOUT even having any temp files showing up in the destination dir(s)
before the end of the copy.  Thus, the -T option allows the user to
specify to scp exactly where this tmp file goes (and what it's named).

What you're describing is something else that would also be useful.  We
could have an option that says, "copy each file over to a temp filename
in the destination dir and rename it into place".  With this option the
user wouldn't have to specify a tempfile pathname, and it would work
with a recursive copy that changed file systems.

Attached is a patch that adds such an option:  -u (use unique tmpfile).
It also includes my previous -E and -T work with a few fixes:  (1) the
-T code now reports the right filename when a directory-creation error
occurs (not the tempfile name), (2) if we're writing to a tmpfile that
will be moved over an existing file and -p was not specified, preserve
the existing file's mode flags, and (3) if the user is doing a
local-to-local copy with flags that can't be handled by cp, ensure that
the source files are fully-qualified pathnames (so that the scp we spawn
knows where to find the files).

This patch is relative to the portable CVS.  I can make it based on the
BSD code, if desired.

..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, uniquetmpfiles;
+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:u")) != -1)
 		switch (ch) {
 		/* User-visible flags. */
 		case '4':
@@ -270,6 +273,15 @@
 		case 'q':
 			showprogress = 0;
 			break;
+		case 'E':
+			eraseflag = 1;
+			break;
+		case 'T':
+			tmpfn = xstrdup(optarg);
+			break;
+		case 'u':
+			uniquetmpfiles = 1;
+			break;

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

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

@@ -370,7 +385,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 +418,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);
@@ -424,11 +439,13 @@
 	int argc;
 	char *argv[];
 {
-	int i, len;
-	char *bp, *host, *src, *suser;
+	int i, len, cwd_len = 0;
+	char *bp, *host, *src, *suser, cwd[MAXPATHLEN];

 	for (i = 0; i < argc - 1; i++) {
-		if (!(src = colon(argv[i]))) {	/* Local to local. */
+		src = colon(argv[i]);
+		/* See if we can just use cp for a local to local copy */
+		if (!src && !eraseflag && !uniquetmpfiles && !*tmpfn) {
 			len = strlen(_PATH_CP) + strlen(argv[i]) +
 			    strlen(argv[argc - 1]) + 20;
 			bp = xmalloc(len);
@@ -441,23 +458,43 @@
 				++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];
+			if (*src != '/') {
+				if (!cwd_len) {
+					if (!getcwd(cwd, sizeof cwd)) {
+						error("Couldn't get local cwd: %s",
+						    strerror(errno));
+						break;
+					}
+					cwd_len = strlen(cwd);
+				}
+				len = cwd_len + strlen(src) + 2;
+				src = xmalloc(len);
+				snprintf(src, len, "%s/%s", cwd, 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 +619,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 +696,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]
@@ -774,6 +814,7 @@
 		exists = stat(np, &stb) == 0;
 		if (buf[0] == 'D') {
 			int mod_flag = pflag;
+			dest = np;
 			if (exists) {
 				if (!S_ISDIR(stb.st_mode)) {
 					errno = ENOTDIR;
@@ -804,9 +845,33 @@
 		}
 		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 (uniquetmpfiles) {
+			int len;
+			char *s;
+			if ((s = strrchr(np, '/')) != NULL)
+				s++;
+			else
+				s = np;
+			if (*tmpfn)
+				xfree(tmpfn);
+			len = strlen(np) + 9;
+			tmpfn = dest = xmalloc(len);
+			strcpy(tmpfn, np);
+			snprintf(tmpfn + (s - np), len, ".%s.XXXXXX", s);
+			mode = -1;  /* Ensure future chmod() */
+			ofd = mkstemp(tmpfn);
+		}
+		else {
+			dest = *tmpfn? tmpfn : np;
+			ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, mode);
+		}
+		if (ofd < 0) {
+bad:			run_err("%s: %s", dest, strerror(errno));
+			continue;
+		}
+		if (!pflag && exists && *tmpfn) {
+			omode = stb.st_mode;
+			exists = 0;
 		}
 		(void) atomicio(write, remout, "", 1);
 		if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
@@ -861,7 +926,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 +935,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 +956,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 +1020,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 [-pqruvBCE46] "
+	    "[-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