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