summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--sftp-client.c288
-rw-r--r--sftp-client.h6
-rw-r--r--sftp-int.c13
-rw-r--r--sftp.17
-rw-r--r--sftp.c11
6 files changed, 225 insertions, 106 deletions
diff --git a/ChangeLog b/ChangeLog
index 984d0c92f..6895f5267 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,10 @@
16 - markus@cvs.openbsd.org 2002/02/11 16:21:42 16 - markus@cvs.openbsd.org 2002/02/11 16:21:42
17 [match.c] 17 [match.c]
18 support up to 40 algorithms per proposal 18 support up to 40 algorithms per proposal
19 - djm@cvs.openbsd.org 2002/02/12 12:32:27
20 [sftp.1 sftp.c sftp-client.c sftp-client.h sftp-int.c]
21 Perform multiple overlapping read/write requests in file transfer. Mostly
22 done by Tobias Ringstrom <tori@ringstrom.mine.nu>; ok markus@
19 23
2020020210 2420020210
21 - (djm) OpenBSD CVS Sync 25 - (djm) OpenBSD CVS Sync
@@ -7563,4 +7567,4 @@
7563 - Wrote replacements for strlcpy and mkdtemp 7567 - Wrote replacements for strlcpy and mkdtemp
7564 - Released 1.0pre1 7568 - Released 1.0pre1
7565 7569
7566$Id: ChangeLog,v 1.1843 2002/02/13 02:55:30 djm Exp $ 7570$Id: ChangeLog,v 1.1844 2002/02/13 03:03:56 djm Exp $
diff --git a/sftp-client.c b/sftp-client.c
index 362814d42..835ae068a 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 2001 Damien Miller. All rights reserved. 2 * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -24,12 +24,17 @@
24 24
25/* XXX: memleaks */ 25/* XXX: memleaks */
26/* XXX: signed vs unsigned */ 26/* XXX: signed vs unsigned */
27/* XXX: redesign to allow concurrent overlapped operations */
28/* XXX: we use fatal too much, error may be more appropriate in places */ 27/* XXX: we use fatal too much, error may be more appropriate in places */
29/* XXX: copy between two remote sites */ 28/* XXX: copy between two remote sites */
30 29
31#include "includes.h" 30#include "includes.h"
32RCSID("$OpenBSD: sftp-client.c,v 1.20 2002/02/05 00:00:46 djm Exp $"); 31RCSID("$OpenBSD: sftp-client.c,v 1.21 2002/02/12 12:32:27 djm Exp $");
32
33#if defined(HAVE_SYS_QUEUE_H) && !defined(HAVE_BOGUS_SYS_QUEUE_H)
34#include <sys/queue.h>
35#else
36#include "openbsd-compat/fake-queue.h"
37#endif
33 38
34#include "buffer.h" 39#include "buffer.h"
35#include "bufaux.h" 40#include "bufaux.h"
@@ -42,6 +47,9 @@ RCSID("$OpenBSD: sftp-client.c,v 1.20 2002/02/05 00:00:46 djm Exp $");
42#include "sftp-common.h" 47#include "sftp-common.h"
43#include "sftp-client.h" 48#include "sftp-client.h"
44 49
50/* Minimum amount of data to read at at time */
51#define MIN_READ_SIZE 512
52
45/* Message ID */ 53/* Message ID */
46static u_int msg_id = 1; 54static u_int msg_id = 1;
47 55
@@ -664,16 +672,44 @@ do_readlink(int fd_in, int fd_out, char *path)
664 return(filename); 672 return(filename);
665} 673}
666 674
675static void
676send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
677 char *handle, u_int handle_len)
678{
679 Buffer msg;
680
681 buffer_init(&msg);
682 buffer_clear(&msg);
683 buffer_put_char(&msg, SSH2_FXP_READ);
684 buffer_put_int(&msg, id);
685 buffer_put_string(&msg, handle, handle_len);
686 buffer_put_int64(&msg, offset);
687 buffer_put_int(&msg, len);
688 send_msg(fd_out, &msg);
689 buffer_free(&msg);
690}
691
667int 692int
668do_download(int fd_in, int fd_out, char *remote_path, char *local_path, 693do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
669 int pflag, size_t buflen) 694 int pflag, size_t buflen, int num_requests)
670{ 695{
671 int local_fd, status;
672 u_int expected_id, handle_len, mode, type, id;
673 u_int64_t offset;
674 char *handle;
675 Buffer msg;
676 Attrib junk, *a; 696 Attrib junk, *a;
697 Buffer msg;
698 char *handle;
699 int local_fd, status, num_req, max_req, write_error;
700 int read_error, write_errno;
701 u_int64_t offset, size;
702 u_int handle_len, mode, type, id;
703 struct request {
704 u_int id;
705 u_int len;
706 u_int64_t offset;
707 TAILQ_ENTRY(request) tq;
708 };
709 TAILQ_HEAD(reqhead, request) requests;
710 struct request *req;
711
712 TAILQ_INIT(&requests);
677 713
678 a = do_stat(fd_in, fd_out, remote_path, 0); 714 a = do_stat(fd_in, fd_out, remote_path, 0);
679 if (a == NULL) 715 if (a == NULL)
@@ -691,6 +727,11 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
691 return(-1); 727 return(-1);
692 } 728 }
693 729
730 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
731 size = a->size;
732 else
733 size = 0;
734
694 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); 735 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
695 if (local_fd == -1) { 736 if (local_fd == -1) {
696 error("Couldn't open local file \"%s\" for writing: %s", 737 error("Couldn't open local file \"%s\" for writing: %s",
@@ -719,88 +760,140 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
719 } 760 }
720 761
721 /* Read from remote and write to local */ 762 /* Read from remote and write to local */
722 offset = 0; 763 write_error = read_error = write_errno = num_req = offset = 0;
723 for (;;) { 764 max_req = 1;
724 u_int len; 765 while (num_req > 0 || max_req > 0) {
725 char *data; 766 char *data;
767 u_int len;
726 768
727 id = expected_id = msg_id++; 769 /* Send some more requests */
728 770 while (num_req < max_req) {
729 buffer_clear(&msg); 771 debug3("Request range %llu -> %llu (%d/%d)",
730 buffer_put_char(&msg, SSH2_FXP_READ); 772 offset, offset + buflen - 1, num_req, max_req);
731 buffer_put_int(&msg, id); 773 req = xmalloc(sizeof(*req));
732 buffer_put_string(&msg, handle, handle_len); 774 req->id = msg_id++;
733 buffer_put_int64(&msg, offset); 775 req->len = buflen;
734 buffer_put_int(&msg, buflen); 776 req->offset = offset;
735 send_msg(fd_out, &msg); 777 offset += buflen;
736 debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u", 778 num_req++;
737 id, (u_int64_t)offset, buflen); 779 TAILQ_INSERT_TAIL(&requests, req, tq);
780 send_read_request(fd_out, req->id, req->offset,
781 req->len, handle, handle_len);
782 }
738 783
739 buffer_clear(&msg); 784 buffer_clear(&msg);
740
741 get_msg(fd_in, &msg); 785 get_msg(fd_in, &msg);
742 type = buffer_get_char(&msg); 786 type = buffer_get_char(&msg);
743 id = buffer_get_int(&msg); 787 id = buffer_get_int(&msg);
744 debug3("Received reply T:%d I:%d", type, id); 788 debug3("Received reply T:%d I:%d R:%d", type, id, max_req);
745 if (id != expected_id) 789
746 fatal("ID mismatch (%d != %d)", id, expected_id); 790 /* Find the request in our queue */
747 if (type == SSH2_FXP_STATUS) { 791 for(req = TAILQ_FIRST(&requests);
792 req != NULL && req->id != id;
793 req = TAILQ_NEXT(req, tq))
794 ;
795 if (req == NULL)
796 fatal("Unexpected reply %u", id);
797
798 switch (type) {
799 case SSH2_FXP_STATUS:
748 status = buffer_get_int(&msg); 800 status = buffer_get_int(&msg);
801 if (status != SSH2_FX_EOF)
802 read_error = 1;
803 max_req = 0;
804 TAILQ_REMOVE(&requests, req, tq);
805 xfree(req);
806 num_req--;
807 break;
808 case SSH2_FXP_DATA:
809 data = buffer_get_string(&msg, &len);
810 debug3("Received data %llu -> %llu", req->offset,
811 req->offset + len - 1);
812 if (len > req->len)
813 fatal("Received more data than asked for "
814 "%d > %d", len, req->len);
815 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
816 atomicio(write, local_fd, data, len) != len) &&
817 !write_error) {
818 write_errno = errno;
819 write_error = 1;
820 max_req = 0;
821 }
822 xfree(data);
749 823
750 if (status == SSH2_FX_EOF) 824 if (len == req->len) {
751 break; 825 TAILQ_REMOVE(&requests, req, tq);
752 else { 826 xfree(req);
753 error("Couldn't read from remote " 827 num_req--;
754 "file \"%s\" : %s", remote_path, 828 } else {
755 fx2txt(status)); 829 /* Resend the request for the missing data */
756 do_close(fd_in, fd_out, handle, handle_len); 830 debug3("Short data block, re-requesting "
757 goto done; 831 "%llu -> %llu (%2d)", req->offset + len,
832 req->offset + req->len - 1, num_req);
833 req->id = msg_id++;
834 req->len -= len;
835 req->offset += len;
836 send_read_request(fd_out, req->id,
837 req->offset, req->len, handle,
838 handle_len);
839 /* Reduce the request size */
840 if (len < buflen)
841 buflen = MAX(MIN_READ_SIZE, len);
842 }
843 if (max_req > 0) { /* max_req = 0 iff EOF received */
844 if (size > 0 && offset > size) {
845 /* Only one request at a time
846 * after the expected EOF */
847 debug3("Finish at %llu (%2d)",
848 offset, num_req);
849 max_req = 1;
850 }
851 else if (max_req < num_requests + 1) {
852 ++max_req;
853 }
758 } 854 }
759 } else if (type != SSH2_FXP_DATA) { 855 break;
856 default:
760 fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", 857 fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
761 SSH2_FXP_DATA, type); 858 SSH2_FXP_DATA, type);
762 } 859 }
763
764 data = buffer_get_string(&msg, &len);
765 if (len > buflen)
766 fatal("Received more data than asked for %d > %d",
767 len, buflen);
768
769 debug3("In read loop, got %d offset %llu", len,
770 (u_int64_t)offset);
771 if (atomicio(write, local_fd, data, len) != len) {
772 error("Couldn't write to \"%s\": %s", local_path,
773 strerror(errno));
774 do_close(fd_in, fd_out, handle, handle_len);
775 status = -1;
776 xfree(data);
777 goto done;
778 }
779
780 offset += len;
781 xfree(data);
782 } 860 }
783 status = do_close(fd_in, fd_out, handle, handle_len);
784 861
785 /* Override umask and utimes if asked */ 862 /* Sanity check */
863 if (TAILQ_FIRST(&requests) != NULL)
864 fatal("Transfer complete, but requests still in queue");
865
866 if (read_error) {
867 error("Couldn't read from remote "
868 "file \"%s\" : %s", remote_path,
869 fx2txt(status));
870 do_close(fd_in, fd_out, handle, handle_len);
871 } else if (write_error) {
872 error("Couldn't write to \"%s\": %s", local_path,
873 strerror(write_errno));
874 status = -1;
875 do_close(fd_in, fd_out, handle, handle_len);
876 } else {
877 status = do_close(fd_in, fd_out, handle, handle_len);
878
879 /* Override umask and utimes if asked */
786#ifdef HAVE_FCHMOD 880#ifdef HAVE_FCHMOD
787 if (pflag && fchmod(local_fd, mode) == -1) 881 if (pflag && fchmod(local_fd, mode) == -1)
788#else 882#else
789 if (pflag && chmod(local_path, mode) == -1) 883 if (pflag && chmod(local_path, mode) == -1)
790#endif /* HAVE_FCHMOD */ 884#endif /* HAVE_FCHMOD */
791 error("Couldn't set mode on \"%s\": %s", local_path, 885 error("Couldn't set mode on \"%s\": %s", local_path,
792 strerror(errno)); 886 strerror(errno));
793 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 887 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
794 struct timeval tv[2]; 888 struct timeval tv[2];
795 tv[0].tv_sec = a->atime; 889 tv[0].tv_sec = a->atime;
796 tv[1].tv_sec = a->mtime; 890 tv[1].tv_sec = a->mtime;
797 tv[0].tv_usec = tv[1].tv_usec = 0; 891 tv[0].tv_usec = tv[1].tv_usec = 0;
798 if (utimes(local_path, tv) == -1) 892 if (utimes(local_path, tv) == -1)
799 error("Can't set times on \"%s\": %s", local_path, 893 error("Can't set times on \"%s\": %s",
800 strerror(errno)); 894 local_path, strerror(errno));
895 }
801 } 896 }
802
803done:
804 close(local_fd); 897 close(local_fd);
805 buffer_free(&msg); 898 buffer_free(&msg);
806 xfree(handle); 899 xfree(handle);
@@ -809,7 +902,7 @@ done:
809 902
810int 903int
811do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, 904do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
812 int pflag, size_t buflen) 905 int pflag, size_t buflen, int num_requests)
813{ 906{
814 int local_fd, status; 907 int local_fd, status;
815 u_int handle_len, id; 908 u_int handle_len, id;
@@ -818,6 +911,8 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
818 Buffer msg; 911 Buffer msg;
819 struct stat sb; 912 struct stat sb;
820 Attrib a; 913 Attrib a;
914 u_int32_t startid;
915 u_int32_t ackid;
821 916
822 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 917 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
823 error("Couldn't open local file \"%s\" for reading: %s", 918 error("Couldn't open local file \"%s\" for reading: %s",
@@ -859,6 +954,7 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
859 return(-1); 954 return(-1);
860 } 955 }
861 956
957 startid = ackid = id + 1;
862 data = xmalloc(buflen); 958 data = xmalloc(buflen);
863 959
864 /* Read from local and write to remote */ 960 /* Read from local and write to remote */
@@ -877,29 +973,34 @@ do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
877 if (len == -1) 973 if (len == -1)
878 fatal("Couldn't read from \"%s\": %s", local_path, 974 fatal("Couldn't read from \"%s\": %s", local_path,
879 strerror(errno)); 975 strerror(errno));
880 if (len == 0) 976
977 if (len != 0) {
978 buffer_clear(&msg);
979 buffer_put_char(&msg, SSH2_FXP_WRITE);
980 buffer_put_int(&msg, ++id);
981 buffer_put_string(&msg, handle, handle_len);
982 buffer_put_int64(&msg, offset);
983 buffer_put_string(&msg, data, len);
984 send_msg(fd_out, &msg);
985 debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
986 id, (u_int64_t)offset, len);
987 } else if ( id < ackid )
881 break; 988 break;
882 989
883 buffer_clear(&msg); 990 if (id == startid || len == 0 ||
884 buffer_put_char(&msg, SSH2_FXP_WRITE); 991 id - ackid >= num_requests) {
885 buffer_put_int(&msg, ++id); 992 status = get_status(fd_in, ackid);
886 buffer_put_string(&msg, handle, handle_len); 993 if (status != SSH2_FX_OK) {
887 buffer_put_int64(&msg, offset); 994 error("Couldn't write to remote file \"%s\": %s",
888 buffer_put_string(&msg, data, len); 995 remote_path, fx2txt(status));
889 send_msg(fd_out, &msg); 996 do_close(fd_in, fd_out, handle, handle_len);
890 debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", 997 close(local_fd);
891 id, (u_int64_t)offset, len); 998 goto done;
892 999 }
893 status = get_status(fd_in, id); 1000 debug3("In write loop, got %d offset %llu", len,
894 if (status != SSH2_FX_OK) { 1001 (u_int64_t)offset);
895 error("Couldn't write to remote file \"%s\": %s", 1002 ++ackid;
896 remote_path, fx2txt(status));
897 do_close(fd_in, fd_out, handle, handle_len);
898 close(local_fd);
899 goto done;
900 } 1003 }
901 debug3("In write loop, got %d offset %llu", len,
902 (u_int64_t)offset);
903 1004
904 offset += len; 1005 offset += len;
905 } 1006 }
@@ -924,4 +1025,3 @@ done:
924 buffer_free(&msg); 1025 buffer_free(&msg);
925 return status; 1026 return status;
926} 1027}
927
diff --git a/sftp-client.h b/sftp-client.h
index 20350701c..477c8ed3b 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.7 2002/02/05 00:00:46 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.8 2002/02/12 12:32:27 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2002 Damien Miller. All rights reserved. 4 * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
@@ -94,10 +94,10 @@ char *do_readlink(int, int, char *);
94 * Download 'remote_path' to 'local_path'. Preserve permissions and times 94 * Download 'remote_path' to 'local_path'. Preserve permissions and times
95 * if 'pflag' is set 95 * if 'pflag' is set
96 */ 96 */
97int do_download(int, int, char *, char *, int, size_t); 97int do_download(int, int, char *, char *, int, size_t, int);
98 98
99/* 99/*
100 * Upload 'local_path' to 'remote_path'. Preserve permissions and times 100 * Upload 'local_path' to 'remote_path'. Preserve permissions and times
101 * if 'pflag' is set 101 * if 'pflag' is set
102 */ 102 */
103int do_upload(int, int, char *, char *, int , size_t); 103int do_upload(int, int, char *, char *, int , size_t, int);
diff --git a/sftp-int.c b/sftp-int.c
index f86922d0c..babc0ed60 100644
--- a/sftp-int.c
+++ b/sftp-int.c
@@ -26,7 +26,7 @@
26/* XXX: recursive operations */ 26/* XXX: recursive operations */
27 27
28#include "includes.h" 28#include "includes.h"
29RCSID("$OpenBSD: sftp-int.c,v 1.42 2002/02/05 00:00:46 djm Exp $"); 29RCSID("$OpenBSD: sftp-int.c,v 1.43 2002/02/12 12:32:27 djm Exp $");
30 30
31#include "buffer.h" 31#include "buffer.h"
32#include "xmalloc.h" 32#include "xmalloc.h"
@@ -45,6 +45,9 @@ extern FILE *infile;
45/* Size of buffer used when copying files */ 45/* Size of buffer used when copying files */
46extern size_t copy_buffer_len; 46extern size_t copy_buffer_len;
47 47
48/* Number of concurrent outstanding requests */
49extern int num_requests;
50
48/* Version of server we are speaking to */ 51/* Version of server we are speaking to */
49int version; 52int version;
50 53
@@ -385,7 +388,7 @@ process_get(int in, int out, char *src, char *dst, char *pwd, int pflag)
385 } 388 }
386 printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst); 389 printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst);
387 err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag, 390 err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag,
388 copy_buffer_len); 391 copy_buffer_len, num_requests);
389 goto out; 392 goto out;
390 } 393 }
391 394
@@ -410,7 +413,7 @@ process_get(int in, int out, char *src, char *dst, char *pwd, int pflag)
410 413
411 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 414 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
412 if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag, 415 if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag,
413 copy_buffer_len) == -1) 416 copy_buffer_len, num_requests) == -1)
414 err = -1; 417 err = -1;
415 xfree(abs_dst); 418 xfree(abs_dst);
416 abs_dst = NULL; 419 abs_dst = NULL;
@@ -469,7 +472,7 @@ process_put(int in, int out, char *src, char *dst, char *pwd, int pflag)
469 } 472 }
470 printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst); 473 printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst);
471 err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag, 474 err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag,
472 copy_buffer_len); 475 copy_buffer_len, num_requests);
473 goto out; 476 goto out;
474 } 477 }
475 478
@@ -494,7 +497,7 @@ process_put(int in, int out, char *src, char *dst, char *pwd, int pflag)
494 497
495 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 498 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
496 if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag, 499 if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag,
497 copy_buffer_len) == -1) 500 copy_buffer_len, num_requests) == -1)
498 err = -1; 501 err = -1;
499 } 502 }
500 503
diff --git a/sftp.1 b/sftp.1
index 059b46d1f..bfdbce1e9 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.29 2002/02/06 14:22:42 markus Exp $ 1.\" $OpenBSD: sftp.1,v 1.30 2002/02/12 12:32:27 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -37,6 +37,7 @@
37.Op Fl B Ar buffer_size 37.Op Fl B Ar buffer_size
38.Op Fl F Ar ssh_config 38.Op Fl F Ar ssh_config
39.Op Fl P Ar sftp_server path 39.Op Fl P Ar sftp_server path
40.Op Fl R Ar num_requests
40.Op Fl S Ar program 41.Op Fl S Ar program
41.Ar host 42.Ar host
42.Nm sftp 43.Nm sftp
@@ -118,6 +119,10 @@ Connect directly to a local
118(rather than via 119(rather than via
119.Nm ssh ) 120.Nm ssh )
120This option may be useful in debugging the client and server. 121This option may be useful in debugging the client and server.
122.It Fl R Ar num_requests
123Specify how many requests may be outstanding at any one time. Increasing
124this may slightly improve file transfer speed but will increase memory
125usage. The default is 16 outstanding requests.
121.It Fl S Ar program 126.It Fl S Ar program
122Name of the 127Name of the
123.Ar program 128.Ar program
diff --git a/sftp.c b/sftp.c
index 160851d7f..045e0766e 100644
--- a/sftp.c
+++ b/sftp.c
@@ -24,7 +24,7 @@
24 24
25#include "includes.h" 25#include "includes.h"
26 26
27RCSID("$OpenBSD: sftp.c,v 1.25 2002/02/06 14:27:23 mpech Exp $"); 27RCSID("$OpenBSD: sftp.c,v 1.26 2002/02/12 12:32:27 djm Exp $");
28 28
29/* XXX: short-form remote directory listings (like 'ls -C') */ 29/* XXX: short-form remote directory listings (like 'ls -C') */
30 30
@@ -47,6 +47,7 @@ char *__progname;
47 47
48FILE* infile; 48FILE* infile;
49size_t copy_buffer_len = 32768; 49size_t copy_buffer_len = 32768;
50size_t num_requests = 16;
50 51
51static void 52static void
52connect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid) 53connect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid)
@@ -125,7 +126,7 @@ main(int argc, char **argv)
125 ll = SYSLOG_LEVEL_INFO; 126 ll = SYSLOG_LEVEL_INFO;
126 infile = stdin; /* Read from STDIN unless changed by -b */ 127 infile = stdin; /* Read from STDIN unless changed by -b */
127 128
128 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:")) != -1) { 129 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
129 switch (ch) { 130 switch (ch) {
130 case 'C': 131 case 'C':
131 addargs(&args, "-C"); 132 addargs(&args, "-C");
@@ -168,6 +169,12 @@ main(int argc, char **argv)
168 if (copy_buffer_len == 0 || *cp != '\0') 169 if (copy_buffer_len == 0 || *cp != '\0')
169 fatal("Invalid buffer size \"%s\"", optarg); 170 fatal("Invalid buffer size \"%s\"", optarg);
170 break; 171 break;
172 case 'R':
173 num_requests = strtol(optarg, &cp, 10);
174 if (num_requests == 0 || *cp != '\0')
175 fatal("Invalid number of requests \"%s\"",
176 optarg);
177 break;
171 case 'h': 178 case 'h':
172 default: 179 default:
173 usage(); 180 usage();