Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.2
1.1 djm 1: /*
2: * Copyright (c) 2001 Damien Miller. All rights reserved.
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: redesign to allow concurrent overlapped operations */
28: /* XXX: we use fatal too much, error may be more appropriate in places */
29: /* XXX: copy between two remote sites */
30:
31: #include "includes.h"
1.2 ! deraadt 32: RCSID("$OpenBSD: sftp-client.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
1.1 djm 33:
34: #include "ssh.h"
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: #include "pathnames.h"
42:
43: #include "sftp.h"
44: #include "sftp-common.h"
45: #include "sftp-client.h"
46:
47: /* How much data to read/write at at time during copies */
48: /* XXX: what should this be? */
49: #define COPY_SIZE 8192
50:
51: void
52: send_msg(int fd, Buffer *m)
53: {
54: int mlen = buffer_len(m);
55: int len;
56: Buffer oqueue;
57:
58: buffer_init(&oqueue);
59: buffer_put_int(&oqueue, mlen);
60: buffer_append(&oqueue, buffer_ptr(m), mlen);
61: buffer_consume(m, mlen);
62:
63: len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
64: if (len <= 0)
65: fatal("Couldn't send packet: %s", strerror(errno));
66:
67: buffer_free(&oqueue);
68: }
69:
70: void
71: get_msg(int fd, Buffer *m)
72: {
73: u_int len, msg_len;
74: unsigned char buf[4096];
75:
76: len = atomicio(read, fd, buf, 4);
77: if (len != 4)
78: fatal("Couldn't read packet: %s", strerror(errno));
79:
80: msg_len = GET_32BIT(buf);
81: if (msg_len > 256 * 1024)
82: fatal("Received message too long %d", msg_len);
83:
84: while (msg_len) {
85: len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
86: if (len <= 0)
87: fatal("Couldn't read packet: %s", strerror(errno));
88:
89: msg_len -= len;
90: buffer_append(m, buf, len);
91: }
92: }
93:
94: void
95: send_string_request(int fd, u_int id, u_int code, char *s,
96: u_int len)
97: {
98: Buffer msg;
99:
100: buffer_init(&msg);
101: buffer_put_char(&msg, code);
102: buffer_put_int(&msg, id);
103: buffer_put_string(&msg, s, len);
104: send_msg(fd, &msg);
105: debug3("Sent message fd %d T:%d I:%d", fd, code, id);
106: buffer_free(&msg);
107: }
108:
109: void
110: send_string_attrs_request(int fd, u_int id, u_int code, char *s,
111: u_int len, Attrib *a)
112: {
113: Buffer msg;
114:
115: buffer_init(&msg);
116: buffer_put_char(&msg, code);
117: buffer_put_int(&msg, id);
118: buffer_put_string(&msg, s, len);
119: encode_attrib(&msg, a);
120: send_msg(fd, &msg);
121: debug3("Sent message fd %d T:%d I:%d", fd, code, id);
122: buffer_free(&msg);
123: }
124:
125: u_int
126: get_status(int fd, int expected_id)
127: {
128: Buffer msg;
129: u_int type, id, status;
130:
131: buffer_init(&msg);
132: get_msg(fd, &msg);
133: type = buffer_get_char(&msg);
134: id = buffer_get_int(&msg);
135:
136: if (id != expected_id)
137: fatal("ID mismatch (%d != %d)", id, expected_id);
138: if (type != SSH2_FXP_STATUS)
139: fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d",
140: SSH2_FXP_STATUS, type);
141:
142: status = buffer_get_int(&msg);
143: buffer_free(&msg);
144:
145: debug3("SSH2_FXP_STATUS %d", status);
146:
147: return(status);
148: }
149:
150: char *
151: get_handle(int fd, u_int expected_id, u_int *len)
152: {
153: Buffer msg;
154: u_int type, id;
155: char *handle;
156:
157: buffer_init(&msg);
158: get_msg(fd, &msg);
159: type = buffer_get_char(&msg);
160: id = buffer_get_int(&msg);
161:
162: if (id != expected_id)
163: fatal("ID mismatch (%d != %d)", id, expected_id);
164: if (type == SSH2_FXP_STATUS) {
165: int status = buffer_get_int(&msg);
166:
167: error("Couldn't get handle: %s", fx2txt(status));
168: return(NULL);
169: } else if (type != SSH2_FXP_HANDLE)
170: fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d",
171: SSH2_FXP_HANDLE, type);
172:
173: handle = buffer_get_string(&msg, len);
174: buffer_free(&msg);
175:
176: return(handle);
177: }
178:
179: Attrib *
180: get_decode_stat(int fd, u_int expected_id)
181: {
182: Buffer msg;
183: u_int type, id;
184: Attrib *a;
185:
186: buffer_init(&msg);
187: get_msg(fd, &msg);
188:
189: type = buffer_get_char(&msg);
190: id = buffer_get_int(&msg);
191:
192: debug3("Received stat reply T:%d I:%d", type, id);
193: if (id != expected_id)
194: fatal("ID mismatch (%d != %d)", id, expected_id);
195: if (type == SSH2_FXP_STATUS) {
196: int status = buffer_get_int(&msg);
197:
198: error("Couldn't stat remote file: %s", fx2txt(status));
199: return(NULL);
200: } else if (type != SSH2_FXP_ATTRS) {
201: fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
202: SSH2_FXP_ATTRS, type);
203: }
204: a = decode_attrib(&msg);
205: buffer_free(&msg);
206:
207: return(a);
208: }
209:
210: int
211: do_init(int fd_in, int fd_out)
212: {
213: int type, version;
214: Buffer msg;
215:
216: buffer_init(&msg);
217: buffer_put_char(&msg, SSH2_FXP_INIT);
218: buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
219: send_msg(fd_out, &msg);
220:
221: buffer_clear(&msg);
222:
223: get_msg(fd_in, &msg);
224:
225: /* Expecting a VERSION reply */
226: if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
227: error("Invalid packet back from SSH2_FXP_INIT (type %d)",
228: type);
229: buffer_free(&msg);
230: return(-1);
231: }
232: version = buffer_get_int(&msg);
233:
234: debug2("Remote version: %d", version);
235:
236: /* Check for extensions */
237: while (buffer_len(&msg) > 0) {
238: char *name = buffer_get_string(&msg, NULL);
239: char *value = buffer_get_string(&msg, NULL);
240:
241: debug2("Init extension: \"%s\"", name);
242: xfree(name);
243: xfree(value);
244: }
245:
246: buffer_free(&msg);
247: return(0);
248: }
249:
250: int
251: do_close(int fd_in, int fd_out, char *handle, u_int handle_len)
252: {
253: u_int id, status;
254: Buffer msg;
255:
256: buffer_init(&msg);
257:
258: id = arc4random();
259: buffer_put_char(&msg, SSH2_FXP_CLOSE);
260: buffer_put_int(&msg, id);
261: buffer_put_string(&msg, handle, handle_len);
262: send_msg(fd_out, &msg);
263: debug3("Sent message SSH2_FXP_CLOSE I:%d", id);
264:
265: status = get_status(fd_in, id);
266: if (status != SSH2_FX_OK)
267: error("Couldn't close file: %s", fx2txt(status));
268:
269: buffer_free(&msg);
270:
271: return(status);
272: }
273:
274: int
275: do_ls(int fd_in, int fd_out, char *path)
276: {
277: Buffer msg;
278: u_int type, id, handle_len, i, expected_id;
279: char *handle;
280:
281: id = arc4random();
282:
283: buffer_init(&msg);
284: buffer_put_char(&msg, SSH2_FXP_OPENDIR);
285: buffer_put_int(&msg, id);
286: buffer_put_cstring(&msg, path);
287: send_msg(fd_out, &msg);
288:
289: buffer_clear(&msg);
290:
291: handle = get_handle(fd_in, id, &handle_len);
292: if (handle == NULL)
293: return(-1);
294:
295: for(;;) {
296: int count;
297:
298: expected_id = ++id;
299:
300: debug3("Sending SSH2_FXP_READDIR I:%d", id);
301:
302: buffer_clear(&msg);
303: buffer_put_char(&msg, SSH2_FXP_READDIR);
304: buffer_put_int(&msg, id);
305: buffer_put_string(&msg, handle, handle_len);
306: send_msg(fd_out, &msg);
307:
308: buffer_clear(&msg);
309:
310: get_msg(fd_in, &msg);
311:
312: type = buffer_get_char(&msg);
313: id = buffer_get_int(&msg);
314:
315: debug3("Received reply T:%d I:%d", type, id);
316:
317: if (id != expected_id)
318: fatal("ID mismatch (%d != %d)", id, expected_id);
319:
320: if (type == SSH2_FXP_STATUS) {
321: int status = buffer_get_int(&msg);
322:
323: debug3("Received SSH2_FXP_STATUS %d", status);
324:
325: if (status == SSH2_FX_EOF) {
326: break;
327: } else {
328: error("Couldn't read directory: %s",
329: fx2txt(status));
330: do_close(fd_in, fd_out, handle, handle_len);
331: return(NULL);
332: }
333: } else if (type != SSH2_FXP_NAME)
334: fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
335: SSH2_FXP_NAME, type);
336:
337: count = buffer_get_int(&msg);
338: debug3("Received %i SSH2_FXP_NAME responses", count);
339: for(i = 0; i < count; i++) {
340: char *filename, *longname;
341: Attrib *a;
342:
343: filename = buffer_get_string(&msg, NULL);
344: longname = buffer_get_string(&msg, NULL);
345: a = decode_attrib(&msg);
346:
347: printf("%s\n", longname);
348:
349: xfree(filename);
350: xfree(longname);
351: }
352: }
353:
354: buffer_free(&msg);
355: do_close(fd_in, fd_out, handle, handle_len);
356: xfree(handle);
357:
358: return(0);
359: }
360:
361: int
362: do_rm(int fd_in, int fd_out, char *path)
363: {
364: u_int status, id;
365:
366: debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
367:
368: id = arc4random();
369: send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path));
370: status = get_status(fd_in, id);
371: if (status != SSH2_FX_OK)
372: error("Couldn't delete file: %s", fx2txt(status));
373: return(status);
374: }
375:
376: int
377: do_mkdir(int fd_in, int fd_out, char *path, Attrib *a)
378: {
379: u_int status, id;
380:
381: id = arc4random();
382: send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path,
383: strlen(path), a);
384:
385: status = get_status(fd_in, id);
386: if (status != SSH2_FX_OK)
387: error("Couldn't create directory: %s", fx2txt(status));
388:
389: return(status);
390: }
391:
392: int
393: do_rmdir(int fd_in, int fd_out, char *path)
394: {
395: u_int status, id;
396:
397: id = arc4random();
398: send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path));
399:
400: status = get_status(fd_in, id);
401: if (status != SSH2_FX_OK)
402: error("Couldn't remove directory: %s", fx2txt(status));
403:
404: return(status);
405: }
406:
407: Attrib *
408: do_stat(int fd_in, int fd_out, char *path)
409: {
410: u_int id;
411:
412: id = arc4random();
413: send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
414: return(get_decode_stat(fd_in, id));
415: }
416:
417: Attrib *
418: do_lstat(int fd_in, int fd_out, char *path)
419: {
420: u_int id;
421:
422: id = arc4random();
423: send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
424: return(get_decode_stat(fd_in, id));
425: }
426:
427: Attrib *
428: do_fstat(int fd_in, int fd_out, char *handle,
429: u_int handle_len)
430: {
431: u_int id;
432:
433: id = arc4random();
434: send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
435: return(get_decode_stat(fd_in, id));
436: }
437:
438: int
439: do_setstat(int fd_in, int fd_out, char *path, Attrib *a)
440: {
441: u_int status, id;
442:
443: id = arc4random();
444: send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path,
445: strlen(path), a);
446:
447: status = get_status(fd_in, id);
448: if (status != SSH2_FX_OK)
449: error("Couldn't setstat on \"%s\": %s", path,
450: fx2txt(status));
451:
452: return(status);
453: }
454:
455: int
456: do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len,
457: Attrib *a)
458: {
459: u_int status, id;
460:
461: id = arc4random();
462: send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle,
463: handle_len, a);
464:
465: status = get_status(fd_in, id);
466: if (status != SSH2_FX_OK)
467: error("Couldn't fsetstat: %s", fx2txt(status));
468:
469: return(status);
470: }
471:
472: char *
473: do_realpath(int fd_in, int fd_out, char *path)
474: {
475: Buffer msg;
476: u_int type, expected_id, count, id;
477: char *filename, *longname;
478: Attrib *a;
479:
480: expected_id = id = arc4random();
481: send_string_request(fd_out, id, SSH2_FXP_REALPATH, path,
482: strlen(path));
483:
484: buffer_init(&msg);
485:
486: get_msg(fd_in, &msg);
487: type = buffer_get_char(&msg);
488: id = buffer_get_int(&msg);
489:
490: if (id != expected_id)
491: fatal("ID mismatch (%d != %d)", id, expected_id);
492:
493: if (type == SSH2_FXP_STATUS) {
494: u_int status = buffer_get_int(&msg);
495:
496: error("Couldn't canonicalise: %s", fx2txt(status));
497: return(NULL);
498: } else if (type != SSH2_FXP_NAME)
499: fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
500: SSH2_FXP_NAME, type);
501:
502: count = buffer_get_int(&msg);
503: if (count != 1)
504: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
505:
506: filename = buffer_get_string(&msg, NULL);
507: longname = buffer_get_string(&msg, NULL);
508: a = decode_attrib(&msg);
509:
510: debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
511:
512: xfree(longname);
513:
514: buffer_free(&msg);
515:
516: return(filename);
517: }
518:
519: int
520: do_rename(int fd_in, int fd_out, char *oldpath, char *newpath)
521: {
522: Buffer msg;
523: u_int status, id;
524:
525: buffer_init(&msg);
526:
527: /* Send rename request */
528: id = arc4random();
529: buffer_put_char(&msg, SSH2_FXP_RENAME);
530: buffer_put_int(&msg, id);
531: buffer_put_cstring(&msg, oldpath);
532: buffer_put_cstring(&msg, newpath);
533: send_msg(fd_out, &msg);
534: debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
535: newpath);
536: buffer_free(&msg);
537:
538: status = get_status(fd_in, id);
539: if (status != SSH2_FX_OK)
540: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
541: fx2txt(status));
542:
543: return(status);
544: }
545:
546: int
547: do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
548: int pflag)
549: {
550: int local_fd;
551: u_int expected_id, handle_len, mode, type, id;
552: u_int64_t offset;
553: char *handle;
554: Buffer msg;
555: Attrib junk, *a;
556:
557: a = do_stat(fd_in, fd_out, remote_path);
558: if (a == NULL)
559: return(-1);
560:
561: /* XXX: should we preserve set[ug]id? */
562: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
563: mode = S_IWRITE | (a->perm & 0777);
564: else
565: mode = 0666;
566:
567: local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
568: if (local_fd == -1) {
569: error("Couldn't open local file \"%s\" for writing: %s",
570: local_path, strerror(errno));
571: return(errno);
572: }
573:
574: /* Override umask and utimes if asked */
575: if (pflag && fchmod(local_fd, mode) == -1)
576: error("Couldn't set mode on \"%s\": %s", local_path,
577: strerror(errno));
578: if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
579: struct timeval tv;
580:
581: tv.tv_sec = a->atime;
582: tv.tv_usec = a->mtime;
583: if (utimes(local_path, &tv) == -1)
584: error("Can't set times on \"%s\": %s", local_path,
585: strerror(errno));
586: }
587:
588: buffer_init(&msg);
589:
590: /* Send open request */
591: id = arc4random();
592: buffer_put_char(&msg, SSH2_FXP_OPEN);
593: buffer_put_int(&msg, id);
594: buffer_put_cstring(&msg, remote_path);
595: buffer_put_int(&msg, SSH2_FXF_READ);
596: attrib_clear(&junk); /* Send empty attributes */
597: encode_attrib(&msg, &junk);
598: send_msg(fd_out, &msg);
599: debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
600:
601: handle = get_handle(fd_in, id, &handle_len);
602: if (handle == NULL) {
603: buffer_free(&msg);
604: close(local_fd);
605: return(-1);
606: }
607:
608: /* Read from remote and write to local */
609: offset = 0;
610: for(;;) {
611: u_int len;
612: char *data;
613:
614: expected_id = ++id;
615:
616: buffer_clear(&msg);
617: buffer_put_char(&msg, SSH2_FXP_READ);
618: buffer_put_int(&msg, id);
619: buffer_put_string(&msg, handle, handle_len);
620: buffer_put_int64(&msg, offset);
621: buffer_put_int(&msg, COPY_SIZE);
622: send_msg(fd_out, &msg);
623: debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
1.2 ! deraadt 624: id, (unsigned long long)offset, COPY_SIZE);
1.1 djm 625:
626: buffer_clear(&msg);
627:
628: get_msg(fd_in, &msg);
629: type = buffer_get_char(&msg);
630: id = buffer_get_int(&msg);
631: debug3("Received reply T:%d I:%d", type, id);
632: if (id != expected_id)
633: fatal("ID mismatch (%d != %d)", id, expected_id);
634: if (type == SSH2_FXP_STATUS) {
635: int status = buffer_get_int(&msg);
636:
637: if (status == SSH2_FX_EOF)
638: break;
639: else {
640: error("Couldn't read from remote "
641: "file \"%s\" : %s", remote_path,
642: fx2txt(status));
643: do_close(fd_in, fd_out, handle, handle_len);
644: xfree(handle);
645: close(local_fd);
646: buffer_free(&msg);
647: return(status);
648: }
649: } else if (type != SSH2_FXP_DATA) {
650: fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
651: SSH2_FXP_DATA, type);
652: }
653:
654: data = buffer_get_string(&msg, &len);
655: if (len > COPY_SIZE)
656: fatal("Received more data than asked for %d > %d",
657: len, COPY_SIZE);
658:
1.2 ! deraadt 659: debug3("In read loop, got %d offset %lld", len,
! 660: (unsigned long long)offset);
1.1 djm 661: if (atomicio(write, local_fd, data, len) != len) {
662: error("Couldn't write to \"%s\": %s", local_path,
663: strerror(errno));
664: do_close(fd_in, fd_out, handle, handle_len);
665: xfree(handle);
666: close(local_fd);
667: xfree(data);
668: buffer_free(&msg);
669: return(-1);
670: }
671:
672: offset += len;
673: xfree(data);
674: }
675: xfree(handle);
676: buffer_free(&msg);
677: close(local_fd);
678:
679: return(do_close(fd_in, fd_out, handle, handle_len));
680: }
681:
682: int
683: do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
684: int pflag)
685: {
686: int local_fd;
687: u_int handle_len, id;
688: u_int64_t offset;
689: char *handle;
690: Buffer msg;
691: struct stat sb;
692: Attrib a;
693:
694: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
695: error("Couldn't open local file \"%s\" for reading: %s",
696: local_path, strerror(errno));
697: return(-1);
698: }
699: if (fstat(local_fd, &sb) == -1) {
700: error("Couldn't fstat local file \"%s\": %s",
701: local_path, strerror(errno));
702: close(local_fd);
703: return(-1);
704: }
705: stat_to_attrib(&sb, &a);
706:
707: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
708: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
709: a.perm &= 0777;
710: if (!pflag)
711: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
712:
713: buffer_init(&msg);
714:
715: /* Send open request */
716: id = arc4random();
717: buffer_put_char(&msg, SSH2_FXP_OPEN);
718: buffer_put_int(&msg, id);
719: buffer_put_cstring(&msg, remote_path);
720: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
721: encode_attrib(&msg, &a);
722: send_msg(fd_out, &msg);
723: debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
724:
725: buffer_clear(&msg);
726:
727: handle = get_handle(fd_in, id, &handle_len);
728: if (handle == NULL) {
729: close(local_fd);
730: buffer_free(&msg);
731: return(-1);
732: }
733:
734: /* Override umask and utimes if asked */
735: if (pflag)
736: do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
737:
738: /* Read from local and write to remote */
739: offset = 0;
740: for(;;) {
741: int len;
742: char data[COPY_SIZE];
743: u_int status;
744:
745: /*
746: * Can't use atomicio here because it returns 0 on EOF, thus losing
747: * the last block of the file
748: */
749: do
750: len = read(local_fd, data, COPY_SIZE);
751: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
752:
753: if (len == -1)
754: fatal("Couldn't read from \"%s\": %s", local_path,
755: strerror(errno));
756: if (len == 0)
757: break;
758:
759: buffer_clear(&msg);
760: buffer_put_char(&msg, SSH2_FXP_WRITE);
761: buffer_put_int(&msg, ++id);
762: buffer_put_string(&msg, handle, handle_len);
763: buffer_put_int64(&msg, offset);
764: buffer_put_string(&msg, data, len);
765: send_msg(fd_out, &msg);
766: debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
1.2 ! deraadt 767: id, (unsigned long long)offset, len);
1.1 djm 768:
769: status = get_status(fd_in, id);
770: if (status != SSH2_FX_OK) {
771: error("Couldn't write to remote file \"%s\": %s",
772: remote_path, fx2txt(status));
773: do_close(fd_in, fd_out, handle, handle_len);
774: xfree(handle);
775: close(local_fd);
776: return(-1);
777: }
1.2 ! deraadt 778: debug3("In write loop, got %d offset %llu", len,
! 779: (unsigned long long)offset);
1.1 djm 780:
781: offset += len;
782: }
783: xfree(handle);
784: buffer_free(&msg);
785:
786: if (close(local_fd) == -1) {
787: error("Couldn't close local file \"%s\": %s", local_path,
788: strerror(errno));
789: do_close(fd_in, fd_out, handle, handle_len);
790: return(-1);
791: }
792:
793: return(do_close(fd_in, fd_out, handle, handle_len));
794: }