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