On Mon, 12 Dec 2022, Chris Rapier wrote: > The value tests should probably be 'val <= 0' as opposed to 'val == 0'. Also > you might want to have the buffer size check for a maximum value. With > overhead, 256K triggers an "Outbound buffer too long" error. 255K seems to > work but I haven't done the exact math on that one yet. Lastly, if nrequests > is set to a ridiculous size (on my system more than 150000) it ends up sucking > up a lot of memory and stalls. I think that's a buyer beware sort of thing but > I just wanted to let you know. > > Also, not defining the sftp_copy_buflen and sftp_nrequests means that in > do_sftp_connect the line > > return do_init(*reminp, *remoutp, > sftp_copy_buflen, sftp_nrequests, limit_kbps); > > Ends up with 0's in there for the buflen and requests. What sort of behaviour > does that create on the sftp server? It seems to just pull out the stops. No, if you look at do_init() then it uses default values of 32K buffer size and 64 concurrent requests. This updates the diff to use scan_scaled() and strtonum() and refuses negative values. diff --git a/scp.1 b/scp.1 index cd23f97..a98df59 100644 --- a/scp.1 +++ b/scp.1 @@ -28,6 +28,7 @@ .Op Fl o Ar ssh_option .Op Fl P Ar port .Op Fl S Ar program +.Op Fl X Ar sftp_option .Ar source ... target .Sh DESCRIPTION .Nm @@ -278,6 +279,19 @@ and to print debugging messages about their progress. This is helpful in debugging connection, authentication, and configuration problems. +.It Fl X Ar sftp_option +Specify an option that controls aspects of SFTP protocol behaviour. +The valid options are: +.Bl -tag -width Ds +.It Cm nrequests Ns = Ns Ar value +Controls how many concurrent SFTP read or write requests may be in progress +at any point in time during a download or upload. +By default 64 requests may be active concurrently. +.It Cm buffer Ns = Ns Ar value +Controls the maximum buffer size for a single SFTP read/write operation used +during download or upload. +By default a 32KB buffer is used. +.El .El .Sh EXIT STATUS .Ex -std scp diff --git a/scp.c b/scp.c index c7194c2..25c8d38 100644 --- a/scp.c +++ b/scp.c @@ -96,6 +96,7 @@ #include <time.h> #include <unistd.h> #include <limits.h> +#include <util.h> #include <vis.h> #include "xmalloc.h" @@ -150,6 +151,10 @@ char *ssh_program = _PATH_SSH_PROGRAM; pid_t do_cmd_pid = -1; pid_t do_cmd_pid2 = -1; +/* SFTP copy parameters */ +size_t sftp_copy_buflen; +size_t sftp_nrequests; + /* Needed for sftp */ volatile sig_atomic_t interrupted = 0; @@ -418,13 +423,14 @@ void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, int main(int argc, char **argv) { - int ch, fflag, tflag, status, n; + int ch, fflag, tflag, status, r, n; char **newargv, *argv0; const char *errstr; extern char *optarg; extern int optind; enum scp_mode_e mode = MODE_SFTP; char *sftp_direct = NULL; + long long llv; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -452,7 +458,7 @@ main(int argc, char **argv) fflag = Tflag = tflag = 0; while ((ch = getopt(argc, argv, - "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:")) != -1) { + "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:X:")) != -1) { switch (ch) { /* User-visible flags. */ case '1': @@ -533,6 +539,31 @@ main(int argc, char **argv) addargs(&remote_remote_args, "-q"); showprogress = 0; break; + case 'X': + /* Please keep in sync with sftp.c -X */ + if (strncmp(optarg, "buffer=", 7) == 0) { + r = scan_scaled(optarg + 7, &llv); + if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { + r = -1; + errno = EINVAL; + } + if (r == -1) { + fatal("Invalid buffer size \"%s\": %s", + optarg + 7, strerror(errno)); + } + sftp_copy_buflen = (size_t)llv; + } else if (strncmp(optarg, "nrequests=", 10) == 0) { + llv = strtonum(optarg + 10, 1, 256 * 1024, + &errstr); + if (errstr != NULL) { + fatal("Invalid number of requests " + "\"%s\": %s", optarg + 10, errstr); + } + sftp_nrequests = (size_t)llv; + } else { + fatal("Invalid -X option"); + } + break; /* Server options. */ case 'd': @@ -941,7 +972,8 @@ do_sftp_connect(char *host, char *user, int port, char *sftp_direct, reminp, remoutp, pidp) < 0) return NULL; } - return do_init(*reminp, *remoutp, 32768, 64, limit_kbps); + return do_init(*reminp, *remoutp, + sftp_copy_buflen, sftp_nrequests, limit_kbps); } void diff --git a/sftp-client.c b/sftp-client.c index a3e7a5d..1907753 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -55,10 +55,10 @@ extern volatile sig_atomic_t interrupted; extern int showprogress; -/* Default size of buffer for up/download */ +/* Default size of buffer for up/download (fix sftp.1 scp.1 if changed) */ #define DEFAULT_COPY_BUFLEN 32768 -/* Default number of concurrent outstanding requests */ +/* Default number of concurrent xfer requests (fix sftp.1 scp.1 if changed) */ #define DEFAULT_NUM_REQUESTS 64 /* Minimum amount of data to read at a time */ diff --git a/sftp.1 b/sftp.1 index 3b3f2c5..3b430c5 100644 --- a/sftp.1 +++ b/sftp.1 @@ -44,6 +44,7 @@ .Op Fl R Ar num_requests .Op Fl S Ar program .Op Fl s Ar subsystem | sftp_server +.Op Fl X Ar sftp_option .Ar destination .Sh DESCRIPTION .Nm @@ -320,6 +321,19 @@ does not have an sftp subsystem configured. .It Fl v Raise logging level. This option is also passed to ssh. +.It Fl X Ar sftp_option +Specify an option that controls aspects of SFTP protocol behaviour. +The valid options are: +.Bl -tag -width Ds +.It Cm nrequests Ns = Ns Ar value +Controls how many concurrent SFTP read or write requests may be in progress +at any point in time during a download or upload. +By default 64 requests may be active concurrently. +.It Cm buffer Ns = Ns Ar value +Controls the maximum buffer size for a single SFTP read/write operation used +during download or upload. +By default a 32KB buffer is used. +.El .El .Sh INTERACTIVE COMMANDS Once in interactive mode, diff --git a/sftp.c b/sftp.c index 02547f6..6dc52a3 100644 --- a/sftp.c +++ b/sftp.c @@ -2383,7 +2383,7 @@ main(int argc, char **argv) struct sftp_conn *conn; size_t copy_buffer_len = 0; size_t num_requests = 0; - long long limit_kbps = 0; + long long llv, limit_kbps = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -2400,7 +2400,7 @@ main(int argc, char **argv) infile = stdin; while ((ch = getopt(argc, argv, - "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) { + "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case 'A': @@ -2497,6 +2497,31 @@ main(int argc, char **argv) ssh_program = optarg; replacearg(&args, 0, "%s", ssh_program); break; + case 'X': + /* Please keep in sync with ssh.c -X */ + if (strncmp(optarg, "buffer=", 7) == 0) { + r = scan_scaled(optarg + 7, &llv); + if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { + r = -1; + errno = EINVAL; + } + if (r == -1) { + fatal("Invalid buffer size \"%s\": %s", + optarg + 7, strerror(errno)); + } + copy_buffer_len = (size_t)llv; + } else if (strncmp(optarg, "nrequests=", 10) == 0) { + llv = strtonum(optarg + 10, 1, 256 * 1024, + &errstr); + if (errstr != NULL) { + fatal("Invalid number of requests " + "\"%s\": %s", optarg + 10, errstr); + } + num_requests = (size_t)llv; + } else { + fatal("Invalid -X option"); + } + break; case 'h': default: usage(); _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev