mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
Preparatory work for allowing PSCP to work over SFTP as well as old-
style scp1. I've built a layer of abstraction covering all the gory details of the old scp network protocol. [originally from svn r1204]
This commit is contained in:
parent
9c5951ed35
commit
f7f96066f7
322
scp.c
322
scp.c
@ -286,7 +286,7 @@ void fatalbox(char *fmt, ...)
|
|||||||
char str[0x100]; /* Make the size big enough */
|
char str[0x100]; /* Make the size big enough */
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
strcpy(str, "Fatal:");
|
strcpy(str, "Fatal: ");
|
||||||
vsprintf(str + strlen(str), fmt, ap);
|
vsprintf(str + strlen(str), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
strcat(str, "\n");
|
strcat(str, "\n");
|
||||||
@ -309,7 +309,7 @@ void connection_fatal(char *fmt, ...)
|
|||||||
char str[0x100]; /* Make the size big enough */
|
char str[0x100]; /* Make the size big enough */
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
strcpy(str, "Fatal:");
|
strcpy(str, "Fatal: ");
|
||||||
vsprintf(str + strlen(str), fmt, ap);
|
vsprintf(str + strlen(str), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
strcat(str, "\n");
|
strcat(str, "\n");
|
||||||
@ -473,7 +473,7 @@ static void bump(char *fmt, ...)
|
|||||||
char str[0x100]; /* Make the size big enough */
|
char str[0x100]; /* Make the size big enough */
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
strcpy(str, "Fatal:");
|
strcpy(str, "Fatal: ");
|
||||||
vsprintf(str + strlen(str), fmt, ap);
|
vsprintf(str + strlen(str), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
strcat(str, "\n");
|
strcat(str, "\n");
|
||||||
@ -708,7 +708,185 @@ static int response(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* ----------------------------------------------------------------------
|
||||||
|
* Helper routines that contain the actual SCP protocol elements,
|
||||||
|
* so they can be switched to use SFTP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int scp_send_errmsg(char *str)
|
||||||
|
{
|
||||||
|
back->send("\001", 1); /* scp protocol error prefix */
|
||||||
|
back->send(str, strlen(str));
|
||||||
|
return 0; /* can't fail */
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_filetimes(unsigned long mtime, unsigned long atime)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
|
||||||
|
back->send(buf, strlen(buf));
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_filename(char *name, unsigned long size, int modes)
|
||||||
|
{
|
||||||
|
char buf[40];
|
||||||
|
sprintf(buf, "C%04o %lu ", modes, size);
|
||||||
|
back->send(buf, strlen(buf));
|
||||||
|
back->send(name, strlen(name));
|
||||||
|
back->send("\n", 1);
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_filedata(char *data, int len)
|
||||||
|
{
|
||||||
|
int bufsize = back->send(data, len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the network transfer is backing up - that is, the remote
|
||||||
|
* site is not accepting data as fast as we can produce it -
|
||||||
|
* then we must loop on network events until we have space in
|
||||||
|
* the buffer again.
|
||||||
|
*/
|
||||||
|
while (bufsize > MAX_SCP_BUFSIZE) {
|
||||||
|
if (!scp_process_network_event())
|
||||||
|
return 1;
|
||||||
|
bufsize = back->sendbuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_finish(void)
|
||||||
|
{
|
||||||
|
back->send("", 1);
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_dirname(char *name, int modes)
|
||||||
|
{
|
||||||
|
char buf[40];
|
||||||
|
sprintf(buf, "D%04o 0 ", modes);
|
||||||
|
back->send(buf, strlen(buf));
|
||||||
|
back->send(name, strlen(name));
|
||||||
|
back->send("\n", 1);
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_send_enddir(void)
|
||||||
|
{
|
||||||
|
back->send("E\n", 2);
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_sink_init(void)
|
||||||
|
{
|
||||||
|
back->send("", 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SCP_SINK_FILE 1
|
||||||
|
#define SCP_SINK_DIR 2
|
||||||
|
#define SCP_SINK_ENDDIR 3
|
||||||
|
struct scp_sink_action {
|
||||||
|
int action; /* FILE, DIR, ENDDIR */
|
||||||
|
char *buf; /* will need freeing after use */
|
||||||
|
char *name; /* filename or dirname (not ENDDIR) */
|
||||||
|
int mode; /* access mode (not ENDDIR) */
|
||||||
|
unsigned long size; /* file size (not ENDDIR) */
|
||||||
|
int settime; /* 1 if atime and mtime are filled */
|
||||||
|
unsigned long atime, mtime; /* access times for the file */
|
||||||
|
};
|
||||||
|
|
||||||
|
int scp_get_sink_action(struct scp_sink_action *act)
|
||||||
|
{
|
||||||
|
int done = 0;
|
||||||
|
int i, bufsize;
|
||||||
|
int action;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
act->settime = 0;
|
||||||
|
act->buf = NULL;
|
||||||
|
bufsize = 0;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
if (ssh_scp_recv(&ch, 1) <= 0)
|
||||||
|
return 1;
|
||||||
|
if (ch == '\n')
|
||||||
|
bump("Protocol error: Unexpected newline");
|
||||||
|
i = 0;
|
||||||
|
action = ch;
|
||||||
|
do {
|
||||||
|
if (ssh_scp_recv(&ch, 1) <= 0)
|
||||||
|
bump("Lost connection");
|
||||||
|
if (i >= bufsize) {
|
||||||
|
bufsize = i + 128;
|
||||||
|
act->buf = srealloc(act->buf, bufsize);
|
||||||
|
}
|
||||||
|
act->buf[i++] = ch;
|
||||||
|
} while (ch != '\n');
|
||||||
|
act->buf[i - 1] = '\0';
|
||||||
|
switch (action) {
|
||||||
|
case '\01': /* error */
|
||||||
|
tell_user(stderr, "%s\n", act->buf);
|
||||||
|
errs++;
|
||||||
|
continue; /* go round again */
|
||||||
|
case '\02': /* fatal error */
|
||||||
|
bump("%s", act->buf);
|
||||||
|
case 'E':
|
||||||
|
back->send("", 1);
|
||||||
|
act->action = SCP_SINK_ENDDIR;
|
||||||
|
return 0;
|
||||||
|
case 'T':
|
||||||
|
if (sscanf(act->buf, "%ld %*d %ld %*d",
|
||||||
|
&act->mtime, &act->atime) == 2) {
|
||||||
|
act->settime = 1;
|
||||||
|
back->send("", 1);
|
||||||
|
continue; /* go round again */
|
||||||
|
}
|
||||||
|
bump("Protocol error: Illegal time format");
|
||||||
|
case 'C':
|
||||||
|
case 'D':
|
||||||
|
act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bump("Protocol error: Expected control record");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We will go round this loop only once, unless we hit
|
||||||
|
* `continue' above.
|
||||||
|
*/
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we get here, we must have seen SCP_SINK_FILE or
|
||||||
|
* SCP_SINK_DIR.
|
||||||
|
*/
|
||||||
|
if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
|
||||||
|
bump("Protocol error: Illegal file descriptor format");
|
||||||
|
act->name = act->buf + i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_accept_filexfer(void)
|
||||||
|
{
|
||||||
|
back->send("", 1);
|
||||||
|
return 0; /* can't fail */
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_recv_filedata(char *data, int len)
|
||||||
|
{
|
||||||
|
return ssh_scp_recv(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int scp_finish_filerecv(void)
|
||||||
|
{
|
||||||
|
back->send("", 1);
|
||||||
|
return response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
* Send an error message to the other side and to the screen.
|
* Send an error message to the other side and to the screen.
|
||||||
* Increment error counter.
|
* Increment error counter.
|
||||||
*/
|
*/
|
||||||
@ -721,8 +899,7 @@ static void run_err(const char *fmt, ...)
|
|||||||
strcpy(str, "scp: ");
|
strcpy(str, "scp: ");
|
||||||
vsprintf(str + strlen(str), fmt, ap);
|
vsprintf(str + strlen(str), fmt, ap);
|
||||||
strcat(str, "\n");
|
strcat(str, "\n");
|
||||||
back->send("\001", 1); /* scp protocol error prefix */
|
scp_send_errmsg(str);
|
||||||
back->send(str, strlen(str));
|
|
||||||
tell_user(stderr, "%s", str);
|
tell_user(stderr, "%s", str);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
@ -792,18 +969,14 @@ static void source(char *src)
|
|||||||
GetFileTime(f, NULL, &actime, &wrtime);
|
GetFileTime(f, NULL, &actime, &wrtime);
|
||||||
TIME_WIN_TO_POSIX(actime, atime);
|
TIME_WIN_TO_POSIX(actime, atime);
|
||||||
TIME_WIN_TO_POSIX(wrtime, mtime);
|
TIME_WIN_TO_POSIX(wrtime, mtime);
|
||||||
sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
|
if (scp_send_filetimes(mtime, atime))
|
||||||
back->send(buf, strlen(buf));
|
|
||||||
if (response())
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = GetFileSize(f, NULL);
|
size = GetFileSize(f, NULL);
|
||||||
sprintf(buf, "C0644 %lu %s\n", size, last);
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
tell_user(stderr, "Sending file modes: %s", buf);
|
tell_user(stderr, "Sending file %s, size=%lu", last, size);
|
||||||
back->send(buf, strlen(buf));
|
if (scp_send_filename(last, size, 0644))
|
||||||
if (response())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
stat_bytes = 0;
|
stat_bytes = 0;
|
||||||
@ -813,7 +986,6 @@ static void source(char *src)
|
|||||||
for (i = 0; i < size; i += 4096) {
|
for (i = 0; i < size; i += 4096) {
|
||||||
char transbuf[4096];
|
char transbuf[4096];
|
||||||
DWORD j, k = 4096;
|
DWORD j, k = 4096;
|
||||||
int bufsize;
|
|
||||||
|
|
||||||
if (i + k > size)
|
if (i + k > size)
|
||||||
k = size - i;
|
k = size - i;
|
||||||
@ -822,7 +994,9 @@ static void source(char *src)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
bump("%s: Read error", src);
|
bump("%s: Read error", src);
|
||||||
}
|
}
|
||||||
bufsize = back->send(transbuf, k);
|
if (scp_send_filedata(transbuf, k))
|
||||||
|
bump("%s: Network error occurred", src);
|
||||||
|
|
||||||
if (statistics) {
|
if (statistics) {
|
||||||
stat_bytes += k;
|
stat_bytes += k;
|
||||||
if (time(NULL) != stat_lasttime || i + k == size) {
|
if (time(NULL) != stat_lasttime || i + k == size) {
|
||||||
@ -832,22 +1006,10 @@ static void source(char *src)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If the network transfer is backing up - that is, the
|
|
||||||
* remote site is not accepting data as fast as we can
|
|
||||||
* produce it - then we must loop on network events until
|
|
||||||
* we have space in the buffer again.
|
|
||||||
*/
|
|
||||||
while (bufsize > MAX_SCP_BUFSIZE) {
|
|
||||||
if (!scp_process_network_event())
|
|
||||||
bump("%s: Network error occurred", src);
|
|
||||||
bufsize = back->sendbuffer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CloseHandle(f);
|
CloseHandle(f);
|
||||||
|
|
||||||
back->send("", 1);
|
(void) scp_send_finish();
|
||||||
(void) response();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -872,11 +1034,9 @@ static void rsource(char *src)
|
|||||||
|
|
||||||
/* maybe send filetime */
|
/* maybe send filetime */
|
||||||
|
|
||||||
sprintf(buf, "D0755 0 %s\n", last);
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
tell_user(stderr, "Entering directory: %s", buf);
|
tell_user(stderr, "Entering directory: %s", last);
|
||||||
back->send(buf, strlen(buf));
|
if (scp_send_dirname(last, 0755))
|
||||||
if (response())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sprintf(buf, "%s/*", src);
|
sprintf(buf, "%s/*", src);
|
||||||
@ -895,9 +1055,7 @@ static void rsource(char *src)
|
|||||||
}
|
}
|
||||||
FindClose(dir);
|
FindClose(dir);
|
||||||
|
|
||||||
sprintf(buf, "E\n");
|
(void) scp_send_enddir();
|
||||||
back->send(buf, strlen(buf));
|
|
||||||
(void) response();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -913,9 +1071,7 @@ static void sink(char *targ, char *src)
|
|||||||
int exists;
|
int exists;
|
||||||
DWORD attr;
|
DWORD attr;
|
||||||
HANDLE f;
|
HANDLE f;
|
||||||
unsigned long mtime, atime;
|
unsigned long received;
|
||||||
unsigned int mode;
|
|
||||||
unsigned long size, i;
|
|
||||||
int wrerror = 0;
|
int wrerror = 0;
|
||||||
unsigned long stat_bytes;
|
unsigned long stat_bytes;
|
||||||
time_t stat_starttime, stat_lasttime;
|
time_t stat_starttime, stat_lasttime;
|
||||||
@ -928,48 +1084,14 @@ static void sink(char *targ, char *src)
|
|||||||
if (targetshouldbedirectory && !targisdir)
|
if (targetshouldbedirectory && !targisdir)
|
||||||
bump("%s: Not a directory", targ);
|
bump("%s: Not a directory", targ);
|
||||||
|
|
||||||
back->send("", 1);
|
scp_sink_init();
|
||||||
while (1) {
|
while (1) {
|
||||||
settime = 0;
|
struct scp_sink_action act;
|
||||||
gottime:
|
if (scp_get_sink_action(&act))
|
||||||
if (ssh_scp_recv(&ch, 1) <= 0)
|
|
||||||
return;
|
return;
|
||||||
if (ch == '\n')
|
|
||||||
bump("Protocol error: Unexpected newline");
|
|
||||||
i = 0;
|
|
||||||
buf[i++] = ch;
|
|
||||||
do {
|
|
||||||
if (ssh_scp_recv(&ch, 1) <= 0)
|
|
||||||
bump("Lost connection");
|
|
||||||
buf[i++] = ch;
|
|
||||||
} while (i < sizeof(buf) && ch != '\n');
|
|
||||||
buf[i - 1] = '\0';
|
|
||||||
switch (buf[0]) {
|
|
||||||
case '\01': /* error */
|
|
||||||
tell_user(stderr, "%s\n", buf + 1);
|
|
||||||
errs++;
|
|
||||||
continue;
|
|
||||||
case '\02': /* fatal error */
|
|
||||||
bump("%s", buf + 1);
|
|
||||||
case 'E':
|
|
||||||
back->send("", 1);
|
|
||||||
return;
|
|
||||||
case 'T':
|
|
||||||
if (sscanf(buf, "T%ld %*d %ld %*d", &mtime, &atime) == 2) {
|
|
||||||
settime = 1;
|
|
||||||
back->send("", 1);
|
|
||||||
goto gottime;
|
|
||||||
}
|
|
||||||
bump("Protocol error: Illegal time format");
|
|
||||||
case 'C':
|
|
||||||
case 'D':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
bump("Protocol error: Expected control record");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sscanf(buf + 1, "%u %lu %[^\n]", &mode, &size, namebuf) != 3)
|
if (act.action == SCP_SINK_ENDDIR)
|
||||||
bump("Protocol error: Illegal file descriptor format");
|
return;
|
||||||
/* Security fix: ensure the file ends up where we asked for it. */
|
/* Security fix: ensure the file ends up where we asked for it. */
|
||||||
if (targisdir) {
|
if (targisdir) {
|
||||||
char t[2048];
|
char t[2048];
|
||||||
@ -977,8 +1099,8 @@ static void sink(char *targ, char *src)
|
|||||||
strcpy(t, targ);
|
strcpy(t, targ);
|
||||||
if (targ[0] != '\0')
|
if (targ[0] != '\0')
|
||||||
strcat(t, "/");
|
strcat(t, "/");
|
||||||
p = namebuf + strlen(namebuf);
|
p = act.name + strlen(act.name);
|
||||||
while (p > namebuf && p[-1] != '/' && p[-1] != '\\')
|
while (p > act.name && p[-1] != '/' && p[-1] != '\\')
|
||||||
p--;
|
p--;
|
||||||
strcat(t, p);
|
strcat(t, p);
|
||||||
strcpy(namebuf, t);
|
strcpy(namebuf, t);
|
||||||
@ -988,7 +1110,7 @@ static void sink(char *targ, char *src)
|
|||||||
attr = GetFileAttributes(namebuf);
|
attr = GetFileAttributes(namebuf);
|
||||||
exists = (attr != (DWORD) - 1);
|
exists = (attr != (DWORD) - 1);
|
||||||
|
|
||||||
if (buf[0] == 'D') {
|
if (act.action == SCP_SINK_DIR) {
|
||||||
if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
if (exists && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||||
run_err("%s: Not a directory", namebuf);
|
run_err("%s: Not a directory", namebuf);
|
||||||
continue;
|
continue;
|
||||||
@ -1011,7 +1133,8 @@ static void sink(char *targ, char *src)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
back->send("", 1);
|
if (scp_accept_filexfer())
|
||||||
|
return;
|
||||||
|
|
||||||
stat_bytes = 0;
|
stat_bytes = 0;
|
||||||
stat_starttime = time(NULL);
|
stat_starttime = time(NULL);
|
||||||
@ -1023,17 +1146,22 @@ static void sink(char *targ, char *src)
|
|||||||
if (strrchr(stat_name, '\\') != NULL)
|
if (strrchr(stat_name, '\\') != NULL)
|
||||||
stat_name = strrchr(stat_name, '\\') + 1;
|
stat_name = strrchr(stat_name, '\\') + 1;
|
||||||
|
|
||||||
for (i = 0; i < size; i += 4096) {
|
received = 0;
|
||||||
|
while (received < act.size) {
|
||||||
char transbuf[4096];
|
char transbuf[4096];
|
||||||
DWORD j, k = 4096;
|
DWORD blksize, read, written;
|
||||||
if (i + k > size)
|
blksize = 4096;
|
||||||
k = size - i;
|
if (blksize > act.size - received)
|
||||||
if (ssh_scp_recv(transbuf, k) == 0)
|
blksize = act.size - received;
|
||||||
|
read = scp_recv_filedata(transbuf, blksize);
|
||||||
|
if (read <= 0)
|
||||||
bump("Lost connection");
|
bump("Lost connection");
|
||||||
if (wrerror)
|
if (wrerror)
|
||||||
continue;
|
continue;
|
||||||
if (!WriteFile(f, transbuf, k, &j, NULL) || j != k) {
|
if (!WriteFile(f, transbuf, read, &written, NULL) ||
|
||||||
|
written != read) {
|
||||||
wrerror = 1;
|
wrerror = 1;
|
||||||
|
/* FIXME: in sftp we can actually abort the transfer */
|
||||||
if (statistics)
|
if (statistics)
|
||||||
printf("\r%-25.25s | %50s\n",
|
printf("\r%-25.25s | %50s\n",
|
||||||
stat_name,
|
stat_name,
|
||||||
@ -1041,20 +1169,20 @@ static void sink(char *targ, char *src)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (statistics) {
|
if (statistics) {
|
||||||
stat_bytes += k;
|
stat_bytes += read;
|
||||||
if (time(NULL) > stat_lasttime || i + k == size) {
|
if (time(NULL) > stat_lasttime ||
|
||||||
|
received + read == act.size) {
|
||||||
stat_lasttime = time(NULL);
|
stat_lasttime = time(NULL);
|
||||||
print_stats(stat_name, size, stat_bytes,
|
print_stats(stat_name, act.size, stat_bytes,
|
||||||
stat_starttime, stat_lasttime);
|
stat_starttime, stat_lasttime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
received += read;
|
||||||
}
|
}
|
||||||
(void) response();
|
if (act.settime) {
|
||||||
|
|
||||||
if (settime) {
|
|
||||||
FILETIME actime, wrtime;
|
FILETIME actime, wrtime;
|
||||||
TIME_POSIX_TO_WIN(atime, actime);
|
TIME_POSIX_TO_WIN(act.atime, actime);
|
||||||
TIME_POSIX_TO_WIN(mtime, wrtime);
|
TIME_POSIX_TO_WIN(act.mtime, wrtime);
|
||||||
SetFileTime(f, NULL, &actime, &wrtime);
|
SetFileTime(f, NULL, &actime, &wrtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1063,12 +1191,12 @@ static void sink(char *targ, char *src)
|
|||||||
run_err("%s: Write error", namebuf);
|
run_err("%s: Write error", namebuf);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
back->send("", 1);
|
(void) scp_finish_filerecv();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We will copy local files to a remote server.
|
* We will copy local files to a remote server.
|
||||||
*/
|
*/
|
||||||
static void toremote(int argc, char *argv[])
|
static void toremote(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user