version 1.35.2.2, 2003/09/16 21:20:27 |
version 1.36, 2002/11/05 20:10:37 |
|
|
/* |
/* |
* Copyright (c) 2001-2003 Damien Miller. All rights reserved. |
* Copyright (c) 2001,2002 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) |
{ |
{ |
u_char mlen[4]; |
int mlen = buffer_len(m); |
|
int len; |
|
Buffer oqueue; |
|
|
if (buffer_len(m) > MAX_MSG_LENGTH) |
buffer_init(&oqueue); |
fatal("Outbound message too long %u", buffer_len(m)); |
buffer_put_int(&oqueue, mlen); |
|
buffer_append(&oqueue, buffer_ptr(m), mlen); |
|
buffer_consume(m, mlen); |
|
|
/* Send length first */ |
len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); |
PUT_32BIT(mlen, buffer_len(m)); |
if (len <= 0) |
if (atomicio(vwrite, fd, mlen, sizeof(mlen)) <= 0) |
|
fatal("Couldn't send packet: %s", strerror(errno)); |
fatal("Couldn't send packet: %s", strerror(errno)); |
|
|
if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) <= 0) |
buffer_free(&oqueue); |
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) |
{ |
{ |
ssize_t len; |
u_int len, msg_len; |
u_int msg_len; |
unsigned char buf[4096]; |
|
|
buffer_append_space(m, 4); |
len = atomicio(read, fd, buf, 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 = buffer_get_int(m); |
msg_len = GET_32BIT(buf); |
if (msg_len > MAX_MSG_LENGTH) |
if (msg_len > 256 * 1024) |
fatal("Received message too long %u", msg_len); |
fatal("Received message too long %u", msg_len); |
|
|
buffer_append_space(m, msg_len); |
while (msg_len) { |
len = atomicio(read, fd, buffer_ptr(m), msg_len); |
len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); |
if (len == 0) |
if (len == 0) |
fatal("Connection closed"); |
fatal("Connection closed"); |
else if (len == -1) |
else if (len == -1) |
fatal("Read packet: %s", strerror(errno)); |
fatal("Couldn't read packet: %s", strerror(errno)); |
|
|
|
msg_len -= len; |
|
buffer_append(m, buf, len); |
|
} |
} |
} |
|
|
static void |
static void |
|
|
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) |
|
|
if (quiet) |
if (quiet) |
debug("Server version does not support lstat operation"); |
debug("Server version does not support lstat operation"); |
else |
else |
logit("Server version does not support lstat operation"); |
log("Server version does not support lstat operation"); |
return(do_stat(conn, path, quiet)); |
return(do_stat(conn, path, quiet)); |
} |
} |
|
|
|
|
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 = a->perm & 0777; |
mode = S_IWRITE | (a->perm & 0777); |
else |
else |
mode = 0666; |
mode = 0666; |
|
|
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && |
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && |
(!S_ISREG(a->perm))) { |
(a->perm & S_IFDIR)) { |
error("Cannot download non-regular file: %s", remote_path); |
error("Cannot download a directory: %s", remote_path); |
return(-1); |
return(-1); |
} |
} |
|
|
|
|
return(-1); |
return(-1); |
} |
} |
|
|
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, |
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); |
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; |
|
|
(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 " |
"%u > %u", 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(vwrite, 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) { |
|
|
} |
} |
} |
} |
|
|
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; |
|
|
|
|
/* 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_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:%u 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 (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 %u bytes at %llu", |
debug3("In write loop, ack for %u %u bytes at %llu", |
|
|
} |
} |
offset += len; |
offset += len; |
} |
} |
if (showprogress) |
|
stop_progress_meter(); |
|
xfree(data); |
xfree(data); |
|
|
if (close(local_fd) == -1) { |
if (close(local_fd) == -1) { |