[PATCH] quick hack for 'resume' support in sftp

Thomas Wouters thomas at xs4all.net
Tue Sep 11 06:58:20 EST 2001


I saw several questions regarding 'resume transfer' support in sftp come up,
in the archives, but no real satisfying answer. I had a particular itch
myself, so I scratched it with a quick hack. Patch attached, since it's not
really big.

To explain, I need to use ssh (scp or sftp) to transfer files to and from a
Windows box. No other method is available. And the Windows machine has no
rsync or unixlike tools or cygwin or anything else I can use to fake
resuming transfers, so I really have to have the resume support in sftp
itself. I noticed the protocol supported it just fine, so I did a little
dirty hacking and added a '-r' option to sftp that enables resuming.

It's very rough: it triggers off (local) name alone, though it refuses to
append to a file that's already larger than the new file. It cuts a bit off
the end of the local file before resuming, but that's mostly out of
paranoia. It should really compare a couple of blocks of the end of the
local file with the remote file, but I didn't want to bother with that. It
should really present a choice when it discovers a possibly partial local
file with the same name, the way NcFTP does, but, again, I didn't want to
bother with that. (And, frankly, the rest of sftp could do with a little
more NcFTP or (Net)BSD FTP interface sprinkled in ;) But one thing at a
time.)

If there's any interest in getting this in the distributed sftp (in a more
mature form, of course) I can get it in better shape, but I'm honestly
clueless on the direction of ssh and sftp development. If this kind of thing
is really not wanted, I guess I'll keep a patch somewhere on a website
instead. I'd like to note, though, that in searching for an sftp version
with resume support I found a number of Windows clients that claimed to
support resuming in their sftp clients :-)

Regards,
  Thomas.

PS: Please include me in any responses, I'm not on the list right now.
-- 
Thomas Wouters <thomas at xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!
-------------- next part --------------
diff -cr openssh-2.9p2/sftp-client.c openssh-2.9p2.resume/sftp-client.c
*** openssh-2.9p2/sftp-client.c	Fri Apr  6 01:26:33 2001
--- openssh-2.9p2.resume/sftp-client.c	Mon Sep 10 18:04:36 2001
***************
*** 48,53 ****
--- 48,56 ----
  /* XXX: what should this be? */
  #define COPY_SIZE	8192
  
+ /* resume or not (from sftp.c) */
+ extern int enable_resume;
+ 
  /* Message ID */
  static u_int msg_id = 1;
  
***************
*** 682,687 ****
--- 685,691 ----
  	Buffer msg;
  	Attrib junk, *a;
  	int status;
+ 	struct stat localfile;
  
  	a = do_stat(fd_in, fd_out, remote_path, 0);
  	if (a == NULL)
***************
*** 698,705 ****
  		error("Cannot download a directory: %s", remote_path);
  		return(-1);
  	}
! 
! 	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
  	if (local_fd == -1) {
  		error("Couldn't open local file \"%s\" for writing: %s",
  		    local_path, strerror(errno));
--- 702,720 ----
  		error("Cannot download a directory: %s", remote_path);
  		return(-1);
  	}
! 	
! 	if (enable_resume && (stat(local_path, &localfile) == 0) &&
! 	    S_ISREG(localfile.st_mode) && (localfile.st_size < a->size)) {
! 		local_fd = open(local_path, O_WRONLY, mode);
! 		offset = localfile.st_size - (localfile.st_size % 16384);
! 		if (local_fd != -1) {
! 			ftruncate(local_fd, offset);
! 			lseek(local_fd, 0, SEEK_END);
! 		}
! 	} else {
! 		local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
! 		offset = 0;
! 	}
  	if (local_fd == -1) {
  		error("Couldn't open local file \"%s\" for writing: %s",
  		    local_path, strerror(errno));
***************
*** 727,733 ****
  	}
  
  	/* Read from remote and write to local */
- 	offset = 0;
  	for(;;) {
  		u_int len;
  		char *data;
--- 742,747 ----
diff -cr openssh-2.9p2/sftp.1 openssh-2.9p2.resume/sftp.1
*** openssh-2.9p2/sftp.1	Sun Apr 22 19:17:46 2001
--- openssh-2.9p2.resume/sftp.1	Mon Sep 10 18:07:56 2001
***************
*** 82,87 ****
--- 82,89 ----
  .Xr ssh 1 .
  .It Fl v
  Raise logging level. This option is also passed to ssh.
+ .It Fl r
+ Enable resuming of downloads. This might not work correctly.
  .El
  .Sh INTERACTIVE COMMANDS
  Once in interactive mode,
diff -cr openssh-2.9p2/sftp.c openssh-2.9p2.resume/sftp.c
*** openssh-2.9p2/sftp.c	Mon Apr 16 10:26:42 2001
--- openssh-2.9p2.resume/sftp.c	Mon Sep 10 17:52:39 2001
***************
*** 48,53 ****
--- 48,54 ----
  #include "scp-common.h"
  
  int use_ssh1 = 0;
+ int enable_resume = 0;
  char *ssh_program = _PATH_SSH_PROGRAM;
  char *sftp_server = NULL;
  FILE* infile;
***************
*** 148,154 ****
  void
  usage(void)
  {
! 	fprintf(stderr, "usage: sftp [-1vC] [-b batchfile] [-osshopt=value] [user@]host[:file [file]]\n");
  	exit(1);
  }
  
--- 149,155 ----
  void
  usage(void)
  {
! 	fprintf(stderr, "usage: sftp [-1vCr] [-b batchfile] [-osshopt=value] [user@]host[:file [file]]\n");
  	exit(1);
  }
  
***************
*** 167,173 ****
  	infile = stdin;         /* Read from STDIN unless changed by -b */
  	debug_level = compress_flag = 0;
  
! 	while ((ch = getopt(argc, argv, "1hvCo:s:S:b:")) != -1) {
  		switch (ch) {
  		case 'C':
  			compress_flag = 1;
--- 168,174 ----
  	infile = stdin;         /* Read from STDIN unless changed by -b */
  	debug_level = compress_flag = 0;
  
! 	while ((ch = getopt(argc, argv, "1hvCro:s:S:b:")) != -1) {
  		switch (ch) {
  		case 'C':
  			compress_flag = 1;
***************
*** 197,202 ****
--- 198,206 ----
  					fatal("%s (%s).", strerror(errno), optarg);
  			} else
  				fatal("Filename already specified.");
+ 			break;
+ 		case 'r':
+ 			enable_resume = 1;
  			break;
  		case 'h':
  		default:


More information about the openssh-unix-dev mailing list