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