version 1.31, 2002/04/06 00:30:08 |
version 1.31.2.3, 2003/04/03 22:35:17 |
|
|
/* |
/* |
* Copyright (c) 2001,2002 Damien Miller. All rights reserved. |
* Copyright (c) 2001-2003 Damien Miller. All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "log.h" |
#include "log.h" |
#include "atomicio.h" |
#include "atomicio.h" |
|
#include "progressmeter.h" |
|
|
#include "sftp.h" |
#include "sftp.h" |
#include "sftp-common.h" |
#include "sftp-common.h" |
#include "sftp-client.h" |
#include "sftp-client.h" |
|
|
|
extern int showprogress; |
|
|
/* Minimum amount of data to read at at time */ |
/* Minimum amount of data to read at at time */ |
#define MIN_READ_SIZE 512 |
#define MIN_READ_SIZE 512 |
|
|
|
/* Maximum packet size */ |
|
#define MAX_MSG_LENGTH (256 * 1024) |
|
|
struct sftp_conn { |
struct sftp_conn { |
int fd_in; |
int fd_in; |
int fd_out; |
int fd_out; |
|
|
static void |
static void |
send_msg(int fd, Buffer *m) |
send_msg(int fd, Buffer *m) |
{ |
{ |
int mlen = buffer_len(m); |
u_char mlen[4]; |
int len; |
|
Buffer oqueue; |
|
|
|
buffer_init(&oqueue); |
if (buffer_len(m) > MAX_MSG_LENGTH) |
buffer_put_int(&oqueue, mlen); |
fatal("Outbound message too long %u", buffer_len(m)); |
buffer_append(&oqueue, buffer_ptr(m), mlen); |
|
buffer_consume(m, mlen); |
|
|
|
len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); |
/* Send length first */ |
if (len <= 0) |
PUT_32BIT(mlen, buffer_len(m)); |
|
if (atomicio(write, fd, mlen, sizeof(mlen)) <= 0) |
fatal("Couldn't send packet: %s", strerror(errno)); |
fatal("Couldn't send packet: %s", strerror(errno)); |
|
|
buffer_free(&oqueue); |
if (atomicio(write, fd, buffer_ptr(m), buffer_len(m)) <= 0) |
|
fatal("Couldn't send packet: %s", strerror(errno)); |
|
|
|
buffer_clear(m); |
} |
} |
|
|
static void |
static void |
get_msg(int fd, Buffer *m) |
get_msg(int fd, Buffer *m) |
{ |
{ |
u_int len, msg_len; |
ssize_t len; |
unsigned char buf[4096]; |
u_int msg_len; |
|
|
len = atomicio(read, fd, buf, 4); |
buffer_append_space(m, 4); |
|
len = atomicio(read, fd, buffer_ptr(m), 4); |
if (len == 0) |
if (len == 0) |
fatal("Connection closed"); |
fatal("Connection closed"); |
else if (len == -1) |
else if (len == -1) |
fatal("Couldn't read packet: %s", strerror(errno)); |
fatal("Couldn't read packet: %s", strerror(errno)); |
|
|
msg_len = GET_32BIT(buf); |
msg_len = buffer_get_int(m); |
if (msg_len > 256 * 1024) |
if (msg_len > MAX_MSG_LENGTH) |
fatal("Received message too long %d", msg_len); |
fatal("Received message too long %u", msg_len); |
|
|
while (msg_len) { |
buffer_append_space(m, msg_len); |
len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); |
len = atomicio(read, fd, buffer_ptr(m), msg_len); |
if (len == 0) |
if (len == 0) |
fatal("Connection closed"); |
fatal("Connection closed"); |
else if (len == -1) |
else if (len == -1) |
fatal("Couldn't read packet: %s", strerror(errno)); |
fatal("Read packet: %s", strerror(errno)); |
|
|
msg_len -= len; |
|
buffer_append(m, buf, len); |
|
} |
|
} |
} |
|
|
static void |
static void |
|
|
buffer_put_int(&msg, id); |
buffer_put_int(&msg, id); |
buffer_put_string(&msg, s, len); |
buffer_put_string(&msg, s, len); |
send_msg(fd, &msg); |
send_msg(fd, &msg); |
debug3("Sent message fd %d T:%d I:%d", fd, code, id); |
debug3("Sent message fd %d T:%u I:%u", fd, code, id); |
buffer_free(&msg); |
buffer_free(&msg); |
} |
} |
|
|
|
|
buffer_put_string(&msg, s, len); |
buffer_put_string(&msg, s, len); |
encode_attrib(&msg, a); |
encode_attrib(&msg, a); |
send_msg(fd, &msg); |
send_msg(fd, &msg); |
debug3("Sent message fd %d T:%d I:%d", fd, code, id); |
debug3("Sent message fd %d T:%u I:%u", fd, code, id); |
buffer_free(&msg); |
buffer_free(&msg); |
} |
} |
|
|
static u_int |
static u_int |
get_status(int fd, int expected_id) |
get_status(int fd, u_int expected_id) |
{ |
{ |
Buffer msg; |
Buffer msg; |
u_int type, id, status; |
u_int type, id, status; |
|
|
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
if (type != SSH2_FXP_STATUS) |
if (type != SSH2_FXP_STATUS) |
fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", |
fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", |
SSH2_FXP_STATUS, type); |
SSH2_FXP_STATUS, type); |
|
|
status = buffer_get_int(&msg); |
status = buffer_get_int(&msg); |
buffer_free(&msg); |
buffer_free(&msg); |
|
|
debug3("SSH2_FXP_STATUS %d", status); |
debug3("SSH2_FXP_STATUS %u", status); |
|
|
return(status); |
return(status); |
} |
} |
|
|
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
if (type == SSH2_FXP_STATUS) { |
if (type == SSH2_FXP_STATUS) { |
int status = buffer_get_int(&msg); |
int status = buffer_get_int(&msg); |
|
|
error("Couldn't get handle: %s", fx2txt(status)); |
error("Couldn't get handle: %s", fx2txt(status)); |
return(NULL); |
return(NULL); |
} else if (type != SSH2_FXP_HANDLE) |
} else if (type != SSH2_FXP_HANDLE) |
fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d", |
fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", |
SSH2_FXP_HANDLE, type); |
SSH2_FXP_HANDLE, type); |
|
|
handle = buffer_get_string(&msg, len); |
handle = buffer_get_string(&msg, len); |
|
|
type = buffer_get_char(&msg); |
type = buffer_get_char(&msg); |
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
debug3("Received stat reply T:%d I:%d", type, id); |
debug3("Received stat reply T:%u I:%u", type, id); |
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
if (type == SSH2_FXP_STATUS) { |
if (type == SSH2_FXP_STATUS) { |
int status = buffer_get_int(&msg); |
int status = buffer_get_int(&msg); |
|
|
|
|
error("Couldn't stat remote file: %s", fx2txt(status)); |
error("Couldn't stat remote file: %s", fx2txt(status)); |
return(NULL); |
return(NULL); |
} else if (type != SSH2_FXP_ATTRS) { |
} else if (type != SSH2_FXP_ATTRS) { |
fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", |
fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", |
SSH2_FXP_ATTRS, type); |
SSH2_FXP_ATTRS, type); |
} |
} |
a = decode_attrib(&msg); |
a = decode_attrib(&msg); |
|
|
struct sftp_conn * |
struct sftp_conn * |
do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) |
do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) |
{ |
{ |
int type, version; |
u_int type; |
|
int version; |
Buffer msg; |
Buffer msg; |
struct sftp_conn *ret; |
struct sftp_conn *ret; |
|
|
|
|
|
|
/* Expecting a VERSION reply */ |
/* Expecting a VERSION reply */ |
if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { |
if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { |
error("Invalid packet back from SSH2_FXP_INIT (type %d)", |
error("Invalid packet back from SSH2_FXP_INIT (type %u)", |
type); |
type); |
buffer_free(&msg); |
buffer_free(&msg); |
return(NULL); |
return(NULL); |
|
|
buffer_put_int(&msg, id); |
buffer_put_int(&msg, id); |
buffer_put_string(&msg, handle, handle_len); |
buffer_put_string(&msg, handle, handle_len); |
send_msg(conn->fd_out, &msg); |
send_msg(conn->fd_out, &msg); |
debug3("Sent message SSH2_FXP_CLOSE I:%d", id); |
debug3("Sent message SSH2_FXP_CLOSE I:%u", id); |
|
|
status = get_status(conn->fd_in, id); |
status = get_status(conn->fd_in, id); |
if (status != SSH2_FX_OK) |
if (status != SSH2_FX_OK) |
|
|
|
|
id = expected_id = conn->msg_id++; |
id = expected_id = conn->msg_id++; |
|
|
debug3("Sending SSH2_FXP_READDIR I:%d", id); |
debug3("Sending SSH2_FXP_READDIR I:%u", id); |
|
|
buffer_clear(&msg); |
buffer_clear(&msg); |
buffer_put_char(&msg, SSH2_FXP_READDIR); |
buffer_put_char(&msg, SSH2_FXP_READDIR); |
|
|
type = buffer_get_char(&msg); |
type = buffer_get_char(&msg); |
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
debug3("Received reply T:%d I:%d", type, id); |
debug3("Received reply T:%u I:%u", type, id); |
|
|
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
|
|
if (type == SSH2_FXP_STATUS) { |
if (type == SSH2_FXP_STATUS) { |
int status = buffer_get_int(&msg); |
int status = buffer_get_int(&msg); |
|
|
error("Couldn't read directory: %s", |
error("Couldn't read directory: %s", |
fx2txt(status)); |
fx2txt(status)); |
do_close(conn, handle, handle_len); |
do_close(conn, handle, handle_len); |
|
xfree(handle); |
return(status); |
return(status); |
} |
} |
} else if (type != SSH2_FXP_NAME) |
} else if (type != SSH2_FXP_NAME) |
fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", |
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", |
SSH2_FXP_NAME, type); |
SSH2_FXP_NAME, type); |
|
|
count = buffer_get_int(&msg); |
count = buffer_get_int(&msg); |
|
|
} |
} |
|
|
int |
int |
do_ls(struct sftp_conn *conn, char *path) |
|
{ |
|
return(do_lsreaddir(conn, path, 1, NULL)); |
|
} |
|
|
|
int |
|
do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) |
do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) |
{ |
{ |
return(do_lsreaddir(conn, path, 0, dir)); |
return(do_lsreaddir(conn, path, 0, dir)); |
|
|
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
|
|
if (type == SSH2_FXP_STATUS) { |
if (type == SSH2_FXP_STATUS) { |
u_int status = buffer_get_int(&msg); |
u_int status = buffer_get_int(&msg); |
|
|
error("Couldn't canonicalise: %s", fx2txt(status)); |
error("Couldn't canonicalise: %s", fx2txt(status)); |
return(NULL); |
return(NULL); |
} else if (type != SSH2_FXP_NAME) |
} else if (type != SSH2_FXP_NAME) |
fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", |
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", |
SSH2_FXP_NAME, type); |
SSH2_FXP_NAME, type); |
|
|
count = buffer_get_int(&msg); |
count = buffer_get_int(&msg); |
|
|
|
|
status = get_status(conn->fd_in, id); |
status = get_status(conn->fd_in, id); |
if (status != SSH2_FX_OK) |
if (status != SSH2_FX_OK) |
error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, |
error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, |
newpath, fx2txt(status)); |
newpath, fx2txt(status)); |
|
|
return(status); |
return(status); |
|
|
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
|
|
if (id != expected_id) |
if (id != expected_id) |
fatal("ID mismatch (%d != %d)", id, expected_id); |
fatal("ID mismatch (%u != %u)", id, expected_id); |
|
|
if (type == SSH2_FXP_STATUS) { |
if (type == SSH2_FXP_STATUS) { |
u_int status = buffer_get_int(&msg); |
u_int status = buffer_get_int(&msg); |
|
|
error("Couldn't readlink: %s", fx2txt(status)); |
error("Couldn't readlink: %s", fx2txt(status)); |
return(NULL); |
return(NULL); |
} else if (type != SSH2_FXP_NAME) |
} else if (type != SSH2_FXP_NAME) |
fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", |
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", |
SSH2_FXP_NAME, type); |
SSH2_FXP_NAME, type); |
|
|
count = buffer_get_int(&msg); |
count = buffer_get_int(&msg); |
|
|
int read_error, write_errno; |
int read_error, write_errno; |
u_int64_t offset, size; |
u_int64_t offset, size; |
u_int handle_len, mode, type, id, buflen; |
u_int handle_len, mode, type, id, buflen; |
|
off_t progress_counter; |
struct request { |
struct request { |
u_int id; |
u_int id; |
u_int len; |
u_int len; |
|
|
|
|
/* XXX: should we preserve set[ug]id? */ |
/* XXX: should we preserve set[ug]id? */ |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) |
mode = S_IWRITE | (a->perm & 0777); |
mode = a->perm & 0777; |
else |
else |
mode = 0666; |
mode = 0666; |
|
|
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && |
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && |
(a->perm & S_IFDIR)) { |
(!S_ISREG(a->perm))) { |
error("Cannot download a directory: %s", remote_path); |
error("Cannot download non-regular file: %s", remote_path); |
return(-1); |
return(-1); |
} |
} |
|
|
|
|
attrib_clear(&junk); /* Send empty attributes */ |
attrib_clear(&junk); /* Send empty attributes */ |
encode_attrib(&msg, &junk); |
encode_attrib(&msg, &junk); |
send_msg(conn->fd_out, &msg); |
send_msg(conn->fd_out, &msg); |
debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); |
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); |
|
|
handle = get_handle(conn->fd_in, id, &handle_len); |
handle = get_handle(conn->fd_in, id, &handle_len); |
if (handle == NULL) { |
if (handle == NULL) { |
|
|
return(-1); |
return(-1); |
} |
} |
|
|
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); |
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, |
|
mode | S_IWRITE); |
if (local_fd == -1) { |
if (local_fd == -1) { |
error("Couldn't open local file \"%s\" for writing: %s", |
error("Couldn't open local file \"%s\" for writing: %s", |
local_path, strerror(errno)); |
local_path, strerror(errno)); |
|
|
/* Read from remote and write to local */ |
/* Read from remote and write to local */ |
write_error = read_error = write_errno = num_req = offset = 0; |
write_error = read_error = write_errno = num_req = offset = 0; |
max_req = 1; |
max_req = 1; |
|
progress_counter = 0; |
|
|
|
if (showprogress) { |
|
if (size) |
|
start_progress_meter(remote_path, size, |
|
&progress_counter); |
|
else |
|
printf("Fetching %s to %s\n", remote_path, local_path); |
|
} |
|
|
while (num_req > 0 || max_req > 0) { |
while (num_req > 0 || max_req > 0) { |
char *data; |
char *data; |
u_int len; |
u_int len; |
|
|
get_msg(conn->fd_in, &msg); |
get_msg(conn->fd_in, &msg); |
type = buffer_get_char(&msg); |
type = buffer_get_char(&msg); |
id = buffer_get_int(&msg); |
id = buffer_get_int(&msg); |
debug3("Received reply T:%d I:%d R:%d", type, id, max_req); |
debug3("Received reply T:%u I:%u R:%d", type, id, max_req); |
|
|
/* Find the request in our queue */ |
/* Find the request in our queue */ |
for(req = TAILQ_FIRST(&requests); |
for(req = TAILQ_FIRST(&requests); |
|
|
(unsigned long long)req->offset + len - 1); |
(unsigned long long)req->offset + len - 1); |
if (len > req->len) |
if (len > req->len) |
fatal("Received more data than asked for " |
fatal("Received more data than asked for " |
"%d > %d", len, req->len); |
"%u > %u", len, req->len); |
if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || |
if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || |
atomicio(write, local_fd, data, len) != len) && |
atomicio(write, local_fd, data, len) != len) && |
!write_error) { |
!write_error) { |
write_errno = errno; |
write_errno = errno; |
write_error = 1; |
write_error = 1; |
max_req = 0; |
max_req = 0; |
} |
} |
|
progress_counter += len; |
xfree(data); |
xfree(data); |
|
|
if (len == req->len) { |
if (len == req->len) { |
|
|
} |
} |
break; |
break; |
default: |
default: |
fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", |
fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", |
SSH2_FXP_DATA, type); |
SSH2_FXP_DATA, type); |
} |
} |
} |
} |
|
|
|
if (showprogress && size) |
|
stop_progress_meter(); |
|
|
/* Sanity check */ |
/* Sanity check */ |
if (TAILQ_FIRST(&requests) != NULL) |
if (TAILQ_FIRST(&requests) != NULL) |
fatal("Transfer complete, but requests still in queue"); |
fatal("Transfer complete, but requests still in queue"); |
|
|
/* Override umask and utimes if asked */ |
/* Override umask and utimes if asked */ |
if (pflag && fchmod(local_fd, mode) == -1) |
if (pflag && fchmod(local_fd, mode) == -1) |
error("Couldn't set mode on \"%s\": %s", local_path, |
error("Couldn't set mode on \"%s\": %s", local_path, |
strerror(errno)); |
strerror(errno)); |
if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { |
if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { |
struct timeval tv[2]; |
struct timeval tv[2]; |
tv[0].tv_sec = a->atime; |
tv[0].tv_sec = a->atime; |
|
|
tv[0].tv_usec = tv[1].tv_usec = 0; |
tv[0].tv_usec = tv[1].tv_usec = 0; |
if (utimes(local_path, tv) == -1) |
if (utimes(local_path, tv) == -1) |
error("Can't set times on \"%s\": %s", |
error("Can't set times on \"%s\": %s", |
local_path, strerror(errno)); |
local_path, strerror(errno)); |
} |
} |
} |
} |
close(local_fd); |
close(local_fd); |
|
|
close(local_fd); |
close(local_fd); |
return(-1); |
return(-1); |
} |
} |
|
if (!S_ISREG(sb.st_mode)) { |
|
error("%s is not a regular file", local_path); |
|
close(local_fd); |
|
return(-1); |
|
} |
stat_to_attrib(&sb, &a); |
stat_to_attrib(&sb, &a); |
|
|
a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; |
a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; |
|
|
buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); |
buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); |
encode_attrib(&msg, &a); |
encode_attrib(&msg, &a); |
send_msg(conn->fd_out, &msg); |
send_msg(conn->fd_out, &msg); |
debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); |
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); |
|
|
buffer_clear(&msg); |
buffer_clear(&msg); |
|
|
|
|
|
|
/* Read from local and write to remote */ |
/* Read from local and write to remote */ |
offset = 0; |
offset = 0; |
|
if (showprogress) |
|
start_progress_meter(local_path, sb.st_size, &offset); |
|
else |
|
printf("Uploading %s to %s\n", local_path, remote_path); |
|
|
for (;;) { |
for (;;) { |
int len; |
int len; |
|
|
|
|
buffer_put_int64(&msg, offset); |
buffer_put_int64(&msg, offset); |
buffer_put_string(&msg, data, len); |
buffer_put_string(&msg, data, len); |
send_msg(conn->fd_out, &msg); |
send_msg(conn->fd_out, &msg); |
debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", |
debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", |
id, (unsigned long long)offset, len); |
id, (unsigned long long)offset, len); |
} else if (TAILQ_FIRST(&acks) == NULL) |
} else if (TAILQ_FIRST(&acks) == NULL) |
break; |
break; |
|
|
|
|
|
|
if (id == startid || len == 0 || |
if (id == startid || len == 0 || |
id - ackid >= conn->num_requests) { |
id - ackid >= conn->num_requests) { |
u_int r_id; |
u_int r_id; |
|
|
buffer_clear(&msg); |
buffer_clear(&msg); |
get_msg(conn->fd_in, &msg); |
get_msg(conn->fd_in, &msg); |
|
|
ack = TAILQ_NEXT(ack, tq)) |
ack = TAILQ_NEXT(ack, tq)) |
; |
; |
if (ack == NULL) |
if (ack == NULL) |
fatal("Can't find request for ID %d", r_id); |
fatal("Can't find request for ID %u", r_id); |
TAILQ_REMOVE(&acks, ack, tq); |
TAILQ_REMOVE(&acks, ack, tq); |
|
|
if (status != SSH2_FX_OK) { |
if (status != SSH2_FX_OK) { |
error("Couldn't write to remote file \"%s\": %s", |
error("Couldn't write to remote file \"%s\": %s", |
remote_path, fx2txt(status)); |
remote_path, fx2txt(status)); |
do_close(conn, handle, handle_len); |
do_close(conn, handle, handle_len); |
close(local_fd); |
close(local_fd); |
|
xfree(data); |
|
xfree(ack); |
goto done; |
goto done; |
} |
} |
debug3("In write loop, ack for %u %d bytes at %llu", |
debug3("In write loop, ack for %u %u bytes at %llu", |
ack->id, ack->len, (unsigned long long)ack->offset); |
ack->id, ack->len, (unsigned long long)ack->offset); |
++ackid; |
++ackid; |
free(ack); |
xfree(ack); |
} |
} |
offset += len; |
offset += len; |
} |
} |
|
if (showprogress) |
|
stop_progress_meter(); |
xfree(data); |
xfree(data); |
|
|
if (close(local_fd) == -1) { |
if (close(local_fd) == -1) { |