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