]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
More upgrades to psftp: it now supports mv, chmod, reget and reput.
[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
10 #include "int64.h"
11 #include "sftp.h"
12
13 #define smalloc malloc
14 #define srealloc realloc
15 #define sfree free
16
17 #define GET_32BIT(cp) \
18     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
19     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
20     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
21     ((unsigned long)(unsigned char)(cp)[3]))
22
23 #define PUT_32BIT(cp, value) { \
24     (cp)[0] = (unsigned char)((value) >> 24); \
25     (cp)[1] = (unsigned char)((value) >> 16); \
26     (cp)[2] = (unsigned char)((value) >> 8); \
27     (cp)[3] = (unsigned char)(value); }
28
29 struct sftp_packet {
30     char *data;
31     int length, maxlen;
32     int savedpos;
33     int type;
34 };
35
36 static const char *fxp_error_message;
37 static int fxp_errtype;
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 = srealloc(pkt->data, pkt->maxlen);
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 = smalloc(sizeof(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     fxp_error_message = NULL;
69     return pkt;
70 }
71 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
72 {
73     sftp_pkt_adddata(pkt, &value, 1);
74 }
75 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
76                                unsigned long value)
77 {
78     unsigned char x[4];
79     PUT_32BIT(x, value);
80     sftp_pkt_adddata(pkt, x, 4);
81 }
82 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
83 {
84     unsigned char x[8];
85     PUT_32BIT(x, value.hi);
86     PUT_32BIT(x + 4, value.lo);
87     sftp_pkt_adddata(pkt, x, 8);
88 }
89 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
90 {
91     sftp_pkt_adduint32(pkt, 0);
92     pkt->savedpos = pkt->length;
93 }
94 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
95 {
96     sftp_pkt_adddata(pkt, data, strlen(data));
97     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
98 }
99 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
100                                     char *data, int len)
101 {
102     sftp_pkt_adddata(pkt, data, len);
103     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
104 }
105 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
106 {
107     sftp_pkt_addstring_start(pkt);
108     sftp_pkt_addstring_str(pkt, data);
109 }
110 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
111 {
112     sftp_pkt_adduint32(pkt, attrs.flags);
113     if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
114         sftp_pkt_adduint32(pkt, attrs.size.hi);
115         sftp_pkt_adduint32(pkt, attrs.size.lo);
116     }
117     if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
118         sftp_pkt_adduint32(pkt, attrs.uid);
119         sftp_pkt_adduint32(pkt, attrs.gid);
120     }
121     if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
122         sftp_pkt_adduint32(pkt, attrs.permissions);
123     }
124     if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
125         sftp_pkt_adduint32(pkt, attrs.atime);
126         sftp_pkt_adduint32(pkt, attrs.mtime);
127     }
128     if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
129         /*
130          * We currently don't support sending any extended
131          * attributes.
132          */
133     }
134 }
135
136 /* ----------------------------------------------------------------------
137  * SFTP packet decode functions.
138  */
139
140 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
141 {
142     unsigned char value;
143     if (pkt->length - pkt->savedpos < 1)
144         return 0;                      /* arrgh, no way to decline (FIXME?) */
145     value = (unsigned char) pkt->data[pkt->savedpos];
146     pkt->savedpos++;
147     return value;
148 }
149 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
150 {
151     unsigned long value;
152     if (pkt->length - pkt->savedpos < 4)
153         return 0;                      /* arrgh, no way to decline (FIXME?) */
154     value = GET_32BIT(pkt->data + pkt->savedpos);
155     pkt->savedpos += 4;
156     return value;
157 }
158 static void sftp_pkt_getstring(struct sftp_packet *pkt,
159                                char **p, int *length)
160 {
161     *p = NULL;
162     if (pkt->length - pkt->savedpos < 4)
163         return;
164     *length = GET_32BIT(pkt->data + pkt->savedpos);
165     pkt->savedpos += 4;
166     if (pkt->length - pkt->savedpos < *length)
167         return;
168     *p = pkt->data + pkt->savedpos;
169     pkt->savedpos += *length;
170 }
171 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
172 {
173     struct fxp_attrs ret;
174     ret.flags = sftp_pkt_getuint32(pkt);
175     if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
176         unsigned long hi, lo;
177         hi = sftp_pkt_getuint32(pkt);
178         lo = sftp_pkt_getuint32(pkt);
179         ret.size = uint64_make(hi, lo);
180     }
181     if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
182         ret.uid = sftp_pkt_getuint32(pkt);
183         ret.gid = sftp_pkt_getuint32(pkt);
184     }
185     if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
186         ret.permissions = sftp_pkt_getuint32(pkt);
187     }
188     if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
189         ret.atime = sftp_pkt_getuint32(pkt);
190         ret.mtime = sftp_pkt_getuint32(pkt);
191     }
192     if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
193         int count;
194         count = sftp_pkt_getuint32(pkt);
195         while (count--) {
196             char *str;
197             int len;
198             /*
199              * We should try to analyse these, if we ever find one
200              * we recognise.
201              */
202             sftp_pkt_getstring(pkt, &str, &len);
203             sftp_pkt_getstring(pkt, &str, &len);
204         }
205     }
206     return ret;
207 }
208 static void sftp_pkt_free(struct sftp_packet *pkt)
209 {
210     if (pkt->data)
211         sfree(pkt->data);
212     sfree(pkt);
213 }
214
215 /* ----------------------------------------------------------------------
216  * Send and receive packet functions.
217  */
218 int sftp_send(struct sftp_packet *pkt)
219 {
220     int ret;
221     char x[4];
222     PUT_32BIT(x, pkt->length);
223     ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
224     sftp_pkt_free(pkt);
225     return ret;
226 }
227 struct sftp_packet *sftp_recv(void)
228 {
229     struct sftp_packet *pkt;
230     char x[4];
231
232     if (!sftp_recvdata(x, 4))
233         return NULL;
234
235     pkt = smalloc(sizeof(struct sftp_packet));
236     pkt->savedpos = 0;
237     pkt->length = pkt->maxlen = GET_32BIT(x);
238     pkt->data = smalloc(pkt->length);
239
240     if (!sftp_recvdata(pkt->data, pkt->length)) {
241         sftp_pkt_free(pkt);
242         return NULL;
243     }
244
245     pkt->type = sftp_pkt_getbyte(pkt);
246
247     return pkt;
248 }
249
250 /* ----------------------------------------------------------------------
251  * String handling routines.
252  */
253
254 static char *mkstr(char *s, int len)
255 {
256     char *p = smalloc(len + 1);
257     memcpy(p, s, len);
258     p[len] = '\0';
259     return p;
260 }
261
262 /* ----------------------------------------------------------------------
263  * SFTP primitives.
264  */
265
266 /*
267  * Deal with (and free) an FXP_STATUS packet. Return 1 if
268  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
269  * Also place the status into fxp_errtype.
270  */
271 static int fxp_got_status(struct sftp_packet *pktin)
272 {
273     static const char *const messages[] = {
274         /* SSH_FX_OK. The only time we will display a _message_ for this
275          * is if we were expecting something other than FXP_STATUS on
276          * success, so this is actually an error message! */
277         "unexpected OK response",
278         "end of file",
279         "no such file or directory",
280         "permission denied",
281         "failure",
282         "bad message",
283         "no connection",
284         "connection lost",
285         "operation unsupported",
286     };
287
288     if (pktin->type != SSH_FXP_STATUS) {
289         fxp_error_message = "expected FXP_STATUS packet";
290         fxp_errtype = -1;
291     } else {
292         fxp_errtype = sftp_pkt_getuint32(pktin);
293         if (fxp_errtype < 0 ||
294             fxp_errtype >= sizeof(messages) / sizeof(*messages))
295                 fxp_error_message = "unknown error code";
296         else
297             fxp_error_message = messages[fxp_errtype];
298     }
299
300     if (fxp_errtype == SSH_FX_OK)
301         return 1;
302     else if (fxp_errtype == SSH_FX_EOF)
303         return 0;
304     else
305         return -1;
306 }
307
308 static void fxp_internal_error(char *msg)
309 {
310     fxp_error_message = msg;
311     fxp_errtype = -1;
312 }
313
314 const char *fxp_error(void)
315 {
316     return fxp_error_message;
317 }
318
319 int fxp_error_type(void)
320 {
321     return fxp_errtype;
322 }
323
324 /*
325  * Perform exchange of init/version packets. Return 0 on failure.
326  */
327 int fxp_init(void)
328 {
329     struct sftp_packet *pktout, *pktin;
330     int remotever;
331
332     pktout = sftp_pkt_init(SSH_FXP_INIT);
333     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
334     sftp_send(pktout);
335
336     pktin = sftp_recv();
337     if (!pktin) {
338         fxp_internal_error("could not connect");
339         return 0;
340     }
341     if (pktin->type != SSH_FXP_VERSION) {
342         fxp_internal_error("did not receive FXP_VERSION");
343         return 0;
344     }
345     remotever = sftp_pkt_getuint32(pktin);
346     if (remotever > SFTP_PROTO_VERSION) {
347         fxp_internal_error
348             ("remote protocol is more advanced than we support");
349         return 0;
350     }
351     /*
352      * In principle, this packet might also contain extension-
353      * string pairs. We should work through them and look for any
354      * we recognise. In practice we don't currently do so because
355      * we know we don't recognise _any_.
356      */
357     sftp_pkt_free(pktin);
358
359     return 1;
360 }
361
362 /*
363  * Canonify a pathname.
364  */
365 char *fxp_realpath(char *path)
366 {
367     struct sftp_packet *pktin, *pktout;
368     int id;
369
370     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
371     sftp_pkt_adduint32(pktout, 0x123); /* request id */
372     sftp_pkt_addstring_start(pktout);
373     sftp_pkt_addstring_str(pktout, path);
374     sftp_send(pktout);
375     pktin = sftp_recv();
376     id = sftp_pkt_getuint32(pktin);
377     if (id != 0x123) {
378         fxp_internal_error("request ID mismatch\n");
379         return NULL;
380     }
381     if (pktin->type == SSH_FXP_NAME) {
382         int count;
383         char *path;
384         int len;
385
386         count = sftp_pkt_getuint32(pktin);
387         if (count != 1) {
388             fxp_internal_error("REALPATH returned name count != 1\n");
389             return NULL;
390         }
391         sftp_pkt_getstring(pktin, &path, &len);
392         if (!path) {
393             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
394             return NULL;
395         }
396         path = mkstr(path, len);
397         sftp_pkt_free(pktin);
398         return path;
399     } else {
400         fxp_got_status(pktin);
401         return NULL;
402     }
403 }
404
405 /*
406  * Open a file.
407  */
408 struct fxp_handle *fxp_open(char *path, int type)
409 {
410     struct sftp_packet *pktin, *pktout;
411     int id;
412
413     pktout = sftp_pkt_init(SSH_FXP_OPEN);
414     sftp_pkt_adduint32(pktout, 0x567); /* request id */
415     sftp_pkt_addstring(pktout, path);
416     sftp_pkt_adduint32(pktout, type);
417     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
418     sftp_send(pktout);
419     pktin = sftp_recv();
420     id = sftp_pkt_getuint32(pktin);
421     if (id != 0x567) {
422         fxp_internal_error("request ID mismatch\n");
423         return NULL;
424     }
425     if (pktin->type == SSH_FXP_HANDLE) {
426         char *hstring;
427         struct fxp_handle *handle;
428         int len;
429
430         sftp_pkt_getstring(pktin, &hstring, &len);
431         if (!hstring) {
432             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
433             return NULL;
434         }
435         handle = smalloc(sizeof(struct fxp_handle));
436         handle->hstring = mkstr(hstring, len);
437         handle->hlen = len;
438         sftp_pkt_free(pktin);
439         return handle;
440     } else {
441         fxp_got_status(pktin);
442         return NULL;
443     }
444 }
445
446 /*
447  * Open a directory.
448  */
449 struct fxp_handle *fxp_opendir(char *path)
450 {
451     struct sftp_packet *pktin, *pktout;
452     int id;
453
454     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
455     sftp_pkt_adduint32(pktout, 0x456); /* request id */
456     sftp_pkt_addstring(pktout, path);
457     sftp_send(pktout);
458     pktin = sftp_recv();
459     id = sftp_pkt_getuint32(pktin);
460     if (id != 0x456) {
461         fxp_internal_error("request ID mismatch\n");
462         return NULL;
463     }
464     if (pktin->type == SSH_FXP_HANDLE) {
465         char *hstring;
466         struct fxp_handle *handle;
467         int len;
468
469         sftp_pkt_getstring(pktin, &hstring, &len);
470         if (!hstring) {
471             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
472             return NULL;
473         }
474         handle = smalloc(sizeof(struct fxp_handle));
475         handle->hstring = mkstr(hstring, len);
476         handle->hlen = len;
477         sftp_pkt_free(pktin);
478         return handle;
479     } else {
480         fxp_got_status(pktin);
481         return NULL;
482     }
483 }
484
485 /*
486  * Close a file/dir.
487  */
488 void fxp_close(struct fxp_handle *handle)
489 {
490     struct sftp_packet *pktin, *pktout;
491     int id;
492
493     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
494     sftp_pkt_adduint32(pktout, 0x789); /* request id */
495     sftp_pkt_addstring_start(pktout);
496     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
497     sftp_send(pktout);
498     pktin = sftp_recv();
499     id = sftp_pkt_getuint32(pktin);
500     if (id != 0x789) {
501         fxp_internal_error("request ID mismatch\n");
502         return;
503     }
504     fxp_got_status(pktin);
505     sfree(handle->hstring);
506     sfree(handle);
507 }
508
509 int fxp_mkdir(char *path)
510 {
511     struct sftp_packet *pktin, *pktout;
512     int id;
513
514     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
515     sftp_pkt_adduint32(pktout, 0x234); /* request id */
516     sftp_pkt_addstring(pktout, path);
517     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
518     sftp_send(pktout);
519     pktin = sftp_recv();
520     id = sftp_pkt_getuint32(pktin);
521     if (id != 0x234) {
522         fxp_internal_error("request ID mismatch\n");
523         return 0;
524     }
525     id = fxp_got_status(pktin);
526     if (id != 1) {
527         return 0;
528     }
529     return 1;
530 }
531
532 int fxp_rmdir(char *path)
533 {
534     struct sftp_packet *pktin, *pktout;
535     int id;
536
537     pktout = sftp_pkt_init(SSH_FXP_RMDIR);
538     sftp_pkt_adduint32(pktout, 0x345); /* request id */
539     sftp_pkt_addstring(pktout, path);
540     sftp_send(pktout);
541     pktin = sftp_recv();
542     id = sftp_pkt_getuint32(pktin);
543     if (id != 0x345) {
544         fxp_internal_error("request ID mismatch\n");
545         return 0;
546     }
547     id = fxp_got_status(pktin);
548     if (id != 1) {
549         return 0;
550     }
551     return 1;
552 }
553
554 int fxp_remove(char *fname)
555 {
556     struct sftp_packet *pktin, *pktout;
557     int id;
558
559     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
560     sftp_pkt_adduint32(pktout, 0x678); /* request id */
561     sftp_pkt_addstring(pktout, fname);
562     sftp_send(pktout);
563     pktin = sftp_recv();
564     id = sftp_pkt_getuint32(pktin);
565     if (id != 0x678) {
566         fxp_internal_error("request ID mismatch\n");
567         return 0;
568     }
569     id = fxp_got_status(pktin);
570     if (id != 1) {
571         return 0;
572     }
573     return 1;
574 }
575
576 int fxp_rename(char *srcfname, char *dstfname)
577 {
578     struct sftp_packet *pktin, *pktout;
579     int id;
580
581     pktout = sftp_pkt_init(SSH_FXP_RENAME);
582     sftp_pkt_adduint32(pktout, 0x678); /* request id */
583     sftp_pkt_addstring(pktout, srcfname);
584     sftp_pkt_addstring(pktout, dstfname);
585     sftp_send(pktout);
586     pktin = sftp_recv();
587     id = sftp_pkt_getuint32(pktin);
588     if (id != 0x678) {
589         fxp_internal_error("request ID mismatch\n");
590         return 0;
591     }
592     id = fxp_got_status(pktin);
593     if (id != 1) {
594         return 0;
595     }
596     return 1;
597 }
598
599 /*
600  * Retrieve the attributes of a file. We have fxp_stat which works
601  * on filenames, and fxp_fstat which works on open file handles.
602  */
603 int fxp_stat(char *fname, struct fxp_attrs *attrs)
604 {
605     struct sftp_packet *pktin, *pktout;
606     int id;
607
608     pktout = sftp_pkt_init(SSH_FXP_STAT);
609     sftp_pkt_adduint32(pktout, 0x678); /* request id */
610     sftp_pkt_addstring(pktout, fname);
611     sftp_send(pktout);
612     pktin = sftp_recv();
613     id = sftp_pkt_getuint32(pktin);
614     if (id != 0x678) {
615         fxp_internal_error("request ID mismatch\n");
616         return 0;
617     }
618
619     if (pktin->type == SSH_FXP_ATTRS) {
620         *attrs = sftp_pkt_getattrs(pktin);
621         return 1;
622     } else {
623         fxp_got_status(pktin);
624         return 0;
625     }
626 }
627
628 int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
629 {
630     struct sftp_packet *pktin, *pktout;
631     int id;
632
633     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
634     sftp_pkt_adduint32(pktout, 0x678); /* request id */
635     sftp_pkt_addstring_start(pktout);
636     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
637     sftp_send(pktout);
638     pktin = sftp_recv();
639     id = sftp_pkt_getuint32(pktin);
640     if (id != 0x678) {
641         fxp_internal_error("request ID mismatch\n");
642         return 0;
643     }
644
645     if (pktin->type == SSH_FXP_ATTRS) {
646         *attrs = sftp_pkt_getattrs(pktin);
647         return 1;
648     } else {
649         fxp_got_status(pktin);
650         return 0;
651     }
652 }
653
654 /*
655  * Set the attributes of a file.
656  */
657 int fxp_setstat(char *fname, struct fxp_attrs attrs)
658 {
659     struct sftp_packet *pktin, *pktout;
660     int id;
661
662     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
663     sftp_pkt_adduint32(pktout, 0x678); /* request id */
664     sftp_pkt_addstring(pktout, fname);
665     sftp_pkt_addattrs(pktout, attrs);
666     sftp_send(pktout);
667     pktin = sftp_recv();
668     id = sftp_pkt_getuint32(pktin);
669     if (id != 0x678) {
670         fxp_internal_error("request ID mismatch\n");
671         return 0;
672     }
673     id = fxp_got_status(pktin);
674     if (id != 1) {
675         return 0;
676     }
677     return 1;
678 }
679
680 /*
681  * Read from a file. Returns the number of bytes read, or -1 on an
682  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
683  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
684  * error indicator. It might even depend on the SFTP server.)
685  */
686 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
687              int len)
688 {
689     struct sftp_packet *pktin, *pktout;
690     int id;
691
692     pktout = sftp_pkt_init(SSH_FXP_READ);
693     sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
694     sftp_pkt_addstring_start(pktout);
695     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
696     sftp_pkt_adduint64(pktout, offset);
697     sftp_pkt_adduint32(pktout, len);
698     sftp_send(pktout);
699     pktin = sftp_recv();
700     id = sftp_pkt_getuint32(pktin);
701     if (id != 0xBCD) {
702         fxp_internal_error("request ID mismatch");
703         return -1;
704     }
705     if (pktin->type == SSH_FXP_DATA) {
706         char *str;
707         int rlen;
708
709         sftp_pkt_getstring(pktin, &str, &rlen);
710
711         if (rlen > len || rlen < 0) {
712             fxp_internal_error("READ returned more bytes than requested");
713             return -1;
714         }
715
716         memcpy(buffer, str, rlen);
717         sfree(pktin);
718         return rlen;
719     } else {
720         fxp_got_status(pktin);
721         return -1;
722     }
723 }
724
725 /*
726  * Read from a directory.
727  */
728 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
729 {
730     struct sftp_packet *pktin, *pktout;
731     int id;
732
733     pktout = sftp_pkt_init(SSH_FXP_READDIR);
734     sftp_pkt_adduint32(pktout, 0xABC); /* request id */
735     sftp_pkt_addstring_start(pktout);
736     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
737     sftp_send(pktout);
738     pktin = sftp_recv();
739     id = sftp_pkt_getuint32(pktin);
740     if (id != 0xABC) {
741         fxp_internal_error("request ID mismatch\n");
742         return NULL;
743     }
744     if (pktin->type == SSH_FXP_NAME) {
745         struct fxp_names *ret;
746         int i;
747         ret = smalloc(sizeof(struct fxp_names));
748         ret->nnames = sftp_pkt_getuint32(pktin);
749         ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
750         for (i = 0; i < ret->nnames; i++) {
751             char *str;
752             int len;
753             sftp_pkt_getstring(pktin, &str, &len);
754             ret->names[i].filename = mkstr(str, len);
755             sftp_pkt_getstring(pktin, &str, &len);
756             ret->names[i].longname = mkstr(str, len);
757             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
758         }
759         return ret;
760     } else {
761         fxp_got_status(pktin);
762         return NULL;
763     }
764 }
765
766 /*
767  * Write to a file. Returns 0 on error, 1 on OK.
768  */
769 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
770               int len)
771 {
772     struct sftp_packet *pktin, *pktout;
773     int id;
774
775     pktout = sftp_pkt_init(SSH_FXP_WRITE);
776     sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
777     sftp_pkt_addstring_start(pktout);
778     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
779     sftp_pkt_adduint64(pktout, offset);
780     sftp_pkt_addstring_start(pktout);
781     sftp_pkt_addstring_data(pktout, buffer, len);
782     sftp_send(pktout);
783     pktin = sftp_recv();
784     id = sftp_pkt_getuint32(pktin);
785     if (id != 0xDCB) {
786         fxp_internal_error("request ID mismatch\n");
787         return 0;
788     }
789     fxp_got_status(pktin);
790     return fxp_errtype == SSH_FX_OK;
791 }
792
793 /*
794  * Free up an fxp_names structure.
795  */
796 void fxp_free_names(struct fxp_names *names)
797 {
798     int i;
799
800     for (i = 0; i < names->nnames; i++) {
801         sfree(names->names[i].filename);
802         sfree(names->names[i].longname);
803     }
804     sfree(names->names);
805     sfree(names);
806 }