]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - sftp.c
Phase 1a of SFTP re-engineering: fix the glaring memory and request
[PuTTY_svn.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
10 #include "misc.h"
11 #include "int64.h"
12 #include "tree234.h"
13 #include "sftp.h"
14
15 #define GET_32BIT(cp) \
16     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
17     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
18     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
19     ((unsigned long)(unsigned char)(cp)[3]))
20
21 #define PUT_32BIT(cp, value) { \
22     (cp)[0] = (unsigned char)((value) >> 24); \
23     (cp)[1] = (unsigned char)((value) >> 16); \
24     (cp)[2] = (unsigned char)((value) >> 8); \
25     (cp)[3] = (unsigned char)(value); }
26
27 struct sftp_packet {
28     char *data;
29     int length, maxlen;
30     int savedpos;
31     int type;
32 };
33
34 static const char *fxp_error_message;
35 static int fxp_errtype;
36
37 static void fxp_internal_error(char *msg);
38
39 /* ----------------------------------------------------------------------
40  * SFTP packet construction functions.
41  */
42 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
43 {
44     if (pkt->maxlen < length) {
45         pkt->maxlen = length + 256;
46         pkt->data = sresize(pkt->data, pkt->maxlen, char);
47     }
48 }
49 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
50 {
51     pkt->length += len;
52     sftp_pkt_ensure(pkt, pkt->length);
53     memcpy(pkt->data + pkt->length - len, data, len);
54 }
55 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
56 {
57     sftp_pkt_adddata(pkt, &byte, 1);
58 }
59 static struct sftp_packet *sftp_pkt_init(int pkt_type)
60 {
61     struct sftp_packet *pkt;
62     pkt = snew(struct sftp_packet);
63     pkt->data = NULL;
64     pkt->savedpos = -1;
65     pkt->length = 0;
66     pkt->maxlen = 0;
67     sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
68     return pkt;
69 }
70 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
71 {
72     sftp_pkt_adddata(pkt, &value, 1);
73 }
74 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
75                                unsigned long value)
76 {
77     unsigned char x[4];
78     PUT_32BIT(x, value);
79     sftp_pkt_adddata(pkt, x, 4);
80 }
81 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
82 {
83     unsigned char x[8];
84     PUT_32BIT(x, value.hi);
85     PUT_32BIT(x + 4, value.lo);
86     sftp_pkt_adddata(pkt, x, 8);
87 }
88 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
89 {
90     sftp_pkt_adduint32(pkt, 0);
91     pkt->savedpos = pkt->length;
92 }
93 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
94 {
95     sftp_pkt_adddata(pkt, data, strlen(data));
96     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
97 }
98 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
99                                     char *data, int len)
100 {
101     sftp_pkt_adddata(pkt, data, len);
102     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
103 }
104 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
105 {
106     sftp_pkt_addstring_start(pkt);
107     sftp_pkt_addstring_str(pkt, data);
108 }
109 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
110 {
111     sftp_pkt_adduint32(pkt, attrs.flags);
112     if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
113         sftp_pkt_adduint32(pkt, attrs.size.hi);
114         sftp_pkt_adduint32(pkt, attrs.size.lo);
115     }
116     if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
117         sftp_pkt_adduint32(pkt, attrs.uid);
118         sftp_pkt_adduint32(pkt, attrs.gid);
119     }
120     if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
121         sftp_pkt_adduint32(pkt, attrs.permissions);
122     }
123     if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
124         sftp_pkt_adduint32(pkt, attrs.atime);
125         sftp_pkt_adduint32(pkt, attrs.mtime);
126     }
127     if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
128         /*
129          * We currently don't support sending any extended
130          * attributes.
131          */
132     }
133 }
134
135 /* ----------------------------------------------------------------------
136  * SFTP packet decode functions.
137  */
138
139 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
140 {
141     unsigned char value;
142     if (pkt->length - pkt->savedpos < 1)
143         return 0;                      /* arrgh, no way to decline (FIXME?) */
144     value = (unsigned char) pkt->data[pkt->savedpos];
145     pkt->savedpos++;
146     return value;
147 }
148 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
149 {
150     unsigned long value;
151     if (pkt->length - pkt->savedpos < 4)
152         return 0;                      /* arrgh, no way to decline (FIXME?) */
153     value = GET_32BIT(pkt->data + pkt->savedpos);
154     pkt->savedpos += 4;
155     return value;
156 }
157 static void sftp_pkt_getstring(struct sftp_packet *pkt,
158                                char **p, int *length)
159 {
160     *p = NULL;
161     if (pkt->length - pkt->savedpos < 4)
162         return;
163     *length = GET_32BIT(pkt->data + pkt->savedpos);
164     pkt->savedpos += 4;
165     if (pkt->length - pkt->savedpos < *length)
166         return;
167     *p = pkt->data + pkt->savedpos;
168     pkt->savedpos += *length;
169 }
170 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
171 {
172     struct fxp_attrs ret;
173     ret.flags = sftp_pkt_getuint32(pkt);
174     if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
175         unsigned long hi, lo;
176         hi = sftp_pkt_getuint32(pkt);
177         lo = sftp_pkt_getuint32(pkt);
178         ret.size = uint64_make(hi, lo);
179     }
180     if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
181         ret.uid = sftp_pkt_getuint32(pkt);
182         ret.gid = sftp_pkt_getuint32(pkt);
183     }
184     if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
185         ret.permissions = sftp_pkt_getuint32(pkt);
186     }
187     if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
188         ret.atime = sftp_pkt_getuint32(pkt);
189         ret.mtime = sftp_pkt_getuint32(pkt);
190     }
191     if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
192         int count;
193         count = sftp_pkt_getuint32(pkt);
194         while (count--) {
195             char *str;
196             int len;
197             /*
198              * We should try to analyse these, if we ever find one
199              * we recognise.
200              */
201             sftp_pkt_getstring(pkt, &str, &len);
202             sftp_pkt_getstring(pkt, &str, &len);
203         }
204     }
205     return ret;
206 }
207 static void sftp_pkt_free(struct sftp_packet *pkt)
208 {
209     if (pkt->data)
210         sfree(pkt->data);
211     sfree(pkt);
212 }
213
214 /* ----------------------------------------------------------------------
215  * Send and receive packet functions.
216  */
217 int sftp_send(struct sftp_packet *pkt)
218 {
219     int ret;
220     char x[4];
221     PUT_32BIT(x, pkt->length);
222     ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
223     sftp_pkt_free(pkt);
224     return ret;
225 }
226 struct sftp_packet *sftp_recv(void)
227 {
228     struct sftp_packet *pkt;
229     char x[4];
230
231     if (!sftp_recvdata(x, 4))
232         return NULL;
233
234     pkt = snew(struct sftp_packet);
235     pkt->savedpos = 0;
236     pkt->length = pkt->maxlen = GET_32BIT(x);
237     pkt->data = snewn(pkt->length, char);
238
239     if (!sftp_recvdata(pkt->data, pkt->length)) {
240         sftp_pkt_free(pkt);
241         return NULL;
242     }
243
244     pkt->type = sftp_pkt_getbyte(pkt);
245
246     return pkt;
247 }
248
249 /* ----------------------------------------------------------------------
250  * Request ID allocation and temporary dispatch routines.
251  */
252
253 #define REQUEST_ID_OFFSET 256
254
255 struct sftp_request {
256     unsigned id;
257     int registered;
258 };
259
260 static int sftp_reqcmp(void *av, void *bv)
261 {
262     struct sftp_request *a = (struct sftp_request *)av;
263     struct sftp_request *b = (struct sftp_request *)bv;
264     if (a->id < b->id)
265         return -1;
266     if (a->id > b->id)
267         return +1;
268     return 0;
269 }
270 static int sftp_reqfind(void *av, void *bv)
271 {
272     unsigned *a = (unsigned *) av;
273     struct sftp_request *b = (struct sftp_request *)bv;
274     if (*a < b->id)
275         return -1;
276     if (*a > b->id)
277         return +1;
278     return 0;
279 }
280
281 static tree234 *sftp_requests;
282
283 static struct sftp_request *sftp_alloc_request(void)
284 {
285     const unsigned CHANNEL_NUMBER_OFFSET = 256;
286     unsigned low, high, mid;
287     int tsize;
288     struct sftp_request *r;
289
290     if (sftp_requests == NULL)
291         sftp_requests = newtree234(sftp_reqcmp);
292
293     /*
294      * First-fit allocation of request IDs: always pick the lowest
295      * unused one. To do this, binary-search using the counted
296      * B-tree to find the largest ID which is in a contiguous
297      * sequence from the beginning. (Precisely everything in that
298      * sequence must have ID equal to its tree index plus
299      * SEQUENCE_NUMBER_OFFSET.)
300      */
301     tsize = count234(sftp_requests);
302
303     low = -1;
304     high = tsize;
305     while (high - low > 1) {
306         mid = (high + low) / 2;
307         r = index234(sftp_requests, mid);
308         if (r->id == mid + REQUEST_ID_OFFSET)
309             low = mid;                 /* this one is fine */
310         else
311             high = mid;                /* this one is past it */
312     }
313     /*
314      * Now low points to either -1, or the tree index of the
315      * largest ID in the initial sequence.
316      */
317     {
318         unsigned i = low + 1 + REQUEST_ID_OFFSET;
319         assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
320     }
321
322     /*
323      * So the request ID we need to create is
324      * low + 1 + REQUEST_ID_OFFSET.
325      */
326     r = snew(struct sftp_request);
327     r->id = low + 1 + REQUEST_ID_OFFSET;
328     r->registered = 0;
329     add234(sftp_requests, r);
330     return r;
331 }
332
333 void sftp_register(struct sftp_request *req)
334 {
335     req->registered = 1;
336 }
337
338 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
339 {
340     unsigned long id;
341     struct sftp_request *req;
342
343     if (!pktin) {
344         fxp_internal_error("did not receive a valid SFTP packet\n");
345         return NULL;
346     }
347
348     id = sftp_pkt_getuint32(pktin);
349     req = find234(sftp_requests, &id, sftp_reqfind);
350
351     if (!req || !req->registered) {
352         fxp_internal_error("request ID mismatch\n");
353         sftp_pkt_free(pktin);
354         return NULL;
355     }
356
357     del234(sftp_requests, req);
358
359     return req;
360 }
361
362 /* ----------------------------------------------------------------------
363  * String handling routines.
364  */
365
366 static char *mkstr(char *s, int len)
367 {
368     char *p = snewn(len + 1, char);
369     memcpy(p, s, len);
370     p[len] = '\0';
371     return p;
372 }
373
374 /* ----------------------------------------------------------------------
375  * SFTP primitives.
376  */
377
378 /*
379  * Deal with (and free) an FXP_STATUS packet. Return 1 if
380  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
381  * Also place the status into fxp_errtype.
382  */
383 static int fxp_got_status(struct sftp_packet *pktin)
384 {
385     static const char *const messages[] = {
386         /* SSH_FX_OK. The only time we will display a _message_ for this
387          * is if we were expecting something other than FXP_STATUS on
388          * success, so this is actually an error message! */
389         "unexpected OK response",
390         "end of file",
391         "no such file or directory",
392         "permission denied",
393         "failure",
394         "bad message",
395         "no connection",
396         "connection lost",
397         "operation unsupported",
398     };
399
400     if (pktin->type != SSH_FXP_STATUS) {
401         fxp_error_message = "expected FXP_STATUS packet";
402         fxp_errtype = -1;
403     } else {
404         fxp_errtype = sftp_pkt_getuint32(pktin);
405         if (fxp_errtype < 0 ||
406             fxp_errtype >= sizeof(messages) / sizeof(*messages))
407                 fxp_error_message = "unknown error code";
408         else
409             fxp_error_message = messages[fxp_errtype];
410     }
411
412     if (fxp_errtype == SSH_FX_OK)
413         return 1;
414     else if (fxp_errtype == SSH_FX_EOF)
415         return 0;
416     else
417         return -1;
418 }
419
420 static void fxp_internal_error(char *msg)
421 {
422     fxp_error_message = msg;
423     fxp_errtype = -1;
424 }
425
426 const char *fxp_error(void)
427 {
428     return fxp_error_message;
429 }
430
431 int fxp_error_type(void)
432 {
433     return fxp_errtype;
434 }
435
436 /*
437  * Perform exchange of init/version packets. Return 0 on failure.
438  */
439 int fxp_init(void)
440 {
441     struct sftp_packet *pktout, *pktin;
442     int remotever;
443
444     pktout = sftp_pkt_init(SSH_FXP_INIT);
445     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
446     sftp_send(pktout);
447
448     pktin = sftp_recv();
449     if (!pktin) {
450         fxp_internal_error("could not connect");
451         return 0;
452     }
453     if (pktin->type != SSH_FXP_VERSION) {
454         fxp_internal_error("did not receive FXP_VERSION");
455         sftp_pkt_free(pktin);
456         return 0;
457     }
458     remotever = sftp_pkt_getuint32(pktin);
459     if (remotever > SFTP_PROTO_VERSION) {
460         fxp_internal_error
461             ("remote protocol is more advanced than we support");
462         sftp_pkt_free(pktin);
463         return 0;
464     }
465     /*
466      * In principle, this packet might also contain extension-
467      * string pairs. We should work through them and look for any
468      * we recognise. In practice we don't currently do so because
469      * we know we don't recognise _any_.
470      */
471     sftp_pkt_free(pktin);
472
473     return 1;
474 }
475
476 /*
477  * Canonify a pathname.
478  */
479 struct sftp_request *fxp_realpath_send(char *path)
480 {
481     struct sftp_request *req = sftp_alloc_request();
482     struct sftp_packet *pktout;
483
484     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
485     sftp_pkt_adduint32(pktout, req->id);
486     sftp_pkt_addstring_start(pktout);
487     sftp_pkt_addstring_str(pktout, path);
488     sftp_send(pktout);
489
490     return req;
491 }
492
493 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
494 {
495     sfree(req);
496
497     if (pktin->type == SSH_FXP_NAME) {
498         int count;
499         char *path;
500         int len;
501
502         count = sftp_pkt_getuint32(pktin);
503         if (count != 1) {
504             fxp_internal_error("REALPATH returned name count != 1\n");
505             sftp_pkt_free(pktin);
506             return NULL;
507         }
508         sftp_pkt_getstring(pktin, &path, &len);
509         if (!path) {
510             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
511             sftp_pkt_free(pktin);
512             return NULL;
513         }
514         path = mkstr(path, len);
515         sftp_pkt_free(pktin);
516         return path;
517     } else {
518         fxp_got_status(pktin);
519         sftp_pkt_free(pktin);
520         return NULL;
521     }
522 }
523
524 /*
525  * Open a file.
526  */
527 struct sftp_request *fxp_open_send(char *path, int type)
528 {
529     struct sftp_request *req = sftp_alloc_request();
530     struct sftp_packet *pktout;
531
532     pktout = sftp_pkt_init(SSH_FXP_OPEN);
533     sftp_pkt_adduint32(pktout, req->id);
534     sftp_pkt_addstring(pktout, path);
535     sftp_pkt_adduint32(pktout, type);
536     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
537     sftp_send(pktout);
538
539     return req;
540 }
541
542 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
543                                  struct sftp_request *req)
544 {
545     sfree(req);
546
547     if (pktin->type == SSH_FXP_HANDLE) {
548         char *hstring;
549         struct fxp_handle *handle;
550         int len;
551
552         sftp_pkt_getstring(pktin, &hstring, &len);
553         if (!hstring) {
554             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
555             sftp_pkt_free(pktin);
556             return NULL;
557         }
558         handle = snew(struct fxp_handle);
559         handle->hstring = mkstr(hstring, len);
560         handle->hlen = len;
561         sftp_pkt_free(pktin);
562         return handle;
563     } else {
564         fxp_got_status(pktin);
565         sftp_pkt_free(pktin);
566         return NULL;
567     }
568 }
569
570 /*
571  * Open a directory.
572  */
573 struct sftp_request *fxp_opendir_send(char *path)
574 {
575     struct sftp_request *req = sftp_alloc_request();
576     struct sftp_packet *pktout;
577
578     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
579     sftp_pkt_adduint32(pktout, req->id);
580     sftp_pkt_addstring(pktout, path);
581     sftp_send(pktout);
582
583     return req;
584 }
585
586 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
587                                     struct sftp_request *req)
588 {
589     sfree(req);
590     if (pktin->type == SSH_FXP_HANDLE) {
591         char *hstring;
592         struct fxp_handle *handle;
593         int len;
594
595         sftp_pkt_getstring(pktin, &hstring, &len);
596         if (!hstring) {
597             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
598             sftp_pkt_free(pktin);
599             return NULL;
600         }
601         handle = snew(struct fxp_handle);
602         handle->hstring = mkstr(hstring, len);
603         handle->hlen = len;
604         sftp_pkt_free(pktin);
605         return handle;
606     } else {
607         fxp_got_status(pktin);
608         sftp_pkt_free(pktin);
609         return NULL;
610     }
611 }
612
613 /*
614  * Close a file/dir.
615  */
616 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
617 {
618     struct sftp_request *req = sftp_alloc_request();
619     struct sftp_packet *pktout;
620
621     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
622     sftp_pkt_adduint32(pktout, req->id);
623     sftp_pkt_addstring_start(pktout);
624     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
625     sftp_send(pktout);
626
627     sfree(handle->hstring);
628     sfree(handle);
629
630     return req;
631 }
632
633 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
634 {
635     sfree(req);
636     fxp_got_status(pktin);
637     sftp_pkt_free(pktin);
638 }
639
640 struct sftp_request *fxp_mkdir_send(char *path)
641 {
642     struct sftp_request *req = sftp_alloc_request();
643     struct sftp_packet *pktout;
644
645     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
646     sftp_pkt_adduint32(pktout, req->id);
647     sftp_pkt_addstring(pktout, path);
648     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
649     sftp_send(pktout);
650
651     return req;
652 }
653
654 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
655 {
656     int id;
657     sfree(req);
658     id = fxp_got_status(pktin);
659     sftp_pkt_free(pktin);
660     if (id != 1) {
661         return 0;
662     }
663     return 1;
664 }
665
666 struct sftp_request *fxp_rmdir_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_RMDIR);
672     sftp_pkt_adduint32(pktout, req->id);
673     sftp_pkt_addstring(pktout, path);
674     sftp_send(pktout);
675
676     return req;
677 }
678
679 int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
680 {
681     int id;
682     sfree(req);
683     id = fxp_got_status(pktin);
684     sftp_pkt_free(pktin);
685     if (id != 1) {
686         return 0;
687     }
688     return 1;
689 }
690
691 struct sftp_request *fxp_remove_send(char *fname)
692 {
693     struct sftp_request *req = sftp_alloc_request();
694     struct sftp_packet *pktout;
695
696     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
697     sftp_pkt_adduint32(pktout, req->id);
698     sftp_pkt_addstring(pktout, fname);
699     sftp_send(pktout);
700
701     return req;
702 }
703
704 int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
705 {
706     int id;
707     sfree(req);
708     id = fxp_got_status(pktin);
709     sftp_pkt_free(pktin);
710     if (id != 1) {
711         return 0;
712     }
713     return 1;
714 }
715
716 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
717 {
718     struct sftp_request *req = sftp_alloc_request();
719     struct sftp_packet *pktout;
720
721     pktout = sftp_pkt_init(SSH_FXP_RENAME);
722     sftp_pkt_adduint32(pktout, req->id);
723     sftp_pkt_addstring(pktout, srcfname);
724     sftp_pkt_addstring(pktout, dstfname);
725     sftp_send(pktout);
726
727     return req;
728 }
729
730 int fxp_rename_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 /*
743  * Retrieve the attributes of a file. We have fxp_stat which works
744  * on filenames, and fxp_fstat which works on open file handles.
745  */
746 struct sftp_request *fxp_stat_send(char *fname)
747 {
748     struct sftp_request *req = sftp_alloc_request();
749     struct sftp_packet *pktout;
750
751     pktout = sftp_pkt_init(SSH_FXP_STAT);
752     sftp_pkt_adduint32(pktout, req->id);
753     sftp_pkt_addstring(pktout, fname);
754     sftp_send(pktout);
755
756     return req;
757 }
758
759 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
760                   struct fxp_attrs *attrs)
761 {
762     sfree(req);
763     if (pktin->type == SSH_FXP_ATTRS) {
764         *attrs = sftp_pkt_getattrs(pktin);
765         sftp_pkt_free(pktin);
766         return 1;
767     } else {
768         fxp_got_status(pktin);
769         sftp_pkt_free(pktin);
770         return 0;
771     }
772 }
773
774 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
775 {
776     struct sftp_request *req = sftp_alloc_request();
777     struct sftp_packet *pktout;
778
779     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
780     sftp_pkt_adduint32(pktout, req->id);
781     sftp_pkt_addstring_start(pktout);
782     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
783     sftp_send(pktout);
784
785     return req;
786 }
787
788 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
789                    struct fxp_attrs *attrs)
790 {
791     sfree(req);
792     if (pktin->type == SSH_FXP_ATTRS) {
793         *attrs = sftp_pkt_getattrs(pktin);
794         sftp_pkt_free(pktin);
795         return 1;
796     } else {
797         fxp_got_status(pktin);
798         sftp_pkt_free(pktin);
799         return 0;
800     }
801 }
802
803 /*
804  * Set the attributes of a file.
805  */
806 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
807 {
808     struct sftp_request *req = sftp_alloc_request();
809     struct sftp_packet *pktout;
810
811     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
812     sftp_pkt_adduint32(pktout, req->id);
813     sftp_pkt_addstring(pktout, fname);
814     sftp_pkt_addattrs(pktout, attrs);
815     sftp_send(pktout);
816
817     return req;
818 }
819
820 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
821 {
822     int id;
823     sfree(req);
824     id = fxp_got_status(pktin);
825     sftp_pkt_free(pktin);
826     if (id != 1) {
827         return 0;
828     }
829     return 1;
830 }
831
832 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
833                                        struct fxp_attrs attrs)
834 {
835     struct sftp_request *req = sftp_alloc_request();
836     struct sftp_packet *pktout;
837
838     pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
839     sftp_pkt_adduint32(pktout, req->id);
840     sftp_pkt_addstring_start(pktout);
841     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
842     sftp_pkt_addattrs(pktout, attrs);
843     sftp_send(pktout);
844
845     return req;
846 }
847
848 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
849 {
850     int id;
851     sfree(req);
852     id = fxp_got_status(pktin);
853     sftp_pkt_free(pktin);
854     if (id != 1) {
855         return 0;
856     }
857     return 1;
858 }
859
860 /*
861  * Read from a file. Returns the number of bytes read, or -1 on an
862  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
863  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
864  * error indicator. It might even depend on the SFTP server.)
865  */
866 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
867                                    uint64 offset, int len)
868 {
869     struct sftp_request *req = sftp_alloc_request();
870     struct sftp_packet *pktout;
871
872     pktout = sftp_pkt_init(SSH_FXP_READ);
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_adduint64(pktout, offset);
877     sftp_pkt_adduint32(pktout, len);
878     sftp_send(pktout);
879
880     return req;
881 }
882
883 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
884                   char *buffer, int len)
885 {
886     sfree(req);
887     if (pktin->type == SSH_FXP_DATA) {
888         char *str;
889         int rlen;
890
891         sftp_pkt_getstring(pktin, &str, &rlen);
892
893         if (rlen > len || rlen < 0) {
894             fxp_internal_error("READ returned more bytes than requested");
895             sftp_pkt_free(pktin);
896             return -1;
897         }
898
899         memcpy(buffer, str, rlen);
900         sftp_pkt_free(pktin);
901         return rlen;
902     } else {
903         fxp_got_status(pktin);
904         sftp_pkt_free(pktin);
905         return -1;
906     }
907 }
908
909 /*
910  * Read from a directory.
911  */
912 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
913 {
914     struct sftp_request *req = sftp_alloc_request();
915     struct sftp_packet *pktout;
916
917     pktout = sftp_pkt_init(SSH_FXP_READDIR);
918     sftp_pkt_adduint32(pktout, req->id);
919     sftp_pkt_addstring_start(pktout);
920     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
921     sftp_send(pktout);
922
923     return req;
924 }
925
926 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
927                                    struct sftp_request *req)
928 {
929     sfree(req);
930     if (pktin->type == SSH_FXP_NAME) {
931         struct fxp_names *ret;
932         int i;
933         ret = snew(struct fxp_names);
934         ret->nnames = sftp_pkt_getuint32(pktin);
935         ret->names = snewn(ret->nnames, struct fxp_name);
936         for (i = 0; i < ret->nnames; i++) {
937             char *str;
938             int len;
939             sftp_pkt_getstring(pktin, &str, &len);
940             ret->names[i].filename = mkstr(str, len);
941             sftp_pkt_getstring(pktin, &str, &len);
942             ret->names[i].longname = mkstr(str, len);
943             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
944         }
945         sftp_pkt_free(pktin);
946         return ret;
947     } else {
948         fxp_got_status(pktin);
949         sftp_pkt_free(pktin);
950         return NULL;
951     }
952 }
953
954 /*
955  * Write to a file. Returns 0 on error, 1 on OK.
956  */
957 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
958                                     char *buffer, uint64 offset, int len)
959 {
960     struct sftp_request *req = sftp_alloc_request();
961     struct sftp_packet *pktout;
962
963     pktout = sftp_pkt_init(SSH_FXP_WRITE);
964     sftp_pkt_adduint32(pktout, req->id);
965     sftp_pkt_addstring_start(pktout);
966     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
967     sftp_pkt_adduint64(pktout, offset);
968     sftp_pkt_addstring_start(pktout);
969     sftp_pkt_addstring_data(pktout, buffer, len);
970     sftp_send(pktout);
971
972     return req;
973 }
974
975 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
976 {
977     sfree(req);
978     fxp_got_status(pktin);
979     sftp_pkt_free(pktin);
980     return fxp_errtype == SSH_FX_OK;
981 }
982
983 /*
984  * Free up an fxp_names structure.
985  */
986 void fxp_free_names(struct fxp_names *names)
987 {
988     int i;
989
990     for (i = 0; i < names->nnames; i++) {
991         sfree(names->names[i].filename);
992         sfree(names->names[i].longname);
993     }
994     sfree(names->names);
995     sfree(names);
996 }
997
998 /*
999  * Duplicate an fxp_name structure.
1000  */
1001 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1002 {
1003     struct fxp_name *ret;
1004     ret = snew(struct fxp_name);
1005     ret->filename = dupstr(name->filename);
1006     ret->longname = dupstr(name->longname);
1007     ret->attrs = name->attrs;          /* structure copy */
1008     return ret;
1009 }
1010
1011 /*
1012  * Free up an fxp_name structure.
1013  */
1014 void fxp_free_name(struct fxp_name *name)
1015 {
1016     sfree(name->filename);
1017     sfree(name->longname);
1018     sfree(name);
1019 }