Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.21
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.21 ! djm 31: RCSID("$OpenBSD: sftp-client.c,v 1.20 2002/02/05 00:00:46 djm Exp $");
! 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.1 djm 899: u_int handle_len, id;
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.1 djm 907:
908: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
909: error("Couldn't open local file \"%s\" for reading: %s",
910: local_path, strerror(errno));
911: return(-1);
912: }
913: if (fstat(local_fd, &sb) == -1) {
914: error("Couldn't fstat local file \"%s\": %s",
915: local_path, strerror(errno));
916: close(local_fd);
917: return(-1);
918: }
919: stat_to_attrib(&sb, &a);
920:
921: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
922: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
923: a.perm &= 0777;
924: if (!pflag)
925: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
926:
927: buffer_init(&msg);
928:
929: /* Send open request */
1.4 djm 930: id = msg_id++;
1.1 djm 931: buffer_put_char(&msg, SSH2_FXP_OPEN);
932: buffer_put_int(&msg, id);
933: buffer_put_cstring(&msg, remote_path);
934: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
935: encode_attrib(&msg, &a);
936: send_msg(fd_out, &msg);
937: debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
938:
939: buffer_clear(&msg);
940:
941: handle = get_handle(fd_in, id, &handle_len);
942: if (handle == NULL) {
943: close(local_fd);
944: buffer_free(&msg);
945: return(-1);
946: }
947:
1.21 ! djm 948: startid = ackid = id + 1;
1.20 djm 949: data = xmalloc(buflen);
950:
1.1 djm 951: /* Read from local and write to remote */
952: offset = 0;
1.19 deraadt 953: for (;;) {
1.1 djm 954: int len;
955:
956: /*
957: * Can't use atomicio here because it returns 0 on EOF, thus losing
958: * the last block of the file
959: */
960: do
1.20 djm 961: len = read(local_fd, data, buflen);
1.1 djm 962: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
963:
964: if (len == -1)
965: fatal("Couldn't read from \"%s\": %s", local_path,
966: strerror(errno));
1.21 ! djm 967:
! 968: if (len != 0) {
! 969: buffer_clear(&msg);
! 970: buffer_put_char(&msg, SSH2_FXP_WRITE);
! 971: buffer_put_int(&msg, ++id);
! 972: buffer_put_string(&msg, handle, handle_len);
! 973: buffer_put_int64(&msg, offset);
! 974: buffer_put_string(&msg, data, len);
! 975: send_msg(fd_out, &msg);
! 976: debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
! 977: id, (u_int64_t)offset, len);
! 978: } else if ( id < ackid )
1.1 djm 979: break;
980:
1.21 ! djm 981: if (id == startid || len == 0 ||
! 982: id - ackid >= num_requests) {
! 983: status = get_status(fd_in, ackid);
! 984: if (status != SSH2_FX_OK) {
! 985: error("Couldn't write to remote file \"%s\": %s",
! 986: remote_path, fx2txt(status));
! 987: do_close(fd_in, fd_out, handle, handle_len);
! 988: close(local_fd);
! 989: goto done;
! 990: }
! 991: debug3("In write loop, got %d offset %llu", len,
! 992: (u_int64_t)offset);
! 993: ++ackid;
1.1 djm 994: }
995:
996: offset += len;
997: }
1.20 djm 998: xfree(data);
1.1 djm 999:
1000: if (close(local_fd) == -1) {
1001: error("Couldn't close local file \"%s\": %s", local_path,
1002: strerror(errno));
1003: do_close(fd_in, fd_out, handle, handle_len);
1.5 djm 1004: status = -1;
1005: goto done;
1.1 djm 1006: }
1007:
1.10 djm 1008: /* Override umask and utimes if asked */
1009: if (pflag)
1010: do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
1011:
1.5 djm 1012: status = do_close(fd_in, fd_out, handle, handle_len);
1013:
1014: done:
1015: xfree(handle);
1016: buffer_free(&msg);
1017: return status;
1.1 djm 1018: }