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: