First phase of SFTP re-engineering. Each base-level fxp_* function
[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 return req;
358 }
359
360 /* ----------------------------------------------------------------------
361 * String handling routines.
362 */
363
364 static char *mkstr(char *s, int len)
365 {
366 char *p = snewn(len + 1, char);
367 memcpy(p, s, len);
368 p[len] = '\0';
369 return p;
370 }
371
372 /* ----------------------------------------------------------------------
373 * SFTP primitives.
374 */
375
376 /*
377 * Deal with (and free) an FXP_STATUS packet. Return 1 if
378 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
379 * Also place the status into fxp_errtype.
380 */
381 static int fxp_got_status(struct sftp_packet *pktin)
382 {
383 static const char *const messages[] = {
384 /* SSH_FX_OK. The only time we will display a _message_ for this
385 * is if we were expecting something other than FXP_STATUS on
386 * success, so this is actually an error message! */
387 "unexpected OK response",
388 "end of file",
389 "no such file or directory",
390 "permission denied",
391 "failure",
392 "bad message",
393 "no connection",
394 "connection lost",
395 "operation unsupported",
396 };
397
398 if (pktin->type != SSH_FXP_STATUS) {
399 fxp_error_message = "expected FXP_STATUS packet";
400 fxp_errtype = -1;
401 } else {
402 fxp_errtype = sftp_pkt_getuint32(pktin);
403 if (fxp_errtype < 0 ||
404 fxp_errtype >= sizeof(messages) / sizeof(*messages))
405 fxp_error_message = "unknown error code";
406 else
407 fxp_error_message = messages[fxp_errtype];
408 }
409
410 if (fxp_errtype == SSH_FX_OK)
411 return 1;
412 else if (fxp_errtype == SSH_FX_EOF)
413 return 0;
414 else
415 return -1;
416 }
417
418 static void fxp_internal_error(char *msg)
419 {
420 fxp_error_message = msg;
421 fxp_errtype = -1;
422 }
423
424 const char *fxp_error(void)
425 {
426 return fxp_error_message;
427 }
428
429 int fxp_error_type(void)
430 {
431 return fxp_errtype;
432 }
433
434 /*
435 * Perform exchange of init/version packets. Return 0 on failure.
436 */
437 int fxp_init(void)
438 {
439 struct sftp_packet *pktout, *pktin;
440 int remotever;
441
442 pktout = sftp_pkt_init(SSH_FXP_INIT);
443 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
444 sftp_send(pktout);
445
446 pktin = sftp_recv();
447 if (!pktin) {
448 fxp_internal_error("could not connect");
449 return 0;
450 }
451 if (pktin->type != SSH_FXP_VERSION) {
452 fxp_internal_error("did not receive FXP_VERSION");
453 sftp_pkt_free(pktin);
454 return 0;
455 }
456 remotever = sftp_pkt_getuint32(pktin);
457 if (remotever > SFTP_PROTO_VERSION) {
458 fxp_internal_error
459 ("remote protocol is more advanced than we support");
460 sftp_pkt_free(pktin);
461 return 0;
462 }
463 /*
464 * In principle, this packet might also contain extension-
465 * string pairs. We should work through them and look for any
466 * we recognise. In practice we don't currently do so because
467 * we know we don't recognise _any_.
468 */
469 sftp_pkt_free(pktin);
470
471 return 1;
472 }
473
474 /*
475 * Canonify a pathname.
476 */
477 struct sftp_request *fxp_realpath_send(char *path)
478 {
479 struct sftp_request *req = sftp_alloc_request();
480 struct sftp_packet *pktout;
481
482 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
483 sftp_pkt_adduint32(pktout, req->id);
484 sftp_pkt_addstring_start(pktout);
485 sftp_pkt_addstring_str(pktout, path);
486 sftp_send(pktout);
487
488 return req;
489 }
490
491 char *fxp_realpath_recv(struct sftp_packet *pktin)
492 {
493 if (pktin->type == SSH_FXP_NAME) {
494 int count;
495 char *path;
496 int len;
497
498 count = sftp_pkt_getuint32(pktin);
499 if (count != 1) {
500 fxp_internal_error("REALPATH returned name count != 1\n");
501 sftp_pkt_free(pktin);
502 return NULL;
503 }
504 sftp_pkt_getstring(pktin, &path, &len);
505 if (!path) {
506 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
507 sftp_pkt_free(pktin);
508 return NULL;
509 }
510 path = mkstr(path, len);
511 sftp_pkt_free(pktin);
512 return path;
513 } else {
514 fxp_got_status(pktin);
515 sftp_pkt_free(pktin);
516 return NULL;
517 }
518 }
519
520 /*
521 * Open a file.
522 */
523 struct sftp_request *fxp_open_send(char *path, int type)
524 {
525 struct sftp_request *req = sftp_alloc_request();
526 struct sftp_packet *pktout;
527
528 pktout = sftp_pkt_init(SSH_FXP_OPEN);
529 sftp_pkt_adduint32(pktout, req->id);
530 sftp_pkt_addstring(pktout, path);
531 sftp_pkt_adduint32(pktout, type);
532 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
533 sftp_send(pktout);
534
535 return req;
536 }
537
538 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin)
539 {
540 if (pktin->type == SSH_FXP_HANDLE) {
541 char *hstring;
542 struct fxp_handle *handle;
543 int len;
544
545 sftp_pkt_getstring(pktin, &hstring, &len);
546 if (!hstring) {
547 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
548 sftp_pkt_free(pktin);
549 return NULL;
550 }
551 handle = snew(struct fxp_handle);
552 handle->hstring = mkstr(hstring, len);
553 handle->hlen = len;
554 sftp_pkt_free(pktin);
555 return handle;
556 } else {
557 fxp_got_status(pktin);
558 sftp_pkt_free(pktin);
559 return NULL;
560 }
561 }
562
563 /*
564 * Open a directory.
565 */
566 struct sftp_request *fxp_opendir_send(char *path)
567 {
568 struct sftp_request *req = sftp_alloc_request();
569 struct sftp_packet *pktout;
570
571 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
572 sftp_pkt_adduint32(pktout, req->id);
573 sftp_pkt_addstring(pktout, path);
574 sftp_send(pktout);
575
576 return req;
577 }
578
579 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin)
580 {
581 if (pktin->type == SSH_FXP_HANDLE) {
582 char *hstring;
583 struct fxp_handle *handle;
584 int len;
585
586 sftp_pkt_getstring(pktin, &hstring, &len);
587 if (!hstring) {
588 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
589 sftp_pkt_free(pktin);
590 return NULL;
591 }
592 handle = snew(struct fxp_handle);
593 handle->hstring = mkstr(hstring, len);
594 handle->hlen = len;
595 sftp_pkt_free(pktin);
596 return handle;
597 } else {
598 fxp_got_status(pktin);
599 sftp_pkt_free(pktin);
600 return NULL;
601 }
602 }
603
604 /*
605 * Close a file/dir.
606 */
607 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
608 {
609 struct sftp_request *req = sftp_alloc_request();
610 struct sftp_packet *pktout;
611
612 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
613 sftp_pkt_adduint32(pktout, req->id);
614 sftp_pkt_addstring_start(pktout);
615 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
616 sftp_send(pktout);
617
618 sfree(handle->hstring);
619 sfree(handle);
620
621 return req;
622 }
623
624 void fxp_close_recv(struct sftp_packet *pktin)
625 {
626 fxp_got_status(pktin);
627 sftp_pkt_free(pktin);
628 }
629
630 struct sftp_request *fxp_mkdir_send(char *path)
631 {
632 struct sftp_request *req = sftp_alloc_request();
633 struct sftp_packet *pktout;
634
635 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
636 sftp_pkt_adduint32(pktout, req->id);
637 sftp_pkt_addstring(pktout, path);
638 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
639 sftp_send(pktout);
640
641 return req;
642 }
643
644 int fxp_mkdir_recv(struct sftp_packet *pktin)
645 {
646 int id = fxp_got_status(pktin);
647 sftp_pkt_free(pktin);
648 if (id != 1) {
649 return 0;
650 }
651 return 1;
652 }
653
654 struct sftp_request *fxp_rmdir_send(char *path)
655 {
656 struct sftp_request *req = sftp_alloc_request();
657 struct sftp_packet *pktout;
658
659 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
660 sftp_pkt_adduint32(pktout, req->id);
661 sftp_pkt_addstring(pktout, path);
662 sftp_send(pktout);
663
664 return req;
665 }
666
667 int fxp_rmdir_recv(struct sftp_packet *pktin)
668 {
669 int id = fxp_got_status(pktin);
670 sftp_pkt_free(pktin);
671 if (id != 1) {
672 return 0;
673 }
674 return 1;
675 }
676
677 struct sftp_request *fxp_remove_send(char *fname)
678 {
679 struct sftp_request *req = sftp_alloc_request();
680 struct sftp_packet *pktout;
681
682 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
683 sftp_pkt_adduint32(pktout, req->id);
684 sftp_pkt_addstring(pktout, fname);
685 sftp_send(pktout);
686
687 return req;
688 }
689
690 int fxp_remove_recv(struct sftp_packet *pktin)
691 {
692 int id = fxp_got_status(pktin);
693 sftp_pkt_free(pktin);
694 if (id != 1) {
695 return 0;
696 }
697 return 1;
698 }
699
700 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
701 {
702 struct sftp_request *req = sftp_alloc_request();
703 struct sftp_packet *pktout;
704
705 pktout = sftp_pkt_init(SSH_FXP_RENAME);
706 sftp_pkt_adduint32(pktout, req->id);
707 sftp_pkt_addstring(pktout, srcfname);
708 sftp_pkt_addstring(pktout, dstfname);
709 sftp_send(pktout);
710
711 return req;
712 }
713
714 int fxp_rename_recv(struct sftp_packet *pktin)
715 {
716 int id = fxp_got_status(pktin);
717 sftp_pkt_free(pktin);
718 if (id != 1) {
719 return 0;
720 }
721 return 1;
722 }
723
724 /*
725 * Retrieve the attributes of a file. We have fxp_stat which works
726 * on filenames, and fxp_fstat which works on open file handles.
727 */
728 struct sftp_request *fxp_stat_send(char *fname)
729 {
730 struct sftp_request *req = sftp_alloc_request();
731 struct sftp_packet *pktout;
732
733 pktout = sftp_pkt_init(SSH_FXP_STAT);
734 sftp_pkt_adduint32(pktout, req->id);
735 sftp_pkt_addstring(pktout, fname);
736 sftp_send(pktout);
737
738 return req;
739 }
740
741 int fxp_stat_recv(struct sftp_packet *pktin, struct fxp_attrs *attrs)
742 {
743 if (pktin->type == SSH_FXP_ATTRS) {
744 *attrs = sftp_pkt_getattrs(pktin);
745 sftp_pkt_free(pktin);
746 return 1;
747 } else {
748 fxp_got_status(pktin);
749 sftp_pkt_free(pktin);
750 return 0;
751 }
752 }
753
754 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
755 {
756 struct sftp_request *req = sftp_alloc_request();
757 struct sftp_packet *pktout;
758
759 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
760 sftp_pkt_adduint32(pktout, req->id);
761 sftp_pkt_addstring_start(pktout);
762 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
763 sftp_send(pktout);
764
765 return req;
766 }
767
768 int fxp_fstat_recv(struct sftp_packet *pktin,
769 struct fxp_attrs *attrs)
770 {
771 if (pktin->type == SSH_FXP_ATTRS) {
772 *attrs = sftp_pkt_getattrs(pktin);
773 sftp_pkt_free(pktin);
774 return 1;
775 } else {
776 fxp_got_status(pktin);
777 sftp_pkt_free(pktin);
778 return 0;
779 }
780 }
781
782 /*
783 * Set the attributes of a file.
784 */
785 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
786 {
787 struct sftp_request *req = sftp_alloc_request();
788 struct sftp_packet *pktout;
789
790 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
791 sftp_pkt_adduint32(pktout, req->id);
792 sftp_pkt_addstring(pktout, fname);
793 sftp_pkt_addattrs(pktout, attrs);
794 sftp_send(pktout);
795
796 return req;
797 }
798
799 int fxp_setstat_recv(struct sftp_packet *pktin)
800 {
801 int id = fxp_got_status(pktin);
802 sftp_pkt_free(pktin);
803 if (id != 1) {
804 return 0;
805 }
806 return 1;
807 }
808
809 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
810 struct fxp_attrs attrs)
811 {
812 struct sftp_request *req = sftp_alloc_request();
813 struct sftp_packet *pktout;
814
815 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
816 sftp_pkt_adduint32(pktout, req->id);
817 sftp_pkt_addstring_start(pktout);
818 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
819 sftp_pkt_addattrs(pktout, attrs);
820 sftp_send(pktout);
821
822 return req;
823 }
824
825 int fxp_fsetstat_recv(struct sftp_packet *pktin)
826 {
827 int id = fxp_got_status(pktin);
828 sftp_pkt_free(pktin);
829 if (id != 1) {
830 return 0;
831 }
832 return 1;
833 }
834
835 /*
836 * Read from a file. Returns the number of bytes read, or -1 on an
837 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
838 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
839 * error indicator. It might even depend on the SFTP server.)
840 */
841 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
842 uint64 offset, int len)
843 {
844 struct sftp_request *req = sftp_alloc_request();
845 struct sftp_packet *pktout;
846
847 pktout = sftp_pkt_init(SSH_FXP_READ);
848 sftp_pkt_adduint32(pktout, req->id);
849 sftp_pkt_addstring_start(pktout);
850 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
851 sftp_pkt_adduint64(pktout, offset);
852 sftp_pkt_adduint32(pktout, len);
853 sftp_send(pktout);
854
855 return req;
856 }
857
858 int fxp_read_recv(struct sftp_packet *pktin, char *buffer, int len)
859 {
860 if (pktin->type == SSH_FXP_DATA) {
861 char *str;
862 int rlen;
863
864 sftp_pkt_getstring(pktin, &str, &rlen);
865
866 if (rlen > len || rlen < 0) {
867 fxp_internal_error("READ returned more bytes than requested");
868 sftp_pkt_free(pktin);
869 return -1;
870 }
871
872 memcpy(buffer, str, rlen);
873 sftp_pkt_free(pktin);
874 return rlen;
875 } else {
876 fxp_got_status(pktin);
877 sftp_pkt_free(pktin);
878 return -1;
879 }
880 }
881
882 /*
883 * Read from a directory.
884 */
885 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
886 {
887 struct sftp_request *req = sftp_alloc_request();
888 struct sftp_packet *pktout;
889
890 pktout = sftp_pkt_init(SSH_FXP_READDIR);
891 sftp_pkt_adduint32(pktout, req->id);
892 sftp_pkt_addstring_start(pktout);
893 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
894 sftp_send(pktout);
895
896 return req;
897 }
898
899 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin)
900 {
901 if (pktin->type == SSH_FXP_NAME) {
902 struct fxp_names *ret;
903 int i;
904 ret = snew(struct fxp_names);
905 ret->nnames = sftp_pkt_getuint32(pktin);
906 ret->names = snewn(ret->nnames, struct fxp_name);
907 for (i = 0; i < ret->nnames; i++) {
908 char *str;
909 int len;
910 sftp_pkt_getstring(pktin, &str, &len);
911 ret->names[i].filename = mkstr(str, len);
912 sftp_pkt_getstring(pktin, &str, &len);
913 ret->names[i].longname = mkstr(str, len);
914 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
915 }
916 sftp_pkt_free(pktin);
917 return ret;
918 } else {
919 fxp_got_status(pktin);
920 sftp_pkt_free(pktin);
921 return NULL;
922 }
923 }
924
925 /*
926 * Write to a file. Returns 0 on error, 1 on OK.
927 */
928 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
929 char *buffer, uint64 offset, int len)
930 {
931 struct sftp_request *req = sftp_alloc_request();
932 struct sftp_packet *pktout;
933
934 pktout = sftp_pkt_init(SSH_FXP_WRITE);
935 sftp_pkt_adduint32(pktout, req->id);
936 sftp_pkt_addstring_start(pktout);
937 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
938 sftp_pkt_adduint64(pktout, offset);
939 sftp_pkt_addstring_start(pktout);
940 sftp_pkt_addstring_data(pktout, buffer, len);
941 sftp_send(pktout);
942
943 return req;
944 }
945
946 int fxp_write_recv(struct sftp_packet *pktin)
947 {
948 fxp_got_status(pktin);
949 sftp_pkt_free(pktin);
950 return fxp_errtype == SSH_FX_OK;
951 }
952
953 /*
954 * Free up an fxp_names structure.
955 */
956 void fxp_free_names(struct fxp_names *names)
957 {
958 int i;
959
960 for (i = 0; i < names->nnames; i++) {
961 sfree(names->names[i].filename);
962 sfree(names->names[i].longname);
963 }
964 sfree(names->names);
965 sfree(names);
966 }
967
968 /*
969 * Duplicate an fxp_name structure.
970 */
971 struct fxp_name *fxp_dup_name(struct fxp_name *name)
972 {
973 struct fxp_name *ret;
974 ret = snew(struct fxp_name);
975 ret->filename = dupstr(name->filename);
976 ret->longname = dupstr(name->longname);
977 ret->attrs = name->attrs; /* structure copy */
978 return ret;
979 }
980
981 /*
982 * Free up an fxp_name structure.
983 */
984 void fxp_free_name(struct fxp_name *name)
985 {
986 sfree(name->filename);
987 sfree(name->longname);
988 sfree(name);
989 }