Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.68
1.68 ! stevesk 1: /* $OpenBSD: sftp-client.c,v 1.67 2006/07/11 20:07:25 stevesk Exp $ */
1.1 djm 2: /*
1.46 djm 3: * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
1.1 djm 4: *
1.46 djm 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 djm 8: *
1.46 djm 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 djm 16: */
17:
18: /* XXX: memleaks */
19: /* XXX: signed vs unsigned */
1.23 djm 20: /* XXX: remove all logging, only return status codes */
1.1 djm 21: /* XXX: copy between two remote sites */
22:
23: #include "includes.h"
1.21 djm 24:
25: #include <sys/queue.h>
1.60 stevesk 26: #include <sys/types.h>
27: #include <sys/stat.h>
1.66 stevesk 28:
1.67 stevesk 29: #include <errno.h>
1.66 stevesk 30: #include <fcntl.h>
1.68 ! stevesk 31: #include <unistd.h>
1.1 djm 32:
33: #include "buffer.h"
34: #include "bufaux.h"
35: #include "xmalloc.h"
36: #include "log.h"
37: #include "atomicio.h"
1.39 fgsch 38: #include "progressmeter.h"
1.64 djm 39: #include "misc.h"
1.1 djm 40:
41: #include "sftp.h"
42: #include "sftp-common.h"
43: #include "sftp-client.h"
44:
1.49 djm 45: extern volatile sig_atomic_t interrupted;
1.39 fgsch 46: extern int showprogress;
47:
1.59 david 48: /* Minimum amount of data to read at a time */
1.21 djm 49: #define MIN_READ_SIZE 512
50:
1.23 djm 51: struct sftp_conn {
52: int fd_in;
53: int fd_out;
54: u_int transfer_buflen;
55: u_int num_requests;
56: u_int version;
57: u_int msg_id;
58: };
1.4 djm 59:
1.17 itojun 60: static void
1.1 djm 61: send_msg(int fd, Buffer *m)
62: {
1.40 djm 63: u_char mlen[4];
1.65 djm 64: struct iovec iov[2];
1.40 djm 65:
1.58 djm 66: if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
1.40 djm 67: fatal("Outbound message too long %u", buffer_len(m));
68:
69: /* Send length first */
1.64 djm 70: put_u32(mlen, buffer_len(m));
1.65 djm 71: iov[0].iov_base = mlen;
72: iov[0].iov_len = sizeof(mlen);
73: iov[1].iov_base = buffer_ptr(m);
74: iov[1].iov_len = buffer_len(m);
75:
76: if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
1.1 djm 77: fatal("Couldn't send packet: %s", strerror(errno));
78:
1.40 djm 79: buffer_clear(m);
1.1 djm 80: }
81:
1.17 itojun 82: static void
1.1 djm 83: get_msg(int fd, Buffer *m)
84: {
1.40 djm 85: u_int msg_len;
1.1 djm 86:
1.40 djm 87: buffer_append_space(m, 4);
1.54 avsm 88: if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
89: if (errno == EPIPE)
90: fatal("Connection closed");
91: else
92: fatal("Couldn't read packet: %s", strerror(errno));
93: }
1.1 djm 94:
1.40 djm 95: msg_len = buffer_get_int(m);
1.58 djm 96: if (msg_len > SFTP_MAX_MSG_LENGTH)
1.33 deraadt 97: fatal("Received message too long %u", msg_len);
1.1 djm 98:
1.40 djm 99: buffer_append_space(m, msg_len);
1.54 avsm 100: if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
101: if (errno == EPIPE)
102: fatal("Connection closed");
103: else
104: fatal("Read packet: %s", strerror(errno));
105: }
1.1 djm 106: }
107:
1.17 itojun 108: static void
1.1 djm 109: send_string_request(int fd, u_int id, u_int code, char *s,
110: u_int len)
111: {
112: Buffer msg;
113:
114: buffer_init(&msg);
115: buffer_put_char(&msg, code);
116: buffer_put_int(&msg, id);
117: buffer_put_string(&msg, s, len);
118: send_msg(fd, &msg);
1.33 deraadt 119: debug3("Sent message fd %d T:%u I:%u", fd, code, id);
1.1 djm 120: buffer_free(&msg);
121: }
122:
1.17 itojun 123: static void
1.1 djm 124: send_string_attrs_request(int fd, u_int id, u_int code, char *s,
125: u_int len, Attrib *a)
126: {
127: Buffer msg;
128:
129: buffer_init(&msg);
130: buffer_put_char(&msg, code);
131: buffer_put_int(&msg, id);
132: buffer_put_string(&msg, s, len);
133: encode_attrib(&msg, a);
134: send_msg(fd, &msg);
1.33 deraadt 135: debug3("Sent message fd %d T:%u I:%u", fd, code, id);
1.1 djm 136: buffer_free(&msg);
137: }
138:
1.17 itojun 139: static u_int
1.33 deraadt 140: get_status(int fd, u_int expected_id)
1.1 djm 141: {
142: Buffer msg;
143: u_int type, id, status;
144:
145: buffer_init(&msg);
146: get_msg(fd, &msg);
147: type = buffer_get_char(&msg);
148: id = buffer_get_int(&msg);
149:
150: if (id != expected_id)
1.33 deraadt 151: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 152: if (type != SSH2_FXP_STATUS)
1.33 deraadt 153: fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
1.1 djm 154: SSH2_FXP_STATUS, type);
155:
156: status = buffer_get_int(&msg);
157: buffer_free(&msg);
158:
1.33 deraadt 159: debug3("SSH2_FXP_STATUS %u", status);
1.1 djm 160:
161: return(status);
162: }
163:
1.17 itojun 164: static char *
1.1 djm 165: get_handle(int fd, u_int expected_id, u_int *len)
166: {
167: Buffer msg;
168: u_int type, id;
169: char *handle;
170:
171: buffer_init(&msg);
172: get_msg(fd, &msg);
173: type = buffer_get_char(&msg);
174: id = buffer_get_int(&msg);
175:
176: if (id != expected_id)
1.33 deraadt 177: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 178: if (type == SSH2_FXP_STATUS) {
179: int status = buffer_get_int(&msg);
180:
181: error("Couldn't get handle: %s", fx2txt(status));
1.52 markus 182: buffer_free(&msg);
1.1 djm 183: return(NULL);
184: } else if (type != SSH2_FXP_HANDLE)
1.33 deraadt 185: fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
1.1 djm 186: SSH2_FXP_HANDLE, type);
187:
188: handle = buffer_get_string(&msg, len);
189: buffer_free(&msg);
190:
191: return(handle);
192: }
193:
1.17 itojun 194: static Attrib *
1.14 djm 195: get_decode_stat(int fd, u_int expected_id, int quiet)
1.1 djm 196: {
197: Buffer msg;
198: u_int type, id;
199: Attrib *a;
200:
201: buffer_init(&msg);
202: get_msg(fd, &msg);
203:
204: type = buffer_get_char(&msg);
205: id = buffer_get_int(&msg);
206:
1.33 deraadt 207: debug3("Received stat reply T:%u I:%u", type, id);
1.1 djm 208: if (id != expected_id)
1.33 deraadt 209: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 210: if (type == SSH2_FXP_STATUS) {
211: int status = buffer_get_int(&msg);
212:
1.14 djm 213: if (quiet)
214: debug("Couldn't stat remote file: %s", fx2txt(status));
215: else
216: error("Couldn't stat remote file: %s", fx2txt(status));
1.52 markus 217: buffer_free(&msg);
1.1 djm 218: return(NULL);
219: } else if (type != SSH2_FXP_ATTRS) {
1.33 deraadt 220: fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
1.1 djm 221: SSH2_FXP_ATTRS, type);
222: }
223: a = decode_attrib(&msg);
224: buffer_free(&msg);
225:
226: return(a);
227: }
228:
1.23 djm 229: struct sftp_conn *
230: do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
1.1 djm 231: {
1.33 deraadt 232: u_int type;
233: int version;
1.1 djm 234: Buffer msg;
1.23 djm 235: struct sftp_conn *ret;
1.1 djm 236:
237: buffer_init(&msg);
238: buffer_put_char(&msg, SSH2_FXP_INIT);
239: buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
240: send_msg(fd_out, &msg);
241:
242: buffer_clear(&msg);
243:
244: get_msg(fd_in, &msg);
245:
1.3 stevesk 246: /* Expecting a VERSION reply */
1.1 djm 247: if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
1.33 deraadt 248: error("Invalid packet back from SSH2_FXP_INIT (type %u)",
1.1 djm 249: type);
250: buffer_free(&msg);
1.23 djm 251: return(NULL);
1.1 djm 252: }
253: version = buffer_get_int(&msg);
254:
255: debug2("Remote version: %d", version);
256:
257: /* Check for extensions */
258: while (buffer_len(&msg) > 0) {
259: char *name = buffer_get_string(&msg, NULL);
260: char *value = buffer_get_string(&msg, NULL);
261:
262: debug2("Init extension: \"%s\"", name);
263: xfree(name);
264: xfree(value);
265: }
266:
267: buffer_free(&msg);
1.11 djm 268:
1.23 djm 269: ret = xmalloc(sizeof(*ret));
270: ret->fd_in = fd_in;
271: ret->fd_out = fd_out;
272: ret->transfer_buflen = transfer_buflen;
273: ret->num_requests = num_requests;
274: ret->version = version;
275: ret->msg_id = 1;
276:
277: /* Some filexfer v.0 servers don't support large packets */
278: if (version == 0)
1.29 markus 279: ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
1.23 djm 280:
281: return(ret);
282: }
283:
284: u_int
285: sftp_proto_version(struct sftp_conn *conn)
286: {
287: return(conn->version);
1.1 djm 288: }
289:
290: int
1.23 djm 291: do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
1.1 djm 292: {
293: u_int id, status;
294: Buffer msg;
295:
296: buffer_init(&msg);
297:
1.23 djm 298: id = conn->msg_id++;
1.1 djm 299: buffer_put_char(&msg, SSH2_FXP_CLOSE);
300: buffer_put_int(&msg, id);
301: buffer_put_string(&msg, handle, handle_len);
1.23 djm 302: send_msg(conn->fd_out, &msg);
1.33 deraadt 303: debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
1.1 djm 304:
1.23 djm 305: status = get_status(conn->fd_in, id);
1.1 djm 306: if (status != SSH2_FX_OK)
307: error("Couldn't close file: %s", fx2txt(status));
308:
309: buffer_free(&msg);
310:
311: return(status);
312: }
313:
1.12 djm 314:
1.17 itojun 315: static int
1.23 djm 316: do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
1.12 djm 317: SFTP_DIRENT ***dir)
1.1 djm 318: {
319: Buffer msg;
1.55 djm 320: u_int count, type, id, handle_len, i, expected_id, ents = 0;
1.1 djm 321: char *handle;
322:
1.23 djm 323: id = conn->msg_id++;
1.1 djm 324:
325: buffer_init(&msg);
326: buffer_put_char(&msg, SSH2_FXP_OPENDIR);
327: buffer_put_int(&msg, id);
328: buffer_put_cstring(&msg, path);
1.23 djm 329: send_msg(conn->fd_out, &msg);
1.1 djm 330:
331: buffer_clear(&msg);
332:
1.23 djm 333: handle = get_handle(conn->fd_in, id, &handle_len);
1.1 djm 334: if (handle == NULL)
335: return(-1);
336:
1.12 djm 337: if (dir) {
338: ents = 0;
339: *dir = xmalloc(sizeof(**dir));
340: (*dir)[0] = NULL;
341: }
342:
1.49 djm 343: for (; !interrupted;) {
1.23 djm 344: id = expected_id = conn->msg_id++;
1.1 djm 345:
1.33 deraadt 346: debug3("Sending SSH2_FXP_READDIR I:%u", id);
1.1 djm 347:
348: buffer_clear(&msg);
349: buffer_put_char(&msg, SSH2_FXP_READDIR);
350: buffer_put_int(&msg, id);
351: buffer_put_string(&msg, handle, handle_len);
1.23 djm 352: send_msg(conn->fd_out, &msg);
1.1 djm 353:
354: buffer_clear(&msg);
355:
1.23 djm 356: get_msg(conn->fd_in, &msg);
1.1 djm 357:
358: type = buffer_get_char(&msg);
359: id = buffer_get_int(&msg);
360:
1.33 deraadt 361: debug3("Received reply T:%u I:%u", type, id);
1.1 djm 362:
363: if (id != expected_id)
1.33 deraadt 364: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 365:
366: if (type == SSH2_FXP_STATUS) {
367: int status = buffer_get_int(&msg);
368:
369: debug3("Received SSH2_FXP_STATUS %d", status);
370:
371: if (status == SSH2_FX_EOF) {
372: break;
373: } else {
374: error("Couldn't read directory: %s",
375: fx2txt(status));
1.23 djm 376: do_close(conn, handle, handle_len);
1.42 markus 377: xfree(handle);
1.9 djm 378: return(status);
1.1 djm 379: }
380: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 381: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 382: SSH2_FXP_NAME, type);
383:
384: count = buffer_get_int(&msg);
1.7 markus 385: if (count == 0)
386: break;
1.8 stevesk 387: debug3("Received %d SSH2_FXP_NAME responses", count);
1.19 deraadt 388: for (i = 0; i < count; i++) {
1.1 djm 389: char *filename, *longname;
390: Attrib *a;
391:
392: filename = buffer_get_string(&msg, NULL);
393: longname = buffer_get_string(&msg, NULL);
394: a = decode_attrib(&msg);
395:
1.12 djm 396: if (printflag)
397: printf("%s\n", longname);
398:
399: if (dir) {
1.62 djm 400: *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
1.12 djm 401: (*dir)[ents] = xmalloc(sizeof(***dir));
402: (*dir)[ents]->filename = xstrdup(filename);
403: (*dir)[ents]->longname = xstrdup(longname);
404: memcpy(&(*dir)[ents]->a, a, sizeof(*a));
405: (*dir)[++ents] = NULL;
406: }
1.1 djm 407:
408: xfree(filename);
409: xfree(longname);
410: }
411: }
412:
413: buffer_free(&msg);
1.23 djm 414: do_close(conn, handle, handle_len);
1.1 djm 415: xfree(handle);
416:
1.49 djm 417: /* Don't return partial matches on interrupt */
418: if (interrupted && dir != NULL && *dir != NULL) {
419: free_sftp_dirents(*dir);
420: *dir = xmalloc(sizeof(**dir));
421: **dir = NULL;
422: }
423:
1.1 djm 424: return(0);
1.12 djm 425: }
426:
427: int
1.23 djm 428: do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
1.12 djm 429: {
1.23 djm 430: return(do_lsreaddir(conn, path, 0, dir));
1.12 djm 431: }
432:
433: void free_sftp_dirents(SFTP_DIRENT **s)
434: {
435: int i;
1.19 deraadt 436:
437: for (i = 0; s[i]; i++) {
1.12 djm 438: xfree(s[i]->filename);
439: xfree(s[i]->longname);
440: xfree(s[i]);
441: }
442: xfree(s);
443: }
444:
445: int
1.23 djm 446: do_rm(struct sftp_conn *conn, char *path)
1.1 djm 447: {
448: u_int status, id;
449:
450: debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
451:
1.23 djm 452: id = conn->msg_id++;
1.28 markus 453: send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
1.23 djm 454: strlen(path));
455: status = get_status(conn->fd_in, id);
1.1 djm 456: if (status != SSH2_FX_OK)
457: error("Couldn't delete file: %s", fx2txt(status));
458: return(status);
459: }
460:
461: int
1.23 djm 462: do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
1.1 djm 463: {
464: u_int status, id;
465:
1.23 djm 466: id = conn->msg_id++;
467: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
1.1 djm 468: strlen(path), a);
469:
1.23 djm 470: status = get_status(conn->fd_in, id);
1.1 djm 471: if (status != SSH2_FX_OK)
472: error("Couldn't create directory: %s", fx2txt(status));
473:
474: return(status);
475: }
476:
477: int
1.23 djm 478: do_rmdir(struct sftp_conn *conn, char *path)
1.1 djm 479: {
480: u_int status, id;
481:
1.23 djm 482: id = conn->msg_id++;
483: send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
484: strlen(path));
1.1 djm 485:
1.23 djm 486: status = get_status(conn->fd_in, id);
1.1 djm 487: if (status != SSH2_FX_OK)
488: error("Couldn't remove directory: %s", fx2txt(status));
489:
490: return(status);
491: }
492:
493: Attrib *
1.23 djm 494: do_stat(struct sftp_conn *conn, char *path, int quiet)
1.1 djm 495: {
496: u_int id;
497:
1.23 djm 498: id = conn->msg_id++;
499:
1.28 markus 500: send_string_request(conn->fd_out, id,
501: conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
1.23 djm 502: path, strlen(path));
503:
504: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 505: }
506:
507: Attrib *
1.23 djm 508: do_lstat(struct sftp_conn *conn, char *path, int quiet)
1.1 djm 509: {
510: u_int id;
511:
1.23 djm 512: if (conn->version == 0) {
513: if (quiet)
514: debug("Server version does not support lstat operation");
515: else
1.43 itojun 516: logit("Server version does not support lstat operation");
1.30 markus 517: return(do_stat(conn, path, quiet));
1.23 djm 518: }
519:
520: id = conn->msg_id++;
521: send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
522: strlen(path));
523:
524: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 525: }
526:
527: Attrib *
1.23 djm 528: do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
1.1 djm 529: {
530: u_int id;
531:
1.23 djm 532: id = conn->msg_id++;
533: send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
534: handle_len);
535:
536: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 537: }
538:
539: int
1.23 djm 540: do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
1.1 djm 541: {
542: u_int status, id;
543:
1.23 djm 544: id = conn->msg_id++;
545: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
1.1 djm 546: strlen(path), a);
547:
1.23 djm 548: status = get_status(conn->fd_in, id);
1.1 djm 549: if (status != SSH2_FX_OK)
550: error("Couldn't setstat on \"%s\": %s", path,
551: fx2txt(status));
552:
553: return(status);
554: }
555:
556: int
1.23 djm 557: do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
1.1 djm 558: Attrib *a)
559: {
560: u_int status, id;
561:
1.23 djm 562: id = conn->msg_id++;
563: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
1.1 djm 564: handle_len, a);
565:
1.23 djm 566: status = get_status(conn->fd_in, id);
1.1 djm 567: if (status != SSH2_FX_OK)
568: error("Couldn't fsetstat: %s", fx2txt(status));
569:
570: return(status);
571: }
572:
573: char *
1.23 djm 574: do_realpath(struct sftp_conn *conn, char *path)
1.1 djm 575: {
576: Buffer msg;
577: u_int type, expected_id, count, id;
578: char *filename, *longname;
579: Attrib *a;
580:
1.23 djm 581: expected_id = id = conn->msg_id++;
582: send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
583: strlen(path));
1.1 djm 584:
585: buffer_init(&msg);
586:
1.23 djm 587: get_msg(conn->fd_in, &msg);
1.1 djm 588: type = buffer_get_char(&msg);
589: id = buffer_get_int(&msg);
590:
591: if (id != expected_id)
1.33 deraadt 592: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 593:
594: if (type == SSH2_FXP_STATUS) {
595: u_int status = buffer_get_int(&msg);
596:
597: error("Couldn't canonicalise: %s", fx2txt(status));
598: return(NULL);
599: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 600: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 601: SSH2_FXP_NAME, type);
602:
603: count = buffer_get_int(&msg);
604: if (count != 1)
605: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
606:
607: filename = buffer_get_string(&msg, NULL);
608: longname = buffer_get_string(&msg, NULL);
609: a = decode_attrib(&msg);
610:
611: debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
612:
613: xfree(longname);
614:
615: buffer_free(&msg);
616:
617: return(filename);
618: }
619:
620: int
1.23 djm 621: do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
1.1 djm 622: {
623: Buffer msg;
624: u_int status, id;
625:
626: buffer_init(&msg);
627:
628: /* Send rename request */
1.23 djm 629: id = conn->msg_id++;
1.1 djm 630: buffer_put_char(&msg, SSH2_FXP_RENAME);
631: buffer_put_int(&msg, id);
632: buffer_put_cstring(&msg, oldpath);
633: buffer_put_cstring(&msg, newpath);
1.23 djm 634: send_msg(conn->fd_out, &msg);
1.1 djm 635: debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
636: newpath);
637: buffer_free(&msg);
638:
1.23 djm 639: status = get_status(conn->fd_in, id);
1.1 djm 640: if (status != SSH2_FX_OK)
1.23 djm 641: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
642: newpath, fx2txt(status));
1.1 djm 643:
644: return(status);
1.11 djm 645: }
646:
647: int
1.23 djm 648: do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
1.11 djm 649: {
650: Buffer msg;
651: u_int status, id;
652:
1.23 djm 653: if (conn->version < 3) {
654: error("This server does not support the symlink operation");
655: return(SSH2_FX_OP_UNSUPPORTED);
656: }
657:
1.11 djm 658: buffer_init(&msg);
659:
1.48 djm 660: /* Send symlink request */
1.23 djm 661: id = conn->msg_id++;
1.11 djm 662: buffer_put_char(&msg, SSH2_FXP_SYMLINK);
663: buffer_put_int(&msg, id);
664: buffer_put_cstring(&msg, oldpath);
665: buffer_put_cstring(&msg, newpath);
1.23 djm 666: send_msg(conn->fd_out, &msg);
1.11 djm 667: debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
668: newpath);
669: buffer_free(&msg);
670:
1.23 djm 671: status = get_status(conn->fd_in, id);
1.11 djm 672: if (status != SSH2_FX_OK)
1.36 markus 673: error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
1.23 djm 674: newpath, fx2txt(status));
1.11 djm 675:
676: return(status);
677: }
678:
679: char *
1.23 djm 680: do_readlink(struct sftp_conn *conn, char *path)
1.11 djm 681: {
682: Buffer msg;
683: u_int type, expected_id, count, id;
684: char *filename, *longname;
685: Attrib *a;
686:
1.23 djm 687: expected_id = id = conn->msg_id++;
688: send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
689: strlen(path));
1.11 djm 690:
691: buffer_init(&msg);
692:
1.23 djm 693: get_msg(conn->fd_in, &msg);
1.11 djm 694: type = buffer_get_char(&msg);
695: id = buffer_get_int(&msg);
696:
697: if (id != expected_id)
1.33 deraadt 698: fatal("ID mismatch (%u != %u)", id, expected_id);
1.11 djm 699:
700: if (type == SSH2_FXP_STATUS) {
701: u_int status = buffer_get_int(&msg);
702:
703: error("Couldn't readlink: %s", fx2txt(status));
704: return(NULL);
705: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 706: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.11 djm 707: SSH2_FXP_NAME, type);
708:
709: count = buffer_get_int(&msg);
710: if (count != 1)
711: fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
712:
713: filename = buffer_get_string(&msg, NULL);
714: longname = buffer_get_string(&msg, NULL);
715: a = decode_attrib(&msg);
716:
717: debug3("SSH_FXP_READLINK %s -> %s", path, filename);
718:
719: xfree(longname);
720:
721: buffer_free(&msg);
722:
723: return(filename);
1.1 djm 724: }
725:
1.21 djm 726: static void
727: send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
728: char *handle, u_int handle_len)
729: {
730: Buffer msg;
1.28 markus 731:
1.21 djm 732: buffer_init(&msg);
733: buffer_clear(&msg);
734: buffer_put_char(&msg, SSH2_FXP_READ);
735: buffer_put_int(&msg, id);
736: buffer_put_string(&msg, handle, handle_len);
737: buffer_put_int64(&msg, offset);
738: buffer_put_int(&msg, len);
739: send_msg(fd_out, &msg);
740: buffer_free(&msg);
1.28 markus 741: }
1.21 djm 742:
1.1 djm 743: int
1.23 djm 744: do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
745: int pflag)
1.1 djm 746: {
1.21 djm 747: Attrib junk, *a;
748: Buffer msg;
1.1 djm 749: char *handle;
1.57 dtucker 750: int local_fd, status = 0, write_error;
1.21 djm 751: int read_error, write_errno;
752: u_int64_t offset, size;
1.55 djm 753: u_int handle_len, mode, type, id, buflen, num_req, max_req;
1.39 fgsch 754: off_t progress_counter;
1.21 djm 755: struct request {
756: u_int id;
757: u_int len;
758: u_int64_t offset;
1.28 markus 759: TAILQ_ENTRY(request) tq;
1.21 djm 760: };
761: TAILQ_HEAD(reqhead, request) requests;
762: struct request *req;
763:
764: TAILQ_INIT(&requests);
1.1 djm 765:
1.23 djm 766: a = do_stat(conn, remote_path, 0);
1.1 djm 767: if (a == NULL)
768: return(-1);
769:
770: /* XXX: should we preserve set[ug]id? */
771: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1.38 djm 772: mode = a->perm & 0777;
1.1 djm 773: else
774: mode = 0666;
775:
1.14 djm 776: if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1.41 djm 777: (!S_ISREG(a->perm))) {
778: error("Cannot download non-regular file: %s", remote_path);
1.14 djm 779: return(-1);
780: }
781:
1.21 djm 782: if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
783: size = a->size;
784: else
785: size = 0;
786:
1.23 djm 787: buflen = conn->transfer_buflen;
1.1 djm 788: buffer_init(&msg);
789:
790: /* Send open request */
1.23 djm 791: id = conn->msg_id++;
1.1 djm 792: buffer_put_char(&msg, SSH2_FXP_OPEN);
793: buffer_put_int(&msg, id);
794: buffer_put_cstring(&msg, remote_path);
795: buffer_put_int(&msg, SSH2_FXF_READ);
796: attrib_clear(&junk); /* Send empty attributes */
797: encode_attrib(&msg, &junk);
1.23 djm 798: send_msg(conn->fd_out, &msg);
1.33 deraadt 799: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 800:
1.23 djm 801: handle = get_handle(conn->fd_in, id, &handle_len);
1.1 djm 802: if (handle == NULL) {
803: buffer_free(&msg);
804: return(-1);
805: }
806:
1.45 djm 807: local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
1.38 djm 808: mode | S_IWRITE);
1.23 djm 809: if (local_fd == -1) {
810: error("Couldn't open local file \"%s\" for writing: %s",
811: local_path, strerror(errno));
1.24 markus 812: buffer_free(&msg);
813: xfree(handle);
1.23 djm 814: return(-1);
815: }
816:
1.1 djm 817: /* Read from remote and write to local */
1.21 djm 818: write_error = read_error = write_errno = num_req = offset = 0;
819: max_req = 1;
1.39 fgsch 820: progress_counter = 0;
821:
1.47 djm 822: if (showprogress && size != 0)
823: start_progress_meter(remote_path, size, &progress_counter);
1.39 fgsch 824:
1.21 djm 825: while (num_req > 0 || max_req > 0) {
826: char *data;
1.1 djm 827: u_int len;
828:
1.49 djm 829: /*
1.51 deraadt 830: * Simulate EOF on interrupt: stop sending new requests and
1.49 djm 831: * allow outstanding requests to drain gracefully
832: */
833: if (interrupted) {
834: if (num_req == 0) /* If we haven't started yet... */
835: break;
836: max_req = 0;
837: }
838:
1.21 djm 839: /* Send some more requests */
840: while (num_req < max_req) {
1.28 markus 841: debug3("Request range %llu -> %llu (%d/%d)",
1.25 itojun 842: (unsigned long long)offset,
843: (unsigned long long)offset + buflen - 1,
844: num_req, max_req);
1.21 djm 845: req = xmalloc(sizeof(*req));
1.23 djm 846: req->id = conn->msg_id++;
1.21 djm 847: req->len = buflen;
848: req->offset = offset;
849: offset += buflen;
850: num_req++;
851: TAILQ_INSERT_TAIL(&requests, req, tq);
1.28 markus 852: send_read_request(conn->fd_out, req->id, req->offset,
1.21 djm 853: req->len, handle, handle_len);
854: }
1.1 djm 855:
856: buffer_clear(&msg);
1.23 djm 857: get_msg(conn->fd_in, &msg);
1.1 djm 858: type = buffer_get_char(&msg);
859: id = buffer_get_int(&msg);
1.33 deraadt 860: debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1.21 djm 861:
862: /* Find the request in our queue */
1.53 deraadt 863: for (req = TAILQ_FIRST(&requests);
1.21 djm 864: req != NULL && req->id != id;
865: req = TAILQ_NEXT(req, tq))
866: ;
867: if (req == NULL)
868: fatal("Unexpected reply %u", id);
869:
870: switch (type) {
871: case SSH2_FXP_STATUS:
1.5 djm 872: status = buffer_get_int(&msg);
1.21 djm 873: if (status != SSH2_FX_EOF)
874: read_error = 1;
875: max_req = 0;
876: TAILQ_REMOVE(&requests, req, tq);
877: xfree(req);
878: num_req--;
879: break;
880: case SSH2_FXP_DATA:
881: data = buffer_get_string(&msg, &len);
1.26 itojun 882: debug3("Received data %llu -> %llu",
1.28 markus 883: (unsigned long long)req->offset,
1.26 itojun 884: (unsigned long long)req->offset + len - 1);
1.21 djm 885: if (len > req->len)
886: fatal("Received more data than asked for "
1.37 deraadt 887: "%u > %u", len, req->len);
1.21 djm 888: if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1.44 deraadt 889: atomicio(vwrite, local_fd, data, len) != len) &&
1.21 djm 890: !write_error) {
891: write_errno = errno;
892: write_error = 1;
893: max_req = 0;
894: }
1.39 fgsch 895: progress_counter += len;
1.21 djm 896: xfree(data);
1.1 djm 897:
1.21 djm 898: if (len == req->len) {
899: TAILQ_REMOVE(&requests, req, tq);
900: xfree(req);
901: num_req--;
902: } else {
903: /* Resend the request for the missing data */
904: debug3("Short data block, re-requesting "
1.26 itojun 905: "%llu -> %llu (%2d)",
1.28 markus 906: (unsigned long long)req->offset + len,
1.27 itojun 907: (unsigned long long)req->offset +
908: req->len - 1, num_req);
1.23 djm 909: req->id = conn->msg_id++;
1.21 djm 910: req->len -= len;
911: req->offset += len;
1.28 markus 912: send_read_request(conn->fd_out, req->id,
1.23 djm 913: req->offset, req->len, handle, handle_len);
1.21 djm 914: /* Reduce the request size */
915: if (len < buflen)
916: buflen = MAX(MIN_READ_SIZE, len);
917: }
918: if (max_req > 0) { /* max_req = 0 iff EOF received */
919: if (size > 0 && offset > size) {
920: /* Only one request at a time
921: * after the expected EOF */
922: debug3("Finish at %llu (%2d)",
1.26 itojun 923: (unsigned long long)offset,
924: num_req);
1.21 djm 925: max_req = 1;
1.49 djm 926: } else if (max_req <= conn->num_requests) {
1.21 djm 927: ++max_req;
928: }
1.1 djm 929: }
1.21 djm 930: break;
931: default:
1.33 deraadt 932: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1.1 djm 933: SSH2_FXP_DATA, type);
934: }
1.21 djm 935: }
1.1 djm 936:
1.39 fgsch 937: if (showprogress && size)
938: stop_progress_meter();
939:
1.21 djm 940: /* Sanity check */
941: if (TAILQ_FIRST(&requests) != NULL)
942: fatal("Transfer complete, but requests still in queue");
943:
944: if (read_error) {
1.28 markus 945: error("Couldn't read from remote file \"%s\" : %s",
1.21 djm 946: remote_path, fx2txt(status));
1.23 djm 947: do_close(conn, handle, handle_len);
1.21 djm 948: } else if (write_error) {
949: error("Couldn't write to \"%s\": %s", local_path,
950: strerror(write_errno));
951: status = -1;
1.23 djm 952: do_close(conn, handle, handle_len);
1.21 djm 953: } else {
1.23 djm 954: status = do_close(conn, handle, handle_len);
1.21 djm 955:
956: /* Override umask and utimes if asked */
957: if (pflag && fchmod(local_fd, mode) == -1)
958: error("Couldn't set mode on \"%s\": %s", local_path,
1.37 deraadt 959: strerror(errno));
1.21 djm 960: if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
961: struct timeval tv[2];
962: tv[0].tv_sec = a->atime;
963: tv[1].tv_sec = a->mtime;
964: tv[0].tv_usec = tv[1].tv_usec = 0;
965: if (utimes(local_path, tv) == -1)
966: error("Can't set times on \"%s\": %s",
1.37 deraadt 967: local_path, strerror(errno));
1.1 djm 968: }
1.10 djm 969: }
1.5 djm 970: close(local_fd);
971: buffer_free(&msg);
1.1 djm 972: xfree(handle);
1.23 djm 973:
974: return(status);
1.1 djm 975: }
976:
977: int
1.23 djm 978: do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
979: int pflag)
1.1 djm 980: {
1.20 djm 981: int local_fd, status;
1.22 djm 982: u_int handle_len, id, type;
1.1 djm 983: u_int64_t offset;
1.20 djm 984: char *handle, *data;
1.1 djm 985: Buffer msg;
986: struct stat sb;
987: Attrib a;
1.21 djm 988: u_int32_t startid;
989: u_int32_t ackid;
1.22 djm 990: struct outstanding_ack {
991: u_int id;
992: u_int len;
993: u_int64_t offset;
1.28 markus 994: TAILQ_ENTRY(outstanding_ack) tq;
1.22 djm 995: };
996: TAILQ_HEAD(ackhead, outstanding_ack) acks;
1.50 pedro 997: struct outstanding_ack *ack = NULL;
1.22 djm 998:
999: TAILQ_INIT(&acks);
1.1 djm 1000:
1001: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1002: error("Couldn't open local file \"%s\" for reading: %s",
1003: local_path, strerror(errno));
1004: return(-1);
1005: }
1006: if (fstat(local_fd, &sb) == -1) {
1007: error("Couldn't fstat local file \"%s\": %s",
1008: local_path, strerror(errno));
1.41 djm 1009: close(local_fd);
1010: return(-1);
1011: }
1012: if (!S_ISREG(sb.st_mode)) {
1013: error("%s is not a regular file", local_path);
1.1 djm 1014: close(local_fd);
1015: return(-1);
1016: }
1017: stat_to_attrib(&sb, &a);
1018:
1019: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1020: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1021: a.perm &= 0777;
1022: if (!pflag)
1023: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1024:
1025: buffer_init(&msg);
1026:
1027: /* Send open request */
1.23 djm 1028: id = conn->msg_id++;
1.1 djm 1029: buffer_put_char(&msg, SSH2_FXP_OPEN);
1030: buffer_put_int(&msg, id);
1031: buffer_put_cstring(&msg, remote_path);
1032: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1033: encode_attrib(&msg, &a);
1.23 djm 1034: send_msg(conn->fd_out, &msg);
1.33 deraadt 1035: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 1036:
1037: buffer_clear(&msg);
1038:
1.23 djm 1039: handle = get_handle(conn->fd_in, id, &handle_len);
1.1 djm 1040: if (handle == NULL) {
1041: close(local_fd);
1042: buffer_free(&msg);
1043: return(-1);
1044: }
1045:
1.21 djm 1046: startid = ackid = id + 1;
1.23 djm 1047: data = xmalloc(conn->transfer_buflen);
1.20 djm 1048:
1.1 djm 1049: /* Read from local and write to remote */
1050: offset = 0;
1.39 fgsch 1051: if (showprogress)
1052: start_progress_meter(local_path, sb.st_size, &offset);
1053:
1.19 deraadt 1054: for (;;) {
1.1 djm 1055: int len;
1056:
1057: /*
1.51 deraadt 1058: * Can't use atomicio here because it returns 0 on EOF,
1.49 djm 1059: * thus losing the last block of the file.
1.51 deraadt 1060: * Simulate an EOF on interrupt, allowing ACKs from the
1.49 djm 1061: * server to drain.
1.1 djm 1062: */
1.49 djm 1063: if (interrupted)
1064: len = 0;
1065: else do
1.23 djm 1066: len = read(local_fd, data, conn->transfer_buflen);
1.1 djm 1067: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1068:
1069: if (len == -1)
1070: fatal("Couldn't read from \"%s\": %s", local_path,
1071: strerror(errno));
1.21 djm 1072:
1073: if (len != 0) {
1.22 djm 1074: ack = xmalloc(sizeof(*ack));
1075: ack->id = ++id;
1076: ack->offset = offset;
1077: ack->len = len;
1078: TAILQ_INSERT_TAIL(&acks, ack, tq);
1079:
1.21 djm 1080: buffer_clear(&msg);
1081: buffer_put_char(&msg, SSH2_FXP_WRITE);
1.22 djm 1082: buffer_put_int(&msg, ack->id);
1.21 djm 1083: buffer_put_string(&msg, handle, handle_len);
1084: buffer_put_int64(&msg, offset);
1085: buffer_put_string(&msg, data, len);
1.23 djm 1086: send_msg(conn->fd_out, &msg);
1.33 deraadt 1087: debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1.37 deraadt 1088: id, (unsigned long long)offset, len);
1.22 djm 1089: } else if (TAILQ_FIRST(&acks) == NULL)
1.1 djm 1090: break;
1091:
1.22 djm 1092: if (ack == NULL)
1093: fatal("Unexpected ACK %u", id);
1094:
1.28 markus 1095: if (id == startid || len == 0 ||
1.23 djm 1096: id - ackid >= conn->num_requests) {
1.32 markus 1097: u_int r_id;
1.31 djm 1098:
1.22 djm 1099: buffer_clear(&msg);
1.23 djm 1100: get_msg(conn->fd_in, &msg);
1.22 djm 1101: type = buffer_get_char(&msg);
1.31 djm 1102: r_id = buffer_get_int(&msg);
1.22 djm 1103:
1104: if (type != SSH2_FXP_STATUS)
1105: fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1106: "got %d", SSH2_FXP_STATUS, type);
1107:
1108: status = buffer_get_int(&msg);
1109: debug3("SSH2_FXP_STATUS %d", status);
1110:
1111: /* Find the request in our queue */
1.53 deraadt 1112: for (ack = TAILQ_FIRST(&acks);
1.31 djm 1113: ack != NULL && ack->id != r_id;
1.22 djm 1114: ack = TAILQ_NEXT(ack, tq))
1115: ;
1116: if (ack == NULL)
1.33 deraadt 1117: fatal("Can't find request for ID %u", r_id);
1.22 djm 1118: TAILQ_REMOVE(&acks, ack, tq);
1119:
1.21 djm 1120: if (status != SSH2_FX_OK) {
1121: error("Couldn't write to remote file \"%s\": %s",
1.37 deraadt 1122: remote_path, fx2txt(status));
1.23 djm 1123: do_close(conn, handle, handle_len);
1.21 djm 1124: close(local_fd);
1.42 markus 1125: xfree(data);
1126: xfree(ack);
1.21 djm 1127: goto done;
1128: }
1.33 deraadt 1129: debug3("In write loop, ack for %u %u bytes at %llu",
1.56 djm 1130: ack->id, ack->len, (unsigned long long)ack->offset);
1.21 djm 1131: ++ackid;
1.34 deraadt 1132: xfree(ack);
1.1 djm 1133: }
1134: offset += len;
1135: }
1.39 fgsch 1136: if (showprogress)
1137: stop_progress_meter();
1.20 djm 1138: xfree(data);
1.1 djm 1139:
1140: if (close(local_fd) == -1) {
1141: error("Couldn't close local file \"%s\": %s", local_path,
1142: strerror(errno));
1.23 djm 1143: do_close(conn, handle, handle_len);
1.5 djm 1144: status = -1;
1145: goto done;
1.1 djm 1146: }
1147:
1.10 djm 1148: /* Override umask and utimes if asked */
1149: if (pflag)
1.23 djm 1150: do_fsetstat(conn, handle, handle_len, &a);
1.10 djm 1151:
1.23 djm 1152: status = do_close(conn, handle, handle_len);
1.5 djm 1153:
1154: done:
1155: xfree(handle);
1156: buffer_free(&msg);
1.23 djm 1157: return(status);
1.1 djm 1158: }