Uploads turn out to be much easier than downloads, so here's faster
[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_register(struct sftp_request *req)
338 {
339 req->registered = 1;
340 }
341
342 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
343 {
344 unsigned long id;
345 struct sftp_request *req;
346
347 if (!pktin) {
348 fxp_internal_error("did not receive a valid SFTP packet\n");
349 return NULL;
350 }
351
352 id = sftp_pkt_getuint32(pktin);
353 req = find234(sftp_requests, &id, sftp_reqfind);
354
355 if (!req || !req->registered) {
356 fxp_internal_error("request ID mismatch\n");
357 sftp_pkt_free(pktin);
358 return NULL;
359 }
360
361 del234(sftp_requests, req);
362
363 return req;
364 }
365
366 /* ----------------------------------------------------------------------
367 * String handling routines.
368 */
369
370 static char *mkstr(char *s, int len)
371 {
372 char *p = snewn(len + 1, char);
373 memcpy(p, s, len);
374 p[len] = '\0';
375 return p;
376 }
377
378 /* ----------------------------------------------------------------------
379 * SFTP primitives.
380 */
381
382 /*
383 * Deal with (and free) an FXP_STATUS packet. Return 1 if
384 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
385 * Also place the status into fxp_errtype.
386 */
387 static int fxp_got_status(struct sftp_packet *pktin)
388 {
389 static const char *const messages[] = {
390 /* SSH_FX_OK. The only time we will display a _message_ for this
391 * is if we were expecting something other than FXP_STATUS on
392 * success, so this is actually an error message! */
393 "unexpected OK response",
394 "end of file",
395 "no such file or directory",
396 "permission denied",
397 "failure",
398 "bad message",
399 "no connection",
400 "connection lost",
401 "operation unsupported",
402 };
403
404 if (pktin->type != SSH_FXP_STATUS) {
405 fxp_error_message = "expected FXP_STATUS packet";
406 fxp_errtype = -1;
407 } else {
408 fxp_errtype = sftp_pkt_getuint32(pktin);
409 if (fxp_errtype < 0 ||
410 fxp_errtype >= sizeof(messages) / sizeof(*messages))
411 fxp_error_message = "unknown error code";
412 else
413 fxp_error_message = messages[fxp_errtype];
414 }
415
416 if (fxp_errtype == SSH_FX_OK)
417 return 1;
418 else if (fxp_errtype == SSH_FX_EOF)
419 return 0;
420 else
421 return -1;
422 }
423
424 static void fxp_internal_error(char *msg)
425 {
426 fxp_error_message = msg;
427 fxp_errtype = -1;
428 }
429
430 const char *fxp_error(void)
431 {
432 return fxp_error_message;
433 }
434
435 int fxp_error_type(void)
436 {
437 return fxp_errtype;
438 }
439
440 /*
441 * Perform exchange of init/version packets. Return 0 on failure.
442 */
443 int fxp_init(void)
444 {
445 struct sftp_packet *pktout, *pktin;
446 int remotever;
447
448 pktout = sftp_pkt_init(SSH_FXP_INIT);
449 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
450 sftp_send(pktout);
451
452 pktin = sftp_recv();
453 if (!pktin) {
454 fxp_internal_error("could not connect");
455 return 0;
456 }
457 if (pktin->type != SSH_FXP_VERSION) {
458 fxp_internal_error("did not receive FXP_VERSION");
459 sftp_pkt_free(pktin);
460 return 0;
461 }
462 remotever = sftp_pkt_getuint32(pktin);
463 if (remotever > SFTP_PROTO_VERSION) {
464 fxp_internal_error
465 ("remote protocol is more advanced than we support");
466 sftp_pkt_free(pktin);
467 return 0;
468 }
469 /*
470 * In principle, this packet might also contain extension-
471 * string pairs. We should work through them and look for any
472 * we recognise. In practice we don't currently do so because
473 * we know we don't recognise _any_.
474 */
475 sftp_pkt_free(pktin);
476
477 return 1;
478 }
479
480 /*
481 * Canonify a pathname.
482 */
483 struct sftp_request *fxp_realpath_send(char *path)
484 {
485 struct sftp_request *req = sftp_alloc_request();
486 struct sftp_packet *pktout;
487
488 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
489 sftp_pkt_adduint32(pktout, req->id);
490 sftp_pkt_addstring_start(pktout);
491 sftp_pkt_addstring_str(pktout, path);
492 sftp_send(pktout);
493
494 return req;
495 }
496
497 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
498 {
499 sfree(req);
500
501 if (pktin->type == SSH_FXP_NAME) {
502 int count;
503 char *path;
504 int len;
505
506 count = sftp_pkt_getuint32(pktin);
507 if (count != 1) {
508 fxp_internal_error("REALPATH returned name count != 1\n");
509 sftp_pkt_free(pktin);
510 return NULL;
511 }
512 sftp_pkt_getstring(pktin, &path, &len);
513 if (!path) {
514 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
515 sftp_pkt_free(pktin);
516 return NULL;
517 }
518 path = mkstr(path, len);
519 sftp_pkt_free(pktin);
520 return path;
521 } else {
522 fxp_got_status(pktin);
523 sftp_pkt_free(pktin);
524 return NULL;
525 }
526 }
527
528 /*
529 * Open a file.
530 */
531 struct sftp_request *fxp_open_send(char *path, int type)
532 {
533 struct sftp_request *req = sftp_alloc_request();
534 struct sftp_packet *pktout;
535
536 pktout = sftp_pkt_init(SSH_FXP_OPEN);
537 sftp_pkt_adduint32(pktout, req->id);
538 sftp_pkt_addstring(pktout, path);
539 sftp_pkt_adduint32(pktout, type);
540 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
541 sftp_send(pktout);
542
543 return req;
544 }
545
546 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
547 struct sftp_request *req)
548 {
549 sfree(req);
550
551 if (pktin->type == SSH_FXP_HANDLE) {
552 char *hstring;
553 struct fxp_handle *handle;
554 int len;
555
556 sftp_pkt_getstring(pktin, &hstring, &len);
557 if (!hstring) {
558 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
559 sftp_pkt_free(pktin);
560 return NULL;
561 }
562 handle = snew(struct fxp_handle);
563 handle->hstring = mkstr(hstring, len);
564 handle->hlen = len;
565 sftp_pkt_free(pktin);
566 return handle;
567 } else {
568 fxp_got_status(pktin);
569 sftp_pkt_free(pktin);
570 return NULL;
571 }
572 }
573
574 /*
575 * Open a directory.
576 */
577 struct sftp_request *fxp_opendir_send(char *path)
578 {
579 struct sftp_request *req = sftp_alloc_request();
580 struct sftp_packet *pktout;
581
582 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
583 sftp_pkt_adduint32(pktout, req->id);
584 sftp_pkt_addstring(pktout, path);
585 sftp_send(pktout);
586
587 return req;
588 }
589
590 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
591 struct sftp_request *req)
592 {
593 sfree(req);
594 if (pktin->type == SSH_FXP_HANDLE) {
595 char *hstring;
596 struct fxp_handle *handle;
597 int len;
598
599 sftp_pkt_getstring(pktin, &hstring, &len);
600 if (!hstring) {
601 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
602 sftp_pkt_free(pktin);
603 return NULL;
604 }
605 handle = snew(struct fxp_handle);
606 handle->hstring = mkstr(hstring, len);
607 handle->hlen = len;
608 sftp_pkt_free(pktin);
609 return handle;
610 } else {
611 fxp_got_status(pktin);
612 sftp_pkt_free(pktin);
613 return NULL;
614 }
615 }
616
617 /*
618 * Close a file/dir.
619 */
620 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
621 {
622 struct sftp_request *req = sftp_alloc_request();
623 struct sftp_packet *pktout;
624
625 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
626 sftp_pkt_adduint32(pktout, req->id);
627 sftp_pkt_addstring_start(pktout);
628 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
629 sftp_send(pktout);
630
631 sfree(handle->hstring);
632 sfree(handle);
633
634 return req;
635 }
636
637 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
638 {
639 sfree(req);
640 fxp_got_status(pktin);
641 sftp_pkt_free(pktin);
642 }
643
644 struct sftp_request *fxp_mkdir_send(char *path)
645 {
646 struct sftp_request *req = sftp_alloc_request();
647 struct sftp_packet *pktout;
648
649 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
650 sftp_pkt_adduint32(pktout, req->id);
651 sftp_pkt_addstring(pktout, path);
652 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
653 sftp_send(pktout);
654
655 return req;
656 }
657
658 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
659 {
660 int id;
661 sfree(req);
662 id = fxp_got_status(pktin);
663 sftp_pkt_free(pktin);
664 if (id != 1) {
665 return 0;
666 }
667 return 1;
668 }
669
670 struct sftp_request *fxp_rmdir_send(char *path)
671 {
672 struct sftp_request *req = sftp_alloc_request();
673 struct sftp_packet *pktout;
674
675 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
676 sftp_pkt_adduint32(pktout, req->id);
677 sftp_pkt_addstring(pktout, path);
678 sftp_send(pktout);
679
680 return req;
681 }
682
683 int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
684 {
685 int id;
686 sfree(req);
687 id = fxp_got_status(pktin);
688 sftp_pkt_free(pktin);
689 if (id != 1) {
690 return 0;
691 }
692 return 1;
693 }
694
695 struct sftp_request *fxp_remove_send(char *fname)
696 {
697 struct sftp_request *req = sftp_alloc_request();
698 struct sftp_packet *pktout;
699
700 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
701 sftp_pkt_adduint32(pktout, req->id);
702 sftp_pkt_addstring(pktout, fname);
703 sftp_send(pktout);
704
705 return req;
706 }
707
708 int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
709 {
710 int id;
711 sfree(req);
712 id = fxp_got_status(pktin);
713 sftp_pkt_free(pktin);
714 if (id != 1) {
715 return 0;
716 }
717 return 1;
718 }
719
720 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
721 {
722 struct sftp_request *req = sftp_alloc_request();
723 struct sftp_packet *pktout;
724
725 pktout = sftp_pkt_init(SSH_FXP_RENAME);
726 sftp_pkt_adduint32(pktout, req->id);
727 sftp_pkt_addstring(pktout, srcfname);
728 sftp_pkt_addstring(pktout, dstfname);
729 sftp_send(pktout);
730
731 return req;
732 }
733
734 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
735 {
736 int id;
737 sfree(req);
738 id = fxp_got_status(pktin);
739 sftp_pkt_free(pktin);
740 if (id != 1) {
741 return 0;
742 }
743 return 1;
744 }
745
746 /*
747 * Retrieve the attributes of a file. We have fxp_stat which works
748 * on filenames, and fxp_fstat which works on open file handles.
749 */
750 struct sftp_request *fxp_stat_send(char *fname)
751 {
752 struct sftp_request *req = sftp_alloc_request();
753 struct sftp_packet *pktout;
754
755 pktout = sftp_pkt_init(SSH_FXP_STAT);
756 sftp_pkt_adduint32(pktout, req->id);
757 sftp_pkt_addstring(pktout, fname);
758 sftp_send(pktout);
759
760 return req;
761 }
762
763 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
764 struct fxp_attrs *attrs)
765 {
766 sfree(req);
767 if (pktin->type == SSH_FXP_ATTRS) {
768 *attrs = sftp_pkt_getattrs(pktin);
769 sftp_pkt_free(pktin);
770 return 1;
771 } else {
772 fxp_got_status(pktin);
773 sftp_pkt_free(pktin);
774 return 0;
775 }
776 }
777
778 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
779 {
780 struct sftp_request *req = sftp_alloc_request();
781 struct sftp_packet *pktout;
782
783 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
784 sftp_pkt_adduint32(pktout, req->id);
785 sftp_pkt_addstring_start(pktout);
786 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
787 sftp_send(pktout);
788
789 return req;
790 }
791
792 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
793 struct fxp_attrs *attrs)
794 {
795 sfree(req);
796 if (pktin->type == SSH_FXP_ATTRS) {
797 *attrs = sftp_pkt_getattrs(pktin);
798 sftp_pkt_free(pktin);
799 return 1;
800 } else {
801 fxp_got_status(pktin);
802 sftp_pkt_free(pktin);
803 return 0;
804 }
805 }
806
807 /*
808 * Set the attributes of a file.
809 */
810 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
811 {
812 struct sftp_request *req = sftp_alloc_request();
813 struct sftp_packet *pktout;
814
815 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
816 sftp_pkt_adduint32(pktout, req->id);
817 sftp_pkt_addstring(pktout, fname);
818 sftp_pkt_addattrs(pktout, attrs);
819 sftp_send(pktout);
820
821 return req;
822 }
823
824 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
825 {
826 int id;
827 sfree(req);
828 id = fxp_got_status(pktin);
829 sftp_pkt_free(pktin);
830 if (id != 1) {
831 return 0;
832 }
833 return 1;
834 }
835
836 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
837 struct fxp_attrs attrs)
838 {
839 struct sftp_request *req = sftp_alloc_request();
840 struct sftp_packet *pktout;
841
842 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
843 sftp_pkt_adduint32(pktout, req->id);
844 sftp_pkt_addstring_start(pktout);
845 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
846 sftp_pkt_addattrs(pktout, attrs);
847 sftp_send(pktout);
848
849 return req;
850 }
851
852 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
853 {
854 int id;
855 sfree(req);
856 id = fxp_got_status(pktin);
857 sftp_pkt_free(pktin);
858 if (id != 1) {
859 return 0;
860 }
861 return 1;
862 }
863
864 /*
865 * Read from a file. Returns the number of bytes read, or -1 on an
866 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
867 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
868 * error indicator. It might even depend on the SFTP server.)
869 */
870 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
871 uint64 offset, int len)
872 {
873 struct sftp_request *req = sftp_alloc_request();
874 struct sftp_packet *pktout;
875
876 pktout = sftp_pkt_init(SSH_FXP_READ);
877 sftp_pkt_adduint32(pktout, req->id);
878 sftp_pkt_addstring_start(pktout);
879 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
880 sftp_pkt_adduint64(pktout, offset);
881 sftp_pkt_adduint32(pktout, len);
882 sftp_send(pktout);
883
884 return req;
885 }
886
887 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
888 char *buffer, int len)
889 {
890 sfree(req);
891 if (pktin->type == SSH_FXP_DATA) {
892 char *str;
893 int rlen;
894
895 sftp_pkt_getstring(pktin, &str, &rlen);
896
897 if (rlen > len || rlen < 0) {
898 fxp_internal_error("READ returned more bytes than requested");
899 sftp_pkt_free(pktin);
900 return -1;
901 }
902
903 memcpy(buffer, str, rlen);
904 sftp_pkt_free(pktin);
905 return rlen;
906 } else {
907 fxp_got_status(pktin);
908 sftp_pkt_free(pktin);
909 return -1;
910 }
911 }
912
913 /*
914 * Read from a directory.
915 */
916 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
917 {
918 struct sftp_request *req = sftp_alloc_request();
919 struct sftp_packet *pktout;
920
921 pktout = sftp_pkt_init(SSH_FXP_READDIR);
922 sftp_pkt_adduint32(pktout, req->id);
923 sftp_pkt_addstring_start(pktout);
924 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
925 sftp_send(pktout);
926
927 return req;
928 }
929
930 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
931 struct sftp_request *req)
932 {
933 sfree(req);
934 if (pktin->type == SSH_FXP_NAME) {
935 struct fxp_names *ret;
936 int i;
937 ret = snew(struct fxp_names);
938 ret->nnames = sftp_pkt_getuint32(pktin);
939 ret->names = snewn(ret->nnames, struct fxp_name);
940 for (i = 0; i < ret->nnames; i++) {
941 char *str;
942 int len;
943 sftp_pkt_getstring(pktin, &str, &len);
944 ret->names[i].filename = mkstr(str, len);
945 sftp_pkt_getstring(pktin, &str, &len);
946 ret->names[i].longname = mkstr(str, len);
947 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
948 }
949 sftp_pkt_free(pktin);
950 return ret;
951 } else {
952 fxp_got_status(pktin);
953 sftp_pkt_free(pktin);
954 return NULL;
955 }
956 }
957
958 /*
959 * Write to a file. Returns 0 on error, 1 on OK.
960 */
961 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
962 char *buffer, uint64 offset, int len)
963 {
964 struct sftp_request *req = sftp_alloc_request();
965 struct sftp_packet *pktout;
966
967 pktout = sftp_pkt_init(SSH_FXP_WRITE);
968 sftp_pkt_adduint32(pktout, req->id);
969 sftp_pkt_addstring_start(pktout);
970 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
971 sftp_pkt_adduint64(pktout, offset);
972 sftp_pkt_addstring_start(pktout);
973 sftp_pkt_addstring_data(pktout, buffer, len);
974 sftp_send(pktout);
975
976 return req;
977 }
978
979 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
980 {
981 sfree(req);
982 fxp_got_status(pktin);
983 sftp_pkt_free(pktin);
984 return fxp_errtype == SSH_FX_OK;
985 }
986
987 /*
988 * Free up an fxp_names structure.
989 */
990 void fxp_free_names(struct fxp_names *names)
991 {
992 int i;
993
994 for (i = 0; i < names->nnames; i++) {
995 sfree(names->names[i].filename);
996 sfree(names->names[i].longname);
997 }
998 sfree(names->names);
999 sfree(names);
1000 }
1001
1002 /*
1003 * Duplicate an fxp_name structure.
1004 */
1005 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1006 {
1007 struct fxp_name *ret;
1008 ret = snew(struct fxp_name);
1009 ret->filename = dupstr(name->filename);
1010 ret->longname = dupstr(name->longname);
1011 ret->attrs = name->attrs; /* structure copy */
1012 return ret;
1013 }
1014
1015 /*
1016 * Free up an fxp_name structure.
1017 */
1018 void fxp_free_name(struct fxp_name *name)
1019 {
1020 sfree(name->filename);
1021 sfree(name->longname);
1022 sfree(name);
1023 }
1024
1025 /*
1026 * Store user data in an sftp_request structure.
1027 */
1028 void *fxp_get_userdata(struct sftp_request *req)
1029 {
1030 return req->userdata;
1031 }
1032
1033 void fxp_set_userdata(struct sftp_request *req, void *data)
1034 {
1035 req->userdata = data;
1036 }
1037
1038 /*
1039 * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1040 * the queueing of multiple read/write requests.
1041 */
1042
1043 struct req {
1044 char *buffer;
1045 int len, retlen, complete;
1046 uint64 offset;
1047 struct req *next, *prev;
1048 };
1049
1050 struct fxp_xfer {
1051 uint64 offset, furthestdata, filesize;
1052 int req_totalsize, req_maxsize, eof, err;
1053 struct fxp_handle *fh;
1054 struct req *head, *tail;
1055 };
1056
1057 static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1058 {
1059 struct fxp_xfer *xfer = snew(struct fxp_xfer);
1060
1061 xfer->fh = fh;
1062 xfer->offset = offset;
1063 xfer->head = xfer->tail = NULL;
1064 xfer->req_totalsize = 0;
1065 xfer->req_maxsize = 16384;
1066 xfer->err = 0;
1067 xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1068 xfer->furthestdata = uint64_make(0, 0);
1069
1070 return xfer;
1071 }
1072
1073 int xfer_done(struct fxp_xfer *xfer)
1074 {
1075 /*
1076 * We're finished if we've seen EOF _and_ there are no
1077 * outstanding requests.
1078 */
1079 return (xfer->eof || xfer->err) && !xfer->head;
1080 }
1081
1082 void xfer_download_queue(struct fxp_xfer *xfer)
1083 {
1084 while (xfer->req_totalsize < xfer->req_maxsize && !xfer->eof) {
1085 /*
1086 * Queue a new read request.
1087 */
1088 struct req *rr;
1089 struct sftp_request *req;
1090
1091 rr = snew(struct req);
1092 rr->offset = xfer->offset;
1093 rr->complete = 0;
1094 if (xfer->tail) {
1095 xfer->tail->next = rr;
1096 rr->prev = xfer->tail;
1097 } else {
1098 xfer->head = rr;
1099 rr->prev = NULL;
1100 }
1101 xfer->tail = rr;
1102 rr->next = NULL;
1103
1104 rr->len = 4096;
1105 rr->buffer = snewn(rr->len, char);
1106 sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1107 fxp_set_userdata(req, rr);
1108
1109 xfer->offset = uint64_add32(xfer->offset, rr->len);
1110 xfer->req_totalsize += rr->len;
1111
1112 #ifdef DEBUG_DOWNLOAD
1113 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1114 #endif
1115 }
1116 }
1117
1118 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1119 {
1120 struct fxp_xfer *xfer = xfer_init(fh, offset);
1121
1122 xfer->eof = FALSE;
1123 xfer_download_queue(xfer);
1124
1125 return xfer;
1126 }
1127
1128 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1129 {
1130 struct sftp_request *rreq;
1131 struct req *rr;
1132
1133 rreq = sftp_find_request(pktin);
1134 rr = (struct req *)fxp_get_userdata(rreq);
1135 if (!rr)
1136 return 0; /* this packet isn't ours */
1137 rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1138 #ifdef DEBUG_DOWNLOAD
1139 printf("read request %p has returned [%d]\n", rr, rr->retlen);
1140 #endif
1141
1142 if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1143 xfer->eof = TRUE;
1144 rr->complete = -1;
1145 #ifdef DEBUG_DOWNLOAD
1146 printf("setting eof\n");
1147 #endif
1148 } else if (rr->retlen < 0) {
1149 /* some error other than EOF; signal it back to caller */
1150 return -1;
1151 }
1152
1153 rr->complete = 1;
1154
1155 /*
1156 * Special case: if we have received fewer bytes than we
1157 * actually read, we should do something. For the moment I'll
1158 * just throw an ersatz FXP error to signal this; the SFTP
1159 * draft I've got says that it can't happen except on special
1160 * files, in which case seeking probably has very little
1161 * meaning and so queueing an additional read request to fill
1162 * up the gap sounds like the wrong answer. I'm not sure what I
1163 * should be doing here - if it _was_ a special file, I suspect
1164 * I simply shouldn't have been queueing multiple requests in
1165 * the first place...
1166 */
1167 if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1168 xfer->furthestdata = rr->offset;
1169 #ifdef DEBUG_DOWNLOAD
1170 { char buf[40];
1171 uint64_decimal(xfer->furthestdata, buf);
1172 printf("setting furthestdata = %s\n", buf); }
1173 #endif
1174 }
1175
1176 if (rr->retlen < rr->len) {
1177 uint64 filesize = uint64_add32(rr->offset,
1178 (rr->retlen < 0 ? 0 : rr->retlen));
1179 #ifdef DEBUG_DOWNLOAD
1180 { char buf[40];
1181 uint64_decimal(filesize, buf);
1182 printf("short block! trying filesize = %s\n", buf); }
1183 #endif
1184 if (uint64_compare(xfer->filesize, filesize) > 0) {
1185 xfer->filesize = filesize;
1186 #ifdef DEBUG_DOWNLOAD
1187 printf("actually changing filesize\n");
1188 #endif
1189 }
1190 }
1191
1192 if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1193 fxp_error_message = "received a short buffer from FXP_READ, but not"
1194 " at EOF";
1195 fxp_errtype = -1;
1196 xfer_set_error(xfer);
1197 return -1;
1198 }
1199
1200 return 1;
1201 }
1202
1203 void xfer_set_error(struct fxp_xfer *xfer)
1204 {
1205 xfer->err = 1;
1206 }
1207
1208 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1209 {
1210 void *retbuf = NULL;
1211 int retlen = 0;
1212
1213 /*
1214 * Discard anything at the head of the rr queue with complete <
1215 * 0; return the first thing with complete > 0.
1216 */
1217 while (xfer->head && xfer->head->complete && !retbuf) {
1218 struct req *rr = xfer->head;
1219
1220 if (rr->complete > 0) {
1221 retbuf = rr->buffer;
1222 retlen = rr->retlen;
1223 #ifdef DEBUG_DOWNLOAD
1224 printf("handing back data from read request %p\n", rr);
1225 #endif
1226 }
1227 #ifdef DEBUG_DOWNLOAD
1228 else
1229 printf("skipping failed read request %p\n", rr);
1230 #endif
1231
1232 xfer->head = xfer->head->next;
1233 if (xfer->head)
1234 xfer->head->prev = NULL;
1235 else
1236 xfer->tail = NULL;
1237 xfer->req_totalsize -= rr->len;
1238 sfree(rr);
1239 }
1240
1241 if (retbuf) {
1242 *buf = retbuf;
1243 *len = retlen;
1244 return 1;
1245 } else
1246 return 0;
1247 }
1248
1249 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1250 {
1251 struct fxp_xfer *xfer = xfer_init(fh, offset);
1252
1253 /*
1254 * We set `eof' to 1 because this will cause xfer_done() to
1255 * return true iff there are no outstanding requests. During an
1256 * upload, our caller will be responsible for working out
1257 * whether all the data has been sent, so all it needs to know
1258 * from us is whether the outstanding requests have been
1259 * handled once that's done.
1260 */
1261 xfer->eof = 1;
1262
1263 return xfer;
1264 }
1265
1266 int xfer_upload_ready(struct fxp_xfer *xfer)
1267 {
1268 if (xfer->req_totalsize < xfer->req_maxsize)
1269 return 1;
1270 else
1271 return 0;
1272 }
1273
1274 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1275 {
1276 struct req *rr;
1277 struct sftp_request *req;
1278
1279 rr = snew(struct req);
1280 rr->offset = xfer->offset;
1281 rr->complete = 0;
1282 if (xfer->tail) {
1283 xfer->tail->next = rr;
1284 rr->prev = xfer->tail;
1285 } else {
1286 xfer->head = rr;
1287 rr->prev = NULL;
1288 }
1289 xfer->tail = rr;
1290 rr->next = NULL;
1291
1292 rr->len = len;
1293 rr->buffer = NULL;
1294 sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1295 fxp_set_userdata(req, rr);
1296
1297 xfer->offset = uint64_add32(xfer->offset, rr->len);
1298 xfer->req_totalsize += rr->len;
1299
1300 #ifdef DEBUG_UPLOAD
1301 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1302 #endif
1303 }
1304
1305 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1306 {
1307 struct sftp_request *rreq;
1308 struct req *rr, *prev, *next;
1309 int ret;
1310
1311 rreq = sftp_find_request(pktin);
1312 rr = (struct req *)fxp_get_userdata(rreq);
1313 if (!rr)
1314 return 0; /* this packet isn't ours */
1315 ret = fxp_write_recv(pktin, rreq);
1316 #ifdef DEBUG_UPLOAD
1317 printf("write request %p has returned [%d]\n", rr, ret);
1318 #endif
1319
1320 /*
1321 * Remove this one from the queue.
1322 */
1323 prev = rr->prev;
1324 next = rr->next;
1325 if (prev)
1326 prev->next = next;
1327 else
1328 xfer->head = next;
1329 if (next)
1330 next->prev = prev;
1331 else
1332 xfer->tail = prev;
1333 xfer->req_totalsize -= rr->len;
1334 sfree(rr);
1335
1336 if (!ret)
1337 return -1;
1338
1339 return 1;
1340 }
1341
1342 void xfer_cleanup(struct fxp_xfer *xfer)
1343 {
1344 struct req *rr;
1345 while (xfer->head) {
1346 rr = xfer->head;
1347 xfer->head = xfer->head->next;
1348 sfree(rr->buffer);
1349 sfree(rr);
1350 }
1351 sfree(xfer);
1352 }