/* XXX: copy between two remote sites */
#include "includes.h"
-RCSID("$OpenBSD: sftp-client.c,v 1.4 2001/02/06 23:30:28 djm Exp $");
+RCSID("$OpenBSD: sftp-client.c,v 1.18 2001/07/14 15:10:16 stevesk Exp $");
-#include "ssh.h"
#include "buffer.h"
#include "bufaux.h"
#include "getput.h"
#include "xmalloc.h"
#include "log.h"
#include "atomicio.h"
-#include "pathnames.h"
#include "sftp.h"
#include "sftp-common.h"
/* Message ID */
static u_int msg_id = 1;
-void
+static void
send_msg(int fd, Buffer *m)
{
int mlen = buffer_len(m);
buffer_free(&oqueue);
}
-void
+static void
get_msg(int fd, Buffer *m)
{
u_int len, msg_len;
unsigned char buf[4096];
len = atomicio(read, fd, buf, 4);
- if (len != 4)
+ if (len == 0)
+ fatal("Connection closed");
+ else if (len == -1)
fatal("Couldn't read packet: %s", strerror(errno));
msg_len = GET_32BIT(buf);
while (msg_len) {
len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
- if (len <= 0)
+ if (len == 0)
+ fatal("Connection closed");
+ else if (len == -1)
fatal("Couldn't read packet: %s", strerror(errno));
msg_len -= len;
}
}
-void
+static void
send_string_request(int fd, u_int id, u_int code, char *s,
u_int len)
{
buffer_free(&msg);
}
-void
+static void
send_string_attrs_request(int fd, u_int id, u_int code, char *s,
u_int len, Attrib *a)
{
buffer_free(&msg);
}
-u_int
+static u_int
get_status(int fd, int expected_id)
{
Buffer msg;
return(status);
}
-char *
+static char *
get_handle(int fd, u_int expected_id, u_int *len)
{
Buffer msg;
return(handle);
}
-Attrib *
-get_decode_stat(int fd, u_int expected_id)
+static Attrib *
+get_decode_stat(int fd, u_int expected_id, int quiet)
{
Buffer msg;
u_int type, id;
if (type == SSH2_FXP_STATUS) {
int status = buffer_get_int(&msg);
- error("Couldn't stat remote file: %s", fx2txt(status));
+ if (quiet)
+ debug("Couldn't stat remote file: %s", fx2txt(status));
+ else
+ error("Couldn't stat remote file: %s", fx2txt(status));
return(NULL);
} else if (type != SSH2_FXP_ATTRS) {
fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
}
buffer_free(&msg);
- return(0);
+
+ return(version);
}
int
return(status);
}
-int
-do_ls(int fd_in, int fd_out, char *path)
+
+static int
+do_lsreaddir(int fd_in, int fd_out, char *path, int printflag,
+ SFTP_DIRENT ***dir)
{
Buffer msg;
- u_int type, id, handle_len, i, expected_id;
+ u_int type, id, handle_len, i, expected_id, ents = 0;
char *handle;
id = msg_id++;
if (handle == NULL)
return(-1);
+ if (dir) {
+ ents = 0;
+ *dir = xmalloc(sizeof(**dir));
+ (*dir)[0] = NULL;
+ }
+
+
for(;;) {
int count;
error("Couldn't read directory: %s",
fx2txt(status));
do_close(fd_in, fd_out, handle, handle_len);
- return(NULL);
+ return(status);
}
} else if (type != SSH2_FXP_NAME)
fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
SSH2_FXP_NAME, type);
count = buffer_get_int(&msg);
- debug3("Received %i SSH2_FXP_NAME responses", count);
+ if (count == 0)
+ break;
+ debug3("Received %d SSH2_FXP_NAME responses", count);
for(i = 0; i < count; i++) {
char *filename, *longname;
Attrib *a;
longname = buffer_get_string(&msg, NULL);
a = decode_attrib(&msg);
- printf("%s\n", longname);
+ if (printflag)
+ printf("%s\n", longname);
+
+ if (dir) {
+ *dir = xrealloc(*dir, sizeof(**dir) *
+ (ents + 2));
+ (*dir)[ents] = xmalloc(sizeof(***dir));
+ (*dir)[ents]->filename = xstrdup(filename);
+ (*dir)[ents]->longname = xstrdup(longname);
+ memcpy(&(*dir)[ents]->a, a, sizeof(*a));
+ (*dir)[++ents] = NULL;
+ }
xfree(filename);
xfree(longname);
return(0);
}
+int
+do_ls(int fd_in, int fd_out, char *path)
+{
+ return(do_lsreaddir(fd_in, fd_out, path, 1, NULL));
+}
+
+int
+do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir)
+{
+ return(do_lsreaddir(fd_in, fd_out, path, 0, dir));
+}
+
+void free_sftp_dirents(SFTP_DIRENT **s)
+{
+ int i;
+
+ for(i = 0; s[i]; i++) {
+ xfree(s[i]->filename);
+ xfree(s[i]->longname);
+ xfree(s[i]);
+ }
+ xfree(s);
+}
+
int
do_rm(int fd_in, int fd_out, char *path)
{
}
Attrib *
-do_stat(int fd_in, int fd_out, char *path)
+do_stat(int fd_in, int fd_out, char *path, int quiet)
{
u_int id;
id = msg_id++;
send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
- return(get_decode_stat(fd_in, id));
+ return(get_decode_stat(fd_in, id, quiet));
}
Attrib *
-do_lstat(int fd_in, int fd_out, char *path)
+do_lstat(int fd_in, int fd_out, char *path, int quiet)
{
u_int id;
id = msg_id++;
send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
- return(get_decode_stat(fd_in, id));
+ return(get_decode_stat(fd_in, id, quiet));
}
Attrib *
-do_fstat(int fd_in, int fd_out, char *handle,
- u_int handle_len)
+do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet)
{
u_int id;
id = msg_id++;
send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
- return(get_decode_stat(fd_in, id));
+ return(get_decode_stat(fd_in, id, quiet));
}
int
Attrib *a;
expected_id = id = msg_id++;
- send_string_request(fd_out, id, SSH2_FXP_REALPATH, path,
- strlen(path));
+ send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path));
buffer_init(&msg);
return(status);
}
+int
+do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath)
+{
+ Buffer msg;
+ u_int status, id;
+
+ buffer_init(&msg);
+
+ /* Send rename request */
+ id = msg_id++;
+ buffer_put_char(&msg, SSH2_FXP_SYMLINK);
+ buffer_put_int(&msg, id);
+ buffer_put_cstring(&msg, oldpath);
+ buffer_put_cstring(&msg, newpath);
+ send_msg(fd_out, &msg);
+ debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
+ newpath);
+ buffer_free(&msg);
+
+ status = get_status(fd_in, id);
+ if (status != SSH2_FX_OK)
+ error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
+ fx2txt(status));
+
+ return(status);
+}
+
+char *
+do_readlink(int fd_in, int fd_out, char *path)
+{
+ Buffer msg;
+ u_int type, expected_id, count, id;
+ char *filename, *longname;
+ Attrib *a;
+
+ expected_id = id = msg_id++;
+ send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path));
+
+ buffer_init(&msg);
+
+ get_msg(fd_in, &msg);
+ type = buffer_get_char(&msg);
+ id = buffer_get_int(&msg);
+
+ if (id != expected_id)
+ fatal("ID mismatch (%d != %d)", id, expected_id);
+
+ if (type == SSH2_FXP_STATUS) {
+ u_int status = buffer_get_int(&msg);
+
+ error("Couldn't readlink: %s", fx2txt(status));
+ return(NULL);
+ } else if (type != SSH2_FXP_NAME)
+ fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
+ SSH2_FXP_NAME, type);
+
+ count = buffer_get_int(&msg);
+ if (count != 1)
+ fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
+
+ filename = buffer_get_string(&msg, NULL);
+ longname = buffer_get_string(&msg, NULL);
+ a = decode_attrib(&msg);
+
+ debug3("SSH_FXP_READLINK %s -> %s", path, filename);
+
+ xfree(longname);
+
+ buffer_free(&msg);
+
+ return(filename);
+}
+
int
do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
int pflag)
char *handle;
Buffer msg;
Attrib junk, *a;
+ int status;
- a = do_stat(fd_in, fd_out, remote_path);
+ a = do_stat(fd_in, fd_out, remote_path, 0);
if (a == NULL)
return(-1);
else
mode = 0666;
+ if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
+ (a->perm & S_IFDIR)) {
+ error("Cannot download a directory: %s", remote_path);
+ return(-1);
+ }
+
local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (local_fd == -1) {
error("Couldn't open local file \"%s\" for writing: %s",
local_path, strerror(errno));
- return(errno);
- }
-
- /* Override umask and utimes if asked */
- if (pflag && fchmod(local_fd, mode) == -1)
- error("Couldn't set mode on \"%s\": %s", local_path,
- strerror(errno));
- if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
- struct timeval tv;
-
- tv.tv_sec = a->atime;
- tv.tv_usec = a->mtime;
- if (utimes(local_path, &tv) == -1)
- error("Can't set times on \"%s\": %s", local_path,
- strerror(errno));
+ return(-1);
}
buffer_init(&msg);
buffer_put_int(&msg, COPY_SIZE);
send_msg(fd_out, &msg);
debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
- id, (unsigned long long)offset, COPY_SIZE);
+ id, (u_int64_t)offset, COPY_SIZE);
buffer_clear(&msg);
if (id != expected_id)
fatal("ID mismatch (%d != %d)", id, expected_id);
if (type == SSH2_FXP_STATUS) {
- int status = buffer_get_int(&msg);
+ status = buffer_get_int(&msg);
if (status == SSH2_FX_EOF)
break;
"file \"%s\" : %s", remote_path,
fx2txt(status));
do_close(fd_in, fd_out, handle, handle_len);
- xfree(handle);
- close(local_fd);
- buffer_free(&msg);
- return(status);
+ goto done;
}
} else if (type != SSH2_FXP_DATA) {
fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
fatal("Received more data than asked for %d > %d",
len, COPY_SIZE);
- debug3("In read loop, got %d offset %lld", len,
- (unsigned long long)offset);
+ debug3("In read loop, got %d offset %llu", len,
+ (u_int64_t)offset);
if (atomicio(write, local_fd, data, len) != len) {
error("Couldn't write to \"%s\": %s", local_path,
strerror(errno));
do_close(fd_in, fd_out, handle, handle_len);
- xfree(handle);
- close(local_fd);
+ status = -1;
xfree(data);
- buffer_free(&msg);
- return(-1);
+ goto done;
}
offset += len;
xfree(data);
}
- xfree(handle);
- buffer_free(&msg);
- close(local_fd);
+ status = do_close(fd_in, fd_out, handle, handle_len);
- return(do_close(fd_in, fd_out, handle, handle_len));
+ /* Override umask and utimes if asked */
+#ifdef HAVE_FCHMOD
+ if (pflag && fchmod(local_fd, mode) == -1)
+#else
+ if (pflag && chmod(local_path, mode) == -1)
+#endif /* HAVE_FCHMOD */
+ error("Couldn't set mode on \"%s\": %s", local_path,
+ strerror(errno));
+ if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
+ struct timeval tv[2];
+ tv[0].tv_sec = a->atime;
+ tv[1].tv_sec = a->mtime;
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+ if (utimes(local_path, tv) == -1)
+ error("Can't set times on \"%s\": %s", local_path,
+ strerror(errno));
+ }
+
+done:
+ close(local_fd);
+ buffer_free(&msg);
+ xfree(handle);
+ return status;
}
int
Buffer msg;
struct stat sb;
Attrib a;
+ int status;
if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
error("Couldn't open local file \"%s\" for reading: %s",
return(-1);
}
- /* Override umask and utimes if asked */
- if (pflag)
- do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
-
/* Read from local and write to remote */
offset = 0;
for(;;) {
int len;
char data[COPY_SIZE];
- u_int status;
/*
* Can't use atomicio here because it returns 0 on EOF, thus losing
buffer_put_string(&msg, data, len);
send_msg(fd_out, &msg);
debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
- id, (unsigned long long)offset, len);
+ id, (u_int64_t)offset, len);
status = get_status(fd_in, id);
if (status != SSH2_FX_OK) {
error("Couldn't write to remote file \"%s\": %s",
remote_path, fx2txt(status));
do_close(fd_in, fd_out, handle, handle_len);
- xfree(handle);
close(local_fd);
- return(-1);
+ goto done;
}
debug3("In write loop, got %d offset %llu", len,
- (unsigned long long)offset);
+ (u_int64_t)offset);
offset += len;
}
- xfree(handle);
- buffer_free(&msg);
if (close(local_fd) == -1) {
error("Couldn't close local file \"%s\": %s", local_path,
strerror(errno));
do_close(fd_in, fd_out, handle, handle_len);
- return(-1);
+ status = -1;
+ goto done;
}
- return(do_close(fd_in, fd_out, handle, handle_len));
+ /* Override umask and utimes if asked */
+ if (pflag)
+ do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
+
+ status = do_close(fd_in, fd_out, handle, handle_len);
+
+done:
+ xfree(handle);
+ buffer_free(&msg);
+ return status;
}
+