The xfer mechanism wasn't gracefully terminating when an error was
[u/mdw/putty] / sftp.c
1 /*
2 * sftp.c: SFTP generic client code.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <limits.h>
10
11 #include "misc.h"
12 #include "int64.h"
13 #include "tree234.h"
14 #include "sftp.h"
15
16 #define GET_32BIT(cp) \
17 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
18 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
19 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
20 ((unsigned long)(unsigned char)(cp)[3]))
21
22 #define PUT_32BIT(cp, value) { \
23 (cp)[0] = (unsigned char)((value) >> 24); \
24 (cp)[1] = (unsigned char)((value) >> 16); \
25 (cp)[2] = (unsigned char)((value) >> 8); \
26 (cp)[3] = (unsigned char)(value); }
27
28 struct sftp_packet {
29 char *data;
30 int length, maxlen;
31 int savedpos;
32 int type;
33 };
34
35 static const char *fxp_error_message;
36 static int fxp_errtype;
37
38 static void fxp_internal_error(char *msg);
39
40 /* ----------------------------------------------------------------------
41 * SFTP packet construction functions.
42 */
43 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
44 {
45 if (pkt->maxlen < length) {
46 pkt->maxlen = length + 256;
47 pkt->data = sresize(pkt->data, pkt->maxlen, char);
48 }
49 }
50 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
51 {
52 pkt->length += len;
53 sftp_pkt_ensure(pkt, pkt->length);
54 memcpy(pkt->data + pkt->length - len, data, len);
55 }
56 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
57 {
58 sftp_pkt_adddata(pkt, &byte, 1);
59 }
60 static struct sftp_packet *sftp_pkt_init(int pkt_type)
61 {
62 struct sftp_packet *pkt;
63 pkt = snew(struct sftp_packet);
64 pkt->data = NULL;
65 pkt->savedpos = -1;
66 pkt->length = 0;
67 pkt->maxlen = 0;
68 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
69 return pkt;
70 }
71 /*
72 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
73 {
74 sftp_pkt_adddata(pkt, &value, 1);
75 }
76 */
77 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
78 unsigned long value)
79 {
80 unsigned char x[4];
81 PUT_32BIT(x, value);
82 sftp_pkt_adddata(pkt, x, 4);
83 }
84 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
85 {
86 unsigned char x[8];
87 PUT_32BIT(x, value.hi);
88 PUT_32BIT(x + 4, value.lo);
89 sftp_pkt_adddata(pkt, x, 8);
90 }
91 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
92 {
93 sftp_pkt_adduint32(pkt, 0);
94 pkt->savedpos = pkt->length;
95 }
96 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
97 {
98 sftp_pkt_adddata(pkt, data, strlen(data));
99 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
100 }
101 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
102 char *data, int len)
103 {
104 sftp_pkt_adddata(pkt, data, len);
105 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
106 }
107 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
108 {
109 sftp_pkt_addstring_start(pkt);
110 sftp_pkt_addstring_str(pkt, data);
111 }
112 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
113 {
114 sftp_pkt_adduint32(pkt, attrs.flags);
115 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
116 sftp_pkt_adduint32(pkt, attrs.size.hi);
117 sftp_pkt_adduint32(pkt, attrs.size.lo);
118 }
119 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
120 sftp_pkt_adduint32(pkt, attrs.uid);
121 sftp_pkt_adduint32(pkt, attrs.gid);
122 }
123 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
124 sftp_pkt_adduint32(pkt, attrs.permissions);
125 }
126 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
127 sftp_pkt_adduint32(pkt, attrs.atime);
128 sftp_pkt_adduint32(pkt, attrs.mtime);
129 }
130 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
131 /*
132 * We currently don't support sending any extended
133 * attributes.
134 */
135 }
136 }
137
138 /* ----------------------------------------------------------------------
139 * SFTP packet decode functions.
140 */
141
142 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
143 {
144 unsigned char value;
145 if (pkt->length - pkt->savedpos < 1)
146 return 0; /* arrgh, no way to decline (FIXME?) */
147 value = (unsigned char) pkt->data[pkt->savedpos];
148 pkt->savedpos++;
149 return value;
150 }
151 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
152 {
153 unsigned long value;
154 if (pkt->length - pkt->savedpos < 4)
155 return 0; /* arrgh, no way to decline (FIXME?) */
156 value = GET_32BIT(pkt->data + pkt->savedpos);
157 pkt->savedpos += 4;
158 return value;
159 }
160 static void sftp_pkt_getstring(struct sftp_packet *pkt,
161 char **p, int *length)
162 {
163 *p = NULL;
164 if (pkt->length - pkt->savedpos < 4)
165 return;
166 *length = GET_32BIT(pkt->data + pkt->savedpos);
167 pkt->savedpos += 4;
168 if (pkt->length - pkt->savedpos < *length)
169 return;
170 *p = pkt->data + pkt->savedpos;
171 pkt->savedpos += *length;
172 }
173 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
174 {
175 struct fxp_attrs ret;
176 ret.flags = sftp_pkt_getuint32(pkt);
177 if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
178 unsigned long hi, lo;
179 hi = sftp_pkt_getuint32(pkt);
180 lo = sftp_pkt_getuint32(pkt);
181 ret.size = uint64_make(hi, lo);
182 }
183 if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
184 ret.uid = sftp_pkt_getuint32(pkt);
185 ret.gid = sftp_pkt_getuint32(pkt);
186 }
187 if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
188 ret.permissions = sftp_pkt_getuint32(pkt);
189 }
190 if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
191 ret.atime = sftp_pkt_getuint32(pkt);
192 ret.mtime = sftp_pkt_getuint32(pkt);
193 }
194 if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
195 int count;
196 count = sftp_pkt_getuint32(pkt);
197 while (count--) {
198 char *str;
199 int len;
200 /*
201 * We should try to analyse these, if we ever find one
202 * we recognise.
203 */
204 sftp_pkt_getstring(pkt, &str, &len);
205 sftp_pkt_getstring(pkt, &str, &len);
206 }
207 }
208 return ret;
209 }
210 static void sftp_pkt_free(struct sftp_packet *pkt)
211 {
212 if (pkt->data)
213 sfree(pkt->data);
214 sfree(pkt);
215 }
216
217 /* ----------------------------------------------------------------------
218 * Send and receive packet functions.
219 */
220 int sftp_send(struct sftp_packet *pkt)
221 {
222 int ret;
223 char x[4];
224 PUT_32BIT(x, pkt->length);
225 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
226 sftp_pkt_free(pkt);
227 return ret;
228 }
229 struct sftp_packet *sftp_recv(void)
230 {
231 struct sftp_packet *pkt;
232 char x[4];
233
234 if (!sftp_recvdata(x, 4))
235 return NULL;
236
237 pkt = snew(struct sftp_packet);
238 pkt->savedpos = 0;
239 pkt->length = pkt->maxlen = GET_32BIT(x);
240 pkt->data = snewn(pkt->length, char);
241
242 if (!sftp_recvdata(pkt->data, pkt->length)) {
243 sftp_pkt_free(pkt);
244 return NULL;
245 }
246
247 pkt->type = sftp_pkt_getbyte(pkt);
248
249 return pkt;
250 }
251
252 /* ----------------------------------------------------------------------
253 * Request ID allocation and temporary dispatch routines.
254 */
255
256 #define REQUEST_ID_OFFSET 256
257
258 struct sftp_request {
259 unsigned id;
260 int registered;
261 void *userdata;
262 };
263
264 static int sftp_reqcmp(void *av, void *bv)
265 {
266 struct sftp_request *a = (struct sftp_request *)av;
267 struct sftp_request *b = (struct sftp_request *)bv;
268 if (a->id < b->id)
269 return -1;
270 if (a->id > b->id)
271 return +1;
272 return 0;
273 }
274 static int sftp_reqfind(void *av, void *bv)
275 {
276 unsigned *a = (unsigned *) av;
277 struct sftp_request *b = (struct sftp_request *)bv;
278 if (*a < b->id)
279 return -1;
280 if (*a > b->id)
281 return +1;
282 return 0;
283 }
284
285 static tree234 *sftp_requests;
286
287 static struct sftp_request *sftp_alloc_request(void)
288 {
289 unsigned low, high, mid;
290 int tsize;
291 struct sftp_request *r;
292
293 if (sftp_requests == NULL)
294 sftp_requests = newtree234(sftp_reqcmp);
295
296 /*
297 * First-fit allocation of request IDs: always pick the lowest
298 * unused one. To do this, binary-search using the counted
299 * B-tree to find the largest ID which is in a contiguous
300 * sequence from the beginning. (Precisely everything in that
301 * sequence must have ID equal to its tree index plus
302 * REQUEST_ID_OFFSET.)
303 */
304 tsize = count234(sftp_requests);
305
306 low = -1;
307 high = tsize;
308 while (high - low > 1) {
309 mid = (high + low) / 2;
310 r = index234(sftp_requests, mid);
311 if (r->id == mid + REQUEST_ID_OFFSET)
312 low = mid; /* this one is fine */
313 else
314 high = mid; /* this one is past it */
315 }
316 /*
317 * Now low points to either -1, or the tree index of the
318 * largest ID in the initial sequence.
319 */
320 {
321 unsigned i = low + 1 + REQUEST_ID_OFFSET;
322 assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
323 }
324
325 /*
326 * So the request ID we need to create is
327 * low + 1 + REQUEST_ID_OFFSET.
328 */
329 r = snew(struct sftp_request);
330 r->id = low + 1 + REQUEST_ID_OFFSET;
331 r->registered = 0;
332 r->userdata = NULL;
333 add234(sftp_requests, r);
334 return r;
335 }
336
337 void sftp_cleanup_request(void)
338 {
339 if (sftp_requests == NULL) {
340 freetree234(sftp_requests);
341 sftp_requests = NULL;
342 }
343 }
344
345 void sftp_register(struct sftp_request *req)
346 {
347 req->registered = 1;
348 }
349
350 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
351 {
352 unsigned long id;
353 struct sftp_request *req;
354
355 if (!pktin) {
356 fxp_internal_error("did not receive a valid SFTP packet\n");
357 return NULL;
358 }
359
360 id = sftp_pkt_getuint32(pktin);
361 req = find234(sftp_requests, &id, sftp_reqfind);
362
363 if (!req || !req->registered) {
364 fxp_internal_error("request ID mismatch\n");
365 sftp_pkt_free(pktin);
366 return NULL;
367 }
368
369 del234(sftp_requests, req);
370
371 return req;
372 }
373
374 /* ----------------------------------------------------------------------
375 * String handling routines.
376 */
377
378 static char *mkstr(char *s, int len)
379 {
380 char *p = snewn(len + 1, char);
381 memcpy(p, s, len);
382 p[len] = '\0';
383 return p;
384 }
385
386 /* ----------------------------------------------------------------------
387 * SFTP primitives.
388 */
389
390 /*
391 * Deal with (and free) an FXP_STATUS packet. Return 1 if
392 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
393 * Also place the status into fxp_errtype.
394 */
395 static int fxp_got_status(struct sftp_packet *pktin)
396 {
397 static const char *const messages[] = {
398 /* SSH_FX_OK. The only time we will display a _message_ for this
399 * is if we were expecting something other than FXP_STATUS on
400 * success, so this is actually an error message! */
401 "unexpected OK response",
402 "end of file",
403 "no such file or directory",
404 "permission denied",
405 "failure",
406 "bad message",
407 "no connection",
408 "connection lost",
409 "operation unsupported",
410 };
411
412 if (pktin->type != SSH_FXP_STATUS) {
413 fxp_error_message = "expected FXP_STATUS packet";
414 fxp_errtype = -1;
415 } else {
416 fxp_errtype = sftp_pkt_getuint32(pktin);
417 if (fxp_errtype < 0 ||
418 fxp_errtype >= sizeof(messages) / sizeof(*messages))
419 fxp_error_message = "unknown error code";
420 else
421 fxp_error_message = messages[fxp_errtype];
422 }
423
424 if (fxp_errtype == SSH_FX_OK)
425 return 1;
426 else if (fxp_errtype == SSH_FX_EOF)
427 return 0;
428 else
429 return -1;
430 }
431
432 static void fxp_internal_error(char *msg)
433 {
434 fxp_error_message = msg;
435 fxp_errtype = -1;
436 }
437
438 const char *fxp_error(void)
439 {
440 return fxp_error_message;
441 }
442
443 int fxp_error_type(void)
444 {
445 return fxp_errtype;
446 }
447
448 /*
449 * Perform exchange of init/version packets. Return 0 on failure.
450 */
451 int fxp_init(void)
452 {
453 struct sftp_packet *pktout, *pktin;
454 int remotever;
455
456 pktout = sftp_pkt_init(SSH_FXP_INIT);
457 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
458 sftp_send(pktout);
459
460 pktin = sftp_recv();
461 if (!pktin) {
462 fxp_internal_error("could not connect");
463 return 0;
464 }
465 if (pktin->type != SSH_FXP_VERSION) {
466 fxp_internal_error("did not receive FXP_VERSION");
467 sftp_pkt_free(pktin);
468 return 0;
469 }
470 remotever = sftp_pkt_getuint32(pktin);
471 if (remotever > SFTP_PROTO_VERSION) {
472 fxp_internal_error
473 ("remote protocol is more advanced than we support");
474 sftp_pkt_free(pktin);
475 return 0;
476 }
477 /*
478 * In principle, this packet might also contain extension-
479 * string pairs. We should work through them and look for any
480 * we recognise. In practice we don't currently do so because
481 * we know we don't recognise _any_.
482 */
483 sftp_pkt_free(pktin);
484
485 return 1;
486 }
487
488 /*
489 * Canonify a pathname.
490 */
491 struct sftp_request *fxp_realpath_send(char *path)
492 {
493 struct sftp_request *req = sftp_alloc_request();
494 struct sftp_packet *pktout;
495
496 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
497 sftp_pkt_adduint32(pktout, req->id);
498 sftp_pkt_addstring_start(pktout);
499 sftp_pkt_addstring_str(pktout, path);
500 sftp_send(pktout);
501
502 return req;
503 }
504
505 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
506 {
507 sfree(req);
508
509 if (pktin->type == SSH_FXP_NAME) {
510 int count;
511 char *path;
512 int len;
513
514 count = sftp_pkt_getuint32(pktin);
515 if (count != 1) {
516 fxp_internal_error("REALPATH returned name count != 1\n");
517 sftp_pkt_free(pktin);
518 return NULL;
519 }
520 sftp_pkt_getstring(pktin, &path, &len);
521 if (!path) {
522 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
523 sftp_pkt_free(pktin);
524 return NULL;
525 }
526 path = mkstr(path, len);
527 sftp_pkt_free(pktin);
528 return path;
529 } else {
530 fxp_got_status(pktin);
531 sftp_pkt_free(pktin);
532 return NULL;
533 }
534 }
535
536 /*
537 * Open a file.
538 */
539 struct sftp_request *fxp_open_send(char *path, int type)
540 {
541 struct sftp_request *req = sftp_alloc_request();
542 struct sftp_packet *pktout;
543
544 pktout = sftp_pkt_init(SSH_FXP_OPEN);
545 sftp_pkt_adduint32(pktout, req->id);
546 sftp_pkt_addstring(pktout, path);
547 sftp_pkt_adduint32(pktout, type);
548 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
549 sftp_send(pktout);
550
551 return req;
552 }
553
554 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
555 struct sftp_request *req)
556 {
557 sfree(req);
558
559 if (pktin->type == SSH_FXP_HANDLE) {
560 char *hstring;
561 struct fxp_handle *handle;
562 int len;
563
564 sftp_pkt_getstring(pktin, &hstring, &len);
565 if (!hstring) {
566 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
567 sftp_pkt_free(pktin);
568 return NULL;
569 }
570 handle = snew(struct fxp_handle);
571 handle->hstring = mkstr(hstring, len);
572 handle->hlen = len;
573 sftp_pkt_free(pktin);
574 return handle;
575 } else {
576 fxp_got_status(pktin);
577 sftp_pkt_free(pktin);
578 return NULL;
579 }
580 }
581
582 /*
583 * Open a directory.
584 */
585 struct sftp_request *fxp_opendir_send(char *path)
586 {
587 struct sftp_request *req = sftp_alloc_request();
588 struct sftp_packet *pktout;
589
590 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
591 sftp_pkt_adduint32(pktout, req->id);
592 sftp_pkt_addstring(pktout, path);
593 sftp_send(pktout);
594
595 return req;
596 }
597
598 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
599 struct sftp_request *req)
600 {
601 sfree(req);
602 if (pktin->type == SSH_FXP_HANDLE) {
603 char *hstring;
604 struct fxp_handle *handle;
605 int len;
606
607 sftp_pkt_getstring(pktin, &hstring, &len);
608 if (!hstring) {
609 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
610 sftp_pkt_free(pktin);
611 return NULL;
612 }
613 handle = snew(struct fxp_handle);
614 handle->hstring = mkstr(hstring, len);
615 handle->hlen = len;
616 sftp_pkt_free(pktin);
617 return handle;
618 } else {
619 fxp_got_status(pktin);
620 sftp_pkt_free(pktin);
621 return NULL;
622 }
623 }
624
625 /*
626 * Close a file/dir.
627 */
628 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
629 {
630 struct sftp_request *req = sftp_alloc_request();
631 struct sftp_packet *pktout;
632
633 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
634 sftp_pkt_adduint32(pktout, req->id);
635 sftp_pkt_addstring_start(pktout);
636 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
637 sftp_send(pktout);
638
639 sfree(handle->hstring);
640 sfree(handle);
641
642 return req;
643 }
644
645 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
646 {
647 sfree(req);
648 fxp_got_status(pktin);
649 sftp_pkt_free(pktin);
650 }
651
652 struct sftp_request *fxp_mkdir_send(char *path)
653 {
654 struct sftp_request *req = sftp_alloc_request();
655 struct sftp_packet *pktout;
656
657 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
658 sftp_pkt_adduint32(pktout, req->id);
659 sftp_pkt_addstring(pktout, path);
660 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
661 sftp_send(pktout);
662
663 return req;
664 }
665
666 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
667 {
668 int id;
669 sfree(req);
670 id = fxp_got_status(pktin);
671 sftp_pkt_free(pktin);
672 if (id != 1) {
673 return 0;
674 }
675 return 1;
676 }
677
678 struct sftp_request *fxp_rmdir_send(char *path)
679 {
680 struct sftp_request *req = sftp_alloc_request();
681 struct sftp_packet *pktout;
682
683 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
684 sftp_pkt_adduint32(pktout, req->id);
685 sftp_pkt_addstring(pktout, path);
686 sftp_send(pktout);
687
688 return req;
689 }
690
691 int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
692 {
693 int id;
694 sfree(req);
695 id = fxp_got_status(pktin);
696 sftp_pkt_free(pktin);
697 if (id != 1) {
698 return 0;
699 }
700 return 1;
701 }
702
703 struct sftp_request *fxp_remove_send(char *fname)
704 {
705 struct sftp_request *req = sftp_alloc_request();
706 struct sftp_packet *pktout;
707
708 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
709 sftp_pkt_adduint32(pktout, req->id);
710 sftp_pkt_addstring(pktout, fname);
711 sftp_send(pktout);
712
713 return req;
714 }
715
716 int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
717 {
718 int id;
719 sfree(req);
720 id = fxp_got_status(pktin);
721 sftp_pkt_free(pktin);
722 if (id != 1) {
723 return 0;
724 }
725 return 1;
726 }
727
728 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
729 {
730 struct sftp_request *req = sftp_alloc_request();
731 struct sftp_packet *pktout;
732
733 pktout = sftp_pkt_init(SSH_FXP_RENAME);
734 sftp_pkt_adduint32(pktout, req->id);
735 sftp_pkt_addstring(pktout, srcfname);
736 sftp_pkt_addstring(pktout, dstfname);
737 sftp_send(pktout);
738
739 return req;
740 }
741
742 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
743 {
744 int id;
745 sfree(req);
746 id = fxp_got_status(pktin);
747 sftp_pkt_free(pktin);
748 if (id != 1) {
749 return 0;
750 }
751 return 1;
752 }
753
754 /*
755 * Retrieve the attributes of a file. We have fxp_stat which works
756 * on filenames, and fxp_fstat which works on open file handles.
757 */
758 struct sftp_request *fxp_stat_send(char *fname)
759 {
760 struct sftp_request *req = sftp_alloc_request();
761 struct sftp_packet *pktout;
762
763 pktout = sftp_pkt_init(SSH_FXP_STAT);
764 sftp_pkt_adduint32(pktout, req->id);
765 sftp_pkt_addstring(pktout, fname);
766 sftp_send(pktout);
767
768 return req;
769 }
770
771 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
772 struct fxp_attrs *attrs)
773 {
774 sfree(req);
775 if (pktin->type == SSH_FXP_ATTRS) {
776 *attrs = sftp_pkt_getattrs(pktin);
777 sftp_pkt_free(pktin);
778 return 1;
779 } else {
780 fxp_got_status(pktin);
781 sftp_pkt_free(pktin);
782 return 0;
783 }
784 }
785
786 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
787 {
788 struct sftp_request *req = sftp_alloc_request();
789 struct sftp_packet *pktout;
790
791 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
792 sftp_pkt_adduint32(pktout, req->id);
793 sftp_pkt_addstring_start(pktout);
794 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
795 sftp_send(pktout);
796
797 return req;
798 }
799
800 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
801 struct fxp_attrs *attrs)
802 {
803 sfree(req);
804 if (pktin->type == SSH_FXP_ATTRS) {
805 *attrs = sftp_pkt_getattrs(pktin);
806 sftp_pkt_free(pktin);
807 return 1;
808 } else {
809 fxp_got_status(pktin);
810 sftp_pkt_free(pktin);
811 return 0;
812 }
813 }
814
815 /*
816 * Set the attributes of a file.
817 */
818 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
819 {
820 struct sftp_request *req = sftp_alloc_request();
821 struct sftp_packet *pktout;
822
823 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
824 sftp_pkt_adduint32(pktout, req->id);
825 sftp_pkt_addstring(pktout, fname);
826 sftp_pkt_addattrs(pktout, attrs);
827 sftp_send(pktout);
828
829 return req;
830 }
831
832 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
833 {
834 int id;
835 sfree(req);
836 id = fxp_got_status(pktin);
837 sftp_pkt_free(pktin);
838 if (id != 1) {
839 return 0;
840 }
841 return 1;
842 }
843
844 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
845 struct fxp_attrs attrs)
846 {
847 struct sftp_request *req = sftp_alloc_request();
848 struct sftp_packet *pktout;
849
850 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
851 sftp_pkt_adduint32(pktout, req->id);
852 sftp_pkt_addstring_start(pktout);
853 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
854 sftp_pkt_addattrs(pktout, attrs);
855 sftp_send(pktout);
856
857 return req;
858 }
859
860 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
861 {
862 int id;
863 sfree(req);
864 id = fxp_got_status(pktin);
865 sftp_pkt_free(pktin);
866 if (id != 1) {
867 return 0;
868 }
869 return 1;
870 }
871
872 /*
873 * Read from a file. Returns the number of bytes read, or -1 on an
874 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
875 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
876 * error indicator. It might even depend on the SFTP server.)
877 */
878 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
879 uint64 offset, int len)
880 {
881 struct sftp_request *req = sftp_alloc_request();
882 struct sftp_packet *pktout;
883
884 pktout = sftp_pkt_init(SSH_FXP_READ);
885 sftp_pkt_adduint32(pktout, req->id);
886 sftp_pkt_addstring_start(pktout);
887 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
888 sftp_pkt_adduint64(pktout, offset);
889 sftp_pkt_adduint32(pktout, len);
890 sftp_send(pktout);
891
892 return req;
893 }
894
895 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
896 char *buffer, int len)
897 {
898 sfree(req);
899 if (pktin->type == SSH_FXP_DATA) {
900 char *str;
901 int rlen;
902
903 sftp_pkt_getstring(pktin, &str, &rlen);
904
905 if (rlen > len || rlen < 0) {
906 fxp_internal_error("READ returned more bytes than requested");
907 sftp_pkt_free(pktin);
908 return -1;
909 }
910
911 memcpy(buffer, str, rlen);
912 sftp_pkt_free(pktin);
913 return rlen;
914 } else {
915 fxp_got_status(pktin);
916 sftp_pkt_free(pktin);
917 return -1;
918 }
919 }
920
921 /*
922 * Read from a directory.
923 */
924 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
925 {
926 struct sftp_request *req = sftp_alloc_request();
927 struct sftp_packet *pktout;
928
929 pktout = sftp_pkt_init(SSH_FXP_READDIR);
930 sftp_pkt_adduint32(pktout, req->id);
931 sftp_pkt_addstring_start(pktout);
932 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
933 sftp_send(pktout);
934
935 return req;
936 }
937
938 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
939 struct sftp_request *req)
940 {
941 sfree(req);
942 if (pktin->type == SSH_FXP_NAME) {
943 struct fxp_names *ret;
944 int i;
945 ret = snew(struct fxp_names);
946 ret->nnames = sftp_pkt_getuint32(pktin);
947 ret->names = snewn(ret->nnames, struct fxp_name);
948 for (i = 0; i < ret->nnames; i++) {
949 char *str;
950 int len;
951 sftp_pkt_getstring(pktin, &str, &len);
952 ret->names[i].filename = mkstr(str, len);
953 sftp_pkt_getstring(pktin, &str, &len);
954 ret->names[i].longname = mkstr(str, len);
955 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
956 }
957 sftp_pkt_free(pktin);
958 return ret;
959 } else {
960 fxp_got_status(pktin);
961 sftp_pkt_free(pktin);
962 return NULL;
963 }
964 }
965
966 /*
967 * Write to a file. Returns 0 on error, 1 on OK.
968 */
969 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
970 char *buffer, uint64 offset, int len)
971 {
972 struct sftp_request *req = sftp_alloc_request();
973 struct sftp_packet *pktout;
974
975 pktout = sftp_pkt_init(SSH_FXP_WRITE);
976 sftp_pkt_adduint32(pktout, req->id);
977 sftp_pkt_addstring_start(pktout);
978 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
979 sftp_pkt_adduint64(pktout, offset);
980 sftp_pkt_addstring_start(pktout);
981 sftp_pkt_addstring_data(pktout, buffer, len);
982 sftp_send(pktout);
983
984 return req;
985 }
986
987 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
988 {
989 sfree(req);
990 fxp_got_status(pktin);
991 sftp_pkt_free(pktin);
992 return fxp_errtype == SSH_FX_OK;
993 }
994
995 /*
996 * Free up an fxp_names structure.
997 */
998 void fxp_free_names(struct fxp_names *names)
999 {
1000 int i;
1001
1002 for (i = 0; i < names->nnames; i++) {
1003 sfree(names->names[i].filename);
1004 sfree(names->names[i].longname);
1005 }
1006 sfree(names->names);
1007 sfree(names);
1008 }
1009
1010 /*
1011 * Duplicate an fxp_name structure.
1012 */
1013 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1014 {
1015 struct fxp_name *ret;
1016 ret = snew(struct fxp_name);
1017 ret->filename = dupstr(name->filename);
1018 ret->longname = dupstr(name->longname);
1019 ret->attrs = name->attrs; /* structure copy */
1020 return ret;
1021 }
1022
1023 /*
1024 * Free up an fxp_name structure.
1025 */
1026 void fxp_free_name(struct fxp_name *name)
1027 {
1028 sfree(name->filename);
1029 sfree(name->longname);
1030 sfree(name);
1031 }
1032
1033 /*
1034 * Store user data in an sftp_request structure.
1035 */
1036 void *fxp_get_userdata(struct sftp_request *req)
1037 {
1038 return req->userdata;
1039 }
1040
1041 void fxp_set_userdata(struct sftp_request *req, void *data)
1042 {
1043 req->userdata = data;
1044 }
1045
1046 /*
1047 * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1048 * the queueing of multiple read/write requests.
1049 */
1050
1051 struct req {
1052 char *buffer;
1053 int len, retlen, complete;
1054 uint64 offset;
1055 struct req *next, *prev;
1056 };
1057
1058 struct fxp_xfer {
1059 uint64 offset, furthestdata, filesize;
1060 int req_totalsize, req_maxsize, eof, err;
1061 struct fxp_handle *fh;
1062 struct req *head, *tail;
1063 };
1064
1065 static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1066 {
1067 struct fxp_xfer *xfer = snew(struct fxp_xfer);
1068
1069 xfer->fh = fh;
1070 xfer->offset = offset;
1071 xfer->head = xfer->tail = NULL;
1072 xfer->req_totalsize = 0;
1073 xfer->req_maxsize = 16384;
1074 xfer->err = 0;
1075 xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1076 xfer->furthestdata = uint64_make(0, 0);
1077
1078 return xfer;
1079 }
1080
1081 int xfer_done(struct fxp_xfer *xfer)
1082 {
1083 /*
1084 * We're finished if we've seen EOF _and_ there are no
1085 * outstanding requests.
1086 */
1087 return (xfer->eof || xfer->err) && !xfer->head;
1088 }
1089
1090 void xfer_download_queue(struct fxp_xfer *xfer)
1091 {
1092 while (xfer->req_totalsize < xfer->req_maxsize &&
1093 !xfer->eof && !xfer->err) {
1094 /*
1095 * Queue a new read request.
1096 */
1097 struct req *rr;
1098 struct sftp_request *req;
1099
1100 rr = snew(struct req);
1101 rr->offset = xfer->offset;
1102 rr->complete = 0;
1103 if (xfer->tail) {
1104 xfer->tail->next = rr;
1105 rr->prev = xfer->tail;
1106 } else {
1107 xfer->head = rr;
1108 rr->prev = NULL;
1109 }
1110 xfer->tail = rr;
1111 rr->next = NULL;
1112
1113 rr->len = 4096;
1114 rr->buffer = snewn(rr->len, char);
1115 sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1116 fxp_set_userdata(req, rr);
1117
1118 xfer->offset = uint64_add32(xfer->offset, rr->len);
1119 xfer->req_totalsize += rr->len;
1120
1121 #ifdef DEBUG_DOWNLOAD
1122 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1123 #endif
1124 }
1125 }
1126
1127 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1128 {
1129 struct fxp_xfer *xfer = xfer_init(fh, offset);
1130
1131 xfer->eof = FALSE;
1132 xfer_download_queue(xfer);
1133
1134 return xfer;
1135 }
1136
1137 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1138 {
1139 struct sftp_request *rreq;
1140 struct req *rr;
1141
1142 rreq = sftp_find_request(pktin);
1143 rr = (struct req *)fxp_get_userdata(rreq);
1144 if (!rr)
1145 return 0; /* this packet isn't ours */
1146 rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1147 #ifdef DEBUG_DOWNLOAD
1148 printf("read request %p has returned [%d]\n", rr, rr->retlen);
1149 #endif
1150
1151 if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1152 xfer->eof = TRUE;
1153 rr->complete = -1;
1154 #ifdef DEBUG_DOWNLOAD
1155 printf("setting eof\n");
1156 #endif
1157 } else if (rr->retlen < 0) {
1158 /* some error other than EOF; signal it back to caller */
1159 xfer_set_error(xfer);
1160 rr->complete = -1;
1161 return -1;
1162 }
1163
1164 rr->complete = 1;
1165
1166 /*
1167 * Special case: if we have received fewer bytes than we
1168 * actually read, we should do something. For the moment I'll
1169 * just throw an ersatz FXP error to signal this; the SFTP
1170 * draft I've got says that it can't happen except on special
1171 * files, in which case seeking probably has very little
1172 * meaning and so queueing an additional read request to fill
1173 * up the gap sounds like the wrong answer. I'm not sure what I
1174 * should be doing here - if it _was_ a special file, I suspect
1175 * I simply shouldn't have been queueing multiple requests in
1176 * the first place...
1177 */
1178 if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1179 xfer->furthestdata = rr->offset;
1180 #ifdef DEBUG_DOWNLOAD
1181 { char buf[40];
1182 uint64_decimal(xfer->furthestdata, buf);
1183 printf("setting furthestdata = %s\n", buf); }
1184 #endif
1185 }
1186
1187 if (rr->retlen < rr->len) {
1188 uint64 filesize = uint64_add32(rr->offset,
1189 (rr->retlen < 0 ? 0 : rr->retlen));
1190 #ifdef DEBUG_DOWNLOAD
1191 { char buf[40];
1192 uint64_decimal(filesize, buf);
1193 printf("short block! trying filesize = %s\n", buf); }
1194 #endif
1195 if (uint64_compare(xfer->filesize, filesize) > 0) {
1196 xfer->filesize = filesize;
1197 #ifdef DEBUG_DOWNLOAD
1198 printf("actually changing filesize\n");
1199 #endif
1200 }
1201 }
1202
1203 if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1204 fxp_error_message = "received a short buffer from FXP_READ, but not"
1205 " at EOF";
1206 fxp_errtype = -1;
1207 xfer_set_error(xfer);
1208 return -1;
1209 }
1210
1211 return 1;
1212 }
1213
1214 void xfer_set_error(struct fxp_xfer *xfer)
1215 {
1216 xfer->err = 1;
1217 }
1218
1219 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1220 {
1221 void *retbuf = NULL;
1222 int retlen = 0;
1223
1224 /*
1225 * Discard anything at the head of the rr queue with complete <
1226 * 0; return the first thing with complete > 0.
1227 */
1228 while (xfer->head && xfer->head->complete && !retbuf) {
1229 struct req *rr = xfer->head;
1230
1231 if (rr->complete > 0) {
1232 retbuf = rr->buffer;
1233 retlen = rr->retlen;
1234 #ifdef DEBUG_DOWNLOAD
1235 printf("handing back data from read request %p\n", rr);
1236 #endif
1237 }
1238 #ifdef DEBUG_DOWNLOAD
1239 else
1240 printf("skipping failed read request %p\n", rr);
1241 #endif
1242
1243 xfer->head = xfer->head->next;
1244 if (xfer->head)
1245 xfer->head->prev = NULL;
1246 else
1247 xfer->tail = NULL;
1248 xfer->req_totalsize -= rr->len;
1249 sfree(rr);
1250 }
1251
1252 if (retbuf) {
1253 *buf = retbuf;
1254 *len = retlen;
1255 return 1;
1256 } else
1257 return 0;
1258 }
1259
1260 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1261 {
1262 struct fxp_xfer *xfer = xfer_init(fh, offset);
1263
1264 /*
1265 * We set `eof' to 1 because this will cause xfer_done() to
1266 * return true iff there are no outstanding requests. During an
1267 * upload, our caller will be responsible for working out
1268 * whether all the data has been sent, so all it needs to know
1269 * from us is whether the outstanding requests have been
1270 * handled once that's done.
1271 */
1272 xfer->eof = 1;
1273
1274 return xfer;
1275 }
1276
1277 int xfer_upload_ready(struct fxp_xfer *xfer)
1278 {
1279 if (xfer->req_totalsize < xfer->req_maxsize)
1280 return 1;
1281 else
1282 return 0;
1283 }
1284
1285 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1286 {
1287 struct req *rr;
1288 struct sftp_request *req;
1289
1290 rr = snew(struct req);
1291 rr->offset = xfer->offset;
1292 rr->complete = 0;
1293 if (xfer->tail) {
1294 xfer->tail->next = rr;
1295 rr->prev = xfer->tail;
1296 } else {
1297 xfer->head = rr;
1298 rr->prev = NULL;
1299 }
1300 xfer->tail = rr;
1301 rr->next = NULL;
1302
1303 rr->len = len;
1304 rr->buffer = NULL;
1305 sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1306 fxp_set_userdata(req, rr);
1307
1308 xfer->offset = uint64_add32(xfer->offset, rr->len);
1309 xfer->req_totalsize += rr->len;
1310
1311 #ifdef DEBUG_UPLOAD
1312 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1313 #endif
1314 }
1315
1316 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1317 {
1318 struct sftp_request *rreq;
1319 struct req *rr, *prev, *next;
1320 int ret;
1321
1322 rreq = sftp_find_request(pktin);
1323 rr = (struct req *)fxp_get_userdata(rreq);
1324 if (!rr)
1325 return 0; /* this packet isn't ours */
1326 ret = fxp_write_recv(pktin, rreq);
1327 #ifdef DEBUG_UPLOAD
1328 printf("write request %p has returned [%d]\n", rr, ret);
1329 #endif
1330
1331 /*
1332 * Remove this one from the queue.
1333 */
1334 prev = rr->prev;
1335 next = rr->next;
1336 if (prev)
1337 prev->next = next;
1338 else
1339 xfer->head = next;
1340 if (next)
1341 next->prev = prev;
1342 else
1343 xfer->tail = prev;
1344 xfer->req_totalsize -= rr->len;
1345 sfree(rr);
1346
1347 if (!ret)
1348 return -1;
1349
1350 return 1;
1351 }
1352
1353 void xfer_cleanup(struct fxp_xfer *xfer)
1354 {
1355 struct req *rr;
1356 while (xfer->head) {
1357 rr = xfer->head;
1358 xfer->head = xfer->head->next;
1359 sfree(rr->buffer);
1360 sfree(rr);
1361 }
1362 sfree(xfer);
1363 }