summaryrefslogtreecommitdiff
path: root/sftp-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp-client.c')
-rw-r--r--sftp-client.c214
1 files changed, 177 insertions, 37 deletions
diff --git a/sftp-client.c b/sftp-client.c
index 2746f3245..5e39aa7d2 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.76 2007/01/22 11:32:50 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.86 2008/06/26 06:10:09 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -24,6 +24,9 @@
24 24
25#include <sys/types.h> 25#include <sys/types.h>
26#include <sys/param.h> 26#include <sys/param.h>
27#ifdef HAVE_SYS_STATVFS_H
28#include <sys/statvfs.h>
29#endif
27#include "openbsd-compat/sys-queue.h" 30#include "openbsd-compat/sys-queue.h"
28#ifdef HAVE_SYS_STAT_H 31#ifdef HAVE_SYS_STAT_H
29# include <sys/stat.h> 32# include <sys/stat.h>
@@ -65,6 +68,10 @@ struct sftp_conn {
65 u_int num_requests; 68 u_int num_requests;
66 u_int version; 69 u_int version;
67 u_int msg_id; 70 u_int msg_id;
71#define SFTP_EXT_POSIX_RENAME 0x00000001
72#define SFTP_EXT_STATVFS 0x00000002
73#define SFTP_EXT_FSTATVFS 0x00000004
74 u_int exts;
68}; 75};
69 76
70static void 77static void
@@ -236,10 +243,61 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
236 return(a); 243 return(a);
237} 244}
238 245
246static int
247get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
248 int quiet)
249{
250 Buffer msg;
251 u_int type, id, flag;
252
253 buffer_init(&msg);
254 get_msg(fd, &msg);
255
256 type = buffer_get_char(&msg);
257 id = buffer_get_int(&msg);
258
259 debug3("Received statvfs reply T:%u I:%u", type, id);
260 if (id != expected_id)
261 fatal("ID mismatch (%u != %u)", id, expected_id);
262 if (type == SSH2_FXP_STATUS) {
263 int status = buffer_get_int(&msg);
264
265 if (quiet)
266 debug("Couldn't statvfs: %s", fx2txt(status));
267 else
268 error("Couldn't statvfs: %s", fx2txt(status));
269 buffer_free(&msg);
270 return -1;
271 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
272 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
273 SSH2_FXP_EXTENDED_REPLY, type);
274 }
275
276 bzero(st, sizeof(*st));
277 st->f_bsize = buffer_get_int64(&msg);
278 st->f_frsize = buffer_get_int64(&msg);
279 st->f_blocks = buffer_get_int64(&msg);
280 st->f_bfree = buffer_get_int64(&msg);
281 st->f_bavail = buffer_get_int64(&msg);
282 st->f_files = buffer_get_int64(&msg);
283 st->f_ffree = buffer_get_int64(&msg);
284 st->f_favail = buffer_get_int64(&msg);
285 st->f_fsid = buffer_get_int64(&msg);
286 flag = buffer_get_int64(&msg);
287 st->f_namemax = buffer_get_int64(&msg);
288
289 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
290 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
291
292 buffer_free(&msg);
293
294 return 0;
295}
296
239struct sftp_conn * 297struct sftp_conn *
240do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 298do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
241{ 299{
242 u_int type; 300 u_int type, exts = 0;
243 int version; 301 int version;
244 Buffer msg; 302 Buffer msg;
245 struct sftp_conn *ret; 303 struct sftp_conn *ret;
@@ -268,8 +326,27 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
268 while (buffer_len(&msg) > 0) { 326 while (buffer_len(&msg) > 0) {
269 char *name = buffer_get_string(&msg, NULL); 327 char *name = buffer_get_string(&msg, NULL);
270 char *value = buffer_get_string(&msg, NULL); 328 char *value = buffer_get_string(&msg, NULL);
271 329 int known = 0;
272 debug2("Init extension: \"%s\"", name); 330
331 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
332 strcmp(value, "1") == 0) {
333 exts |= SFTP_EXT_POSIX_RENAME;
334 known = 1;
335 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
336 strcmp(value, "2") == 0) {
337 exts |= SFTP_EXT_STATVFS;
338 known = 1;
339 } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
340 strcmp(value, "2") == 0) {
341 exts |= SFTP_EXT_FSTATVFS;
342 known = 1;
343 }
344 if (known) {
345 debug2("Server supports extension \"%s\" revision %s",
346 name, value);
347 } else {
348 debug2("Unrecognised server extension \"%s\"", name);
349 }
273 xfree(name); 350 xfree(name);
274 xfree(value); 351 xfree(value);
275 } 352 }
@@ -283,6 +360,7 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
283 ret->num_requests = num_requests; 360 ret->num_requests = num_requests;
284 ret->version = version; 361 ret->version = version;
285 ret->msg_id = 1; 362 ret->msg_id = 1;
363 ret->exts = exts;
286 364
287 /* Some filexfer v.0 servers don't support large packets */ 365 /* Some filexfer v.0 servers don't support large packets */
288 if (version == 0) 366 if (version == 0)
@@ -534,6 +612,7 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet)
534 return(get_decode_stat(conn->fd_in, id, quiet)); 612 return(get_decode_stat(conn->fd_in, id, quiet));
535} 613}
536 614
615#ifdef notyet
537Attrib * 616Attrib *
538do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 617do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
539{ 618{
@@ -545,6 +624,7 @@ do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
545 624
546 return(get_decode_stat(conn->fd_in, id, quiet)); 625 return(get_decode_stat(conn->fd_in, id, quiet));
547} 626}
627#endif
548 628
549int 629int
550do_setstat(struct sftp_conn *conn, char *path, Attrib *a) 630do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
@@ -637,13 +717,20 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
637 717
638 /* Send rename request */ 718 /* Send rename request */
639 id = conn->msg_id++; 719 id = conn->msg_id++;
640 buffer_put_char(&msg, SSH2_FXP_RENAME); 720 if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
641 buffer_put_int(&msg, id); 721 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
722 buffer_put_int(&msg, id);
723 buffer_put_cstring(&msg, "posix-rename@openssh.com");
724 } else {
725 buffer_put_char(&msg, SSH2_FXP_RENAME);
726 buffer_put_int(&msg, id);
727 }
642 buffer_put_cstring(&msg, oldpath); 728 buffer_put_cstring(&msg, oldpath);
643 buffer_put_cstring(&msg, newpath); 729 buffer_put_cstring(&msg, newpath);
644 send_msg(conn->fd_out, &msg); 730 send_msg(conn->fd_out, &msg);
645 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, 731 debug3("Sent message %s \"%s\" -> \"%s\"",
646 newpath); 732 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
733 "SSH2_FXP_RENAME", oldpath, newpath);
647 buffer_free(&msg); 734 buffer_free(&msg);
648 735
649 status = get_status(conn->fd_in, id); 736 status = get_status(conn->fd_in, id);
@@ -686,6 +773,7 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
686 return(status); 773 return(status);
687} 774}
688 775
776#ifdef notyet
689char * 777char *
690do_readlink(struct sftp_conn *conn, char *path) 778do_readlink(struct sftp_conn *conn, char *path)
691{ 779{
@@ -732,6 +820,61 @@ do_readlink(struct sftp_conn *conn, char *path)
732 820
733 return(filename); 821 return(filename);
734} 822}
823#endif
824
825int
826do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
827 int quiet)
828{
829 Buffer msg;
830 u_int id;
831
832 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
833 error("Server does not support statvfs@openssh.com extension");
834 return -1;
835 }
836
837 id = conn->msg_id++;
838
839 buffer_init(&msg);
840 buffer_clear(&msg);
841 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
842 buffer_put_int(&msg, id);
843 buffer_put_cstring(&msg, "statvfs@openssh.com");
844 buffer_put_cstring(&msg, path);
845 send_msg(conn->fd_out, &msg);
846 buffer_free(&msg);
847
848 return get_decode_statvfs(conn->fd_in, st, id, quiet);
849}
850
851#ifdef notyet
852int
853do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
854 struct sftp_statvfs *st, int quiet)
855{
856 Buffer msg;
857 u_int id;
858
859 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
860 error("Server does not support fstatvfs@openssh.com extension");
861 return -1;
862 }
863
864 id = conn->msg_id++;
865
866 buffer_init(&msg);
867 buffer_clear(&msg);
868 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
869 buffer_put_int(&msg, id);
870 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
871 buffer_put_string(&msg, handle, handle_len);
872 send_msg(conn->fd_out, &msg);
873 buffer_free(&msg);
874
875 return get_decode_statvfs(conn->fd_in, st, id, quiet);
876}
877#endif
735 878
736static void 879static void
737send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, 880send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
@@ -777,7 +920,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
777 if (a == NULL) 920 if (a == NULL)
778 return(-1); 921 return(-1);
779 922
780 /* XXX: should we preserve set[ug]id? */ 923 /* Do not preserve set[ug]id here, as we do not preserve ownership */
781 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 924 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
782 mode = a->perm & 0777; 925 mode = a->perm & 0777;
783 else 926 else
@@ -819,6 +962,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
819 if (local_fd == -1) { 962 if (local_fd == -1) {
820 error("Couldn't open local file \"%s\" for writing: %s", 963 error("Couldn't open local file \"%s\" for writing: %s",
821 local_path, strerror(errno)); 964 local_path, strerror(errno));
965 do_close(conn, handle, handle_len);
822 buffer_free(&msg); 966 buffer_free(&msg);
823 xfree(handle); 967 xfree(handle);
824 return(-1); 968 return(-1);
@@ -992,9 +1136,10 @@ int
992do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1136do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
993 int pflag) 1137 int pflag)
994{ 1138{
995 int local_fd, status; 1139 int local_fd;
1140 int status = SSH2_FX_OK;
996 u_int handle_len, id, type; 1141 u_int handle_len, id, type;
997 u_int64_t offset; 1142 off_t offset;
998 char *handle, *data; 1143 char *handle, *data;
999 Buffer msg; 1144 Buffer msg;
1000 struct stat sb; 1145 struct stat sb;
@@ -1004,7 +1149,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1004 struct outstanding_ack { 1149 struct outstanding_ack {
1005 u_int id; 1150 u_int id;
1006 u_int len; 1151 u_int len;
1007 u_int64_t offset; 1152 off_t offset;
1008 TAILQ_ENTRY(outstanding_ack) tq; 1153 TAILQ_ENTRY(outstanding_ack) tq;
1009 }; 1154 };
1010 TAILQ_HEAD(ackhead, outstanding_ack) acks; 1155 TAILQ_HEAD(ackhead, outstanding_ack) acks;
@@ -1054,7 +1199,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1054 if (handle == NULL) { 1199 if (handle == NULL) {
1055 close(local_fd); 1200 close(local_fd);
1056 buffer_free(&msg); 1201 buffer_free(&msg);
1057 return(-1); 1202 return -1;
1058 } 1203 }
1059 1204
1060 startid = ackid = id + 1; 1205 startid = ackid = id + 1;
@@ -1074,11 +1219,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1074 * Simulate an EOF on interrupt, allowing ACKs from the 1219 * Simulate an EOF on interrupt, allowing ACKs from the
1075 * server to drain. 1220 * server to drain.
1076 */ 1221 */
1077 if (interrupted) 1222 if (interrupted || status != SSH2_FX_OK)
1078 len = 0; 1223 len = 0;
1079 else do 1224 else do
1080 len = read(local_fd, data, conn->transfer_buflen); 1225 len = read(local_fd, data, conn->transfer_buflen);
1081 while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 1226 while ((len == -1) &&
1227 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
1082 1228
1083 if (len == -1) 1229 if (len == -1)
1084 fatal("Couldn't read from \"%s\": %s", local_path, 1230 fatal("Couldn't read from \"%s\": %s", local_path,
@@ -1130,46 +1276,40 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1130 if (ack == NULL) 1276 if (ack == NULL)
1131 fatal("Can't find request for ID %u", r_id); 1277 fatal("Can't find request for ID %u", r_id);
1132 TAILQ_REMOVE(&acks, ack, tq); 1278 TAILQ_REMOVE(&acks, ack, tq);
1133 1279 debug3("In write loop, ack for %u %u bytes at %lld",
1134 if (status != SSH2_FX_OK) { 1280 ack->id, ack->len, (long long)ack->offset);
1135 error("Couldn't write to remote file \"%s\": %s",
1136 remote_path, fx2txt(status));
1137 if (showprogress)
1138 stop_progress_meter();
1139 do_close(conn, handle, handle_len);
1140 close(local_fd);
1141 xfree(data);
1142 xfree(ack);
1143 status = -1;
1144 goto done;
1145 }
1146 debug3("In write loop, ack for %u %u bytes at %llu",
1147 ack->id, ack->len, (unsigned long long)ack->offset);
1148 ++ackid; 1281 ++ackid;
1149 xfree(ack); 1282 xfree(ack);
1150 } 1283 }
1151 offset += len; 1284 offset += len;
1285 if (offset < 0)
1286 fatal("%s: offset < 0", __func__);
1152 } 1287 }
1288 buffer_free(&msg);
1289
1153 if (showprogress) 1290 if (showprogress)
1154 stop_progress_meter(); 1291 stop_progress_meter();
1155 xfree(data); 1292 xfree(data);
1156 1293
1294 if (status != SSH2_FX_OK) {
1295 error("Couldn't write to remote file \"%s\": %s",
1296 remote_path, fx2txt(status));
1297 status = -1;
1298 }
1299
1157 if (close(local_fd) == -1) { 1300 if (close(local_fd) == -1) {
1158 error("Couldn't close local file \"%s\": %s", local_path, 1301 error("Couldn't close local file \"%s\": %s", local_path,
1159 strerror(errno)); 1302 strerror(errno));
1160 do_close(conn, handle, handle_len);
1161 status = -1; 1303 status = -1;
1162 goto done;
1163 } 1304 }
1164 1305
1165 /* Override umask and utimes if asked */ 1306 /* Override umask and utimes if asked */
1166 if (pflag) 1307 if (pflag)
1167 do_fsetstat(conn, handle, handle_len, &a); 1308 do_fsetstat(conn, handle, handle_len, &a);
1168 1309
1169 status = do_close(conn, handle, handle_len); 1310 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1170 1311 status = -1;
1171done:
1172 xfree(handle); 1312 xfree(handle);
1173 buffer_free(&msg); 1313
1174 return(status); 1314 return status;
1175} 1315}