Phase 1a of SFTP re-engineering: fix the glaring memory and request
[u/mdw/putty] / sftp.c
1 /*
2 * sftp.c: SFTP generic client code.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9
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 }