Propagate file permissions in both directions in Unix pscp and psftp.
[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 #include <limits.h>
10
11 #include "misc.h"
12 #include "int64.h"
13 #include "tree234.h"
14 #include "sftp.h"
15
16 struct sftp_packet {
17 char *data;
18 unsigned length, maxlen;
19 unsigned savedpos;
20 int type;
21 };
22
23 static const char *fxp_error_message;
24 static int fxp_errtype;
25
26 static void fxp_internal_error(char *msg);
27
28 /* ----------------------------------------------------------------------
29 * SFTP packet construction functions.
30 */
31 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
32 {
33 if ((int)pkt->maxlen < length) {
34 pkt->maxlen = length + 256;
35 pkt->data = sresize(pkt->data, pkt->maxlen, char);
36 }
37 }
38 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
39 {
40 pkt->length += len;
41 sftp_pkt_ensure(pkt, pkt->length);
42 memcpy(pkt->data + pkt->length - len, data, len);
43 }
44 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
45 {
46 sftp_pkt_adddata(pkt, &byte, 1);
47 }
48 static struct sftp_packet *sftp_pkt_init(int pkt_type)
49 {
50 struct sftp_packet *pkt;
51 pkt = snew(struct sftp_packet);
52 pkt->data = NULL;
53 pkt->savedpos = -1;
54 pkt->length = 0;
55 pkt->maxlen = 0;
56 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
57 return pkt;
58 }
59 /*
60 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
61 {
62 sftp_pkt_adddata(pkt, &value, 1);
63 }
64 */
65 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
66 unsigned long value)
67 {
68 unsigned char x[4];
69 PUT_32BIT(x, value);
70 sftp_pkt_adddata(pkt, x, 4);
71 }
72 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
73 {
74 unsigned char x[8];
75 PUT_32BIT(x, value.hi);
76 PUT_32BIT(x + 4, value.lo);
77 sftp_pkt_adddata(pkt, x, 8);
78 }
79 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
80 {
81 sftp_pkt_adduint32(pkt, 0);
82 pkt->savedpos = pkt->length;
83 }
84 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
85 {
86 sftp_pkt_adddata(pkt, data, strlen(data));
87 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
88 }
89 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
90 char *data, int len)
91 {
92 sftp_pkt_adddata(pkt, data, len);
93 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
94 }
95 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
96 {
97 sftp_pkt_addstring_start(pkt);
98 sftp_pkt_addstring_str(pkt, data);
99 }
100 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
101 {
102 sftp_pkt_adduint32(pkt, attrs.flags);
103 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
104 sftp_pkt_adduint32(pkt, attrs.size.hi);
105 sftp_pkt_adduint32(pkt, attrs.size.lo);
106 }
107 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
108 sftp_pkt_adduint32(pkt, attrs.uid);
109 sftp_pkt_adduint32(pkt, attrs.gid);
110 }
111 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
112 sftp_pkt_adduint32(pkt, attrs.permissions);
113 }
114 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
115 sftp_pkt_adduint32(pkt, attrs.atime);
116 sftp_pkt_adduint32(pkt, attrs.mtime);
117 }
118 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
119 /*
120 * We currently don't support sending any extended
121 * attributes.
122 */
123 }
124 }
125
126 /* ----------------------------------------------------------------------
127 * SFTP packet decode functions.
128 */
129
130 static int sftp_pkt_getbyte(struct sftp_packet *pkt, unsigned char *ret)
131 {
132 if (pkt->length - pkt->savedpos < 1)
133 return 0;
134 *ret = (unsigned char) pkt->data[pkt->savedpos];
135 pkt->savedpos++;
136 return 1;
137 }
138 static int sftp_pkt_getuint32(struct sftp_packet *pkt, unsigned long *ret)
139 {
140 if (pkt->length - pkt->savedpos < 4)
141 return 0;
142 *ret = GET_32BIT(pkt->data + pkt->savedpos);
143 pkt->savedpos += 4;
144 return 1;
145 }
146 static int sftp_pkt_getstring(struct sftp_packet *pkt,
147 char **p, int *length)
148 {
149 *p = NULL;
150 if (pkt->length - pkt->savedpos < 4)
151 return 0;
152 *length = GET_32BIT(pkt->data + pkt->savedpos);
153 pkt->savedpos += 4;
154 if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) {
155 *length = 0;
156 return 0;
157 }
158 *p = pkt->data + pkt->savedpos;
159 pkt->savedpos += *length;
160 return 1;
161 }
162 static int sftp_pkt_getattrs(struct sftp_packet *pkt, struct fxp_attrs *ret)
163 {
164 if (!sftp_pkt_getuint32(pkt, &ret->flags))
165 return 0;
166 if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {
167 unsigned long hi, lo;
168 if (!sftp_pkt_getuint32(pkt, &hi) ||
169 !sftp_pkt_getuint32(pkt, &lo))
170 return 0;
171 ret->size = uint64_make(hi, lo);
172 }
173 if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {
174 if (!sftp_pkt_getuint32(pkt, &ret->uid) ||
175 !sftp_pkt_getuint32(pkt, &ret->gid))
176 return 0;
177 }
178 if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
179 if (!sftp_pkt_getuint32(pkt, &ret->permissions))
180 return 0;
181 }
182 if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
183 if (!sftp_pkt_getuint32(pkt, &ret->atime) ||
184 !sftp_pkt_getuint32(pkt, &ret->mtime))
185 return 0;
186 }
187 if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {
188 unsigned long count;
189 if (!sftp_pkt_getuint32(pkt, &count))
190 return 0;
191 while (count--) {
192 char *str;
193 int len;
194 /*
195 * We should try to analyse these, if we ever find one
196 * we recognise.
197 */
198 if (!sftp_pkt_getstring(pkt, &str, &len) ||
199 !sftp_pkt_getstring(pkt, &str, &len))
200 return 0;
201 }
202 }
203 return 1;
204 }
205 static void sftp_pkt_free(struct sftp_packet *pkt)
206 {
207 if (pkt->data)
208 sfree(pkt->data);
209 sfree(pkt);
210 }
211
212 /* ----------------------------------------------------------------------
213 * Send and receive packet functions.
214 */
215 int sftp_send(struct sftp_packet *pkt)
216 {
217 int ret;
218 char x[4];
219 PUT_32BIT(x, pkt->length);
220 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
221 sftp_pkt_free(pkt);
222 return ret;
223 }
224 struct sftp_packet *sftp_recv(void)
225 {
226 struct sftp_packet *pkt;
227 char x[4];
228 unsigned char uc;
229
230 if (!sftp_recvdata(x, 4))
231 return NULL;
232
233 pkt = snew(struct sftp_packet);
234 pkt->savedpos = 0;
235 pkt->length = pkt->maxlen = GET_32BIT(x);
236 pkt->data = snewn(pkt->length, char);
237
238 if (!sftp_recvdata(pkt->data, pkt->length)) {
239 sftp_pkt_free(pkt);
240 return NULL;
241 }
242
243 if (!sftp_pkt_getbyte(pkt, &uc)) {
244 sftp_pkt_free(pkt);
245 return NULL;
246 } else {
247 pkt->type = uc;
248 }
249
250 return pkt;
251 }
252
253 /* ----------------------------------------------------------------------
254 * Request ID allocation and temporary dispatch routines.
255 */
256
257 #define REQUEST_ID_OFFSET 256
258
259 struct sftp_request {
260 unsigned id;
261 int registered;
262 void *userdata;
263 };
264
265 static int sftp_reqcmp(void *av, void *bv)
266 {
267 struct sftp_request *a = (struct sftp_request *)av;
268 struct sftp_request *b = (struct sftp_request *)bv;
269 if (a->id < b->id)
270 return -1;
271 if (a->id > b->id)
272 return +1;
273 return 0;
274 }
275 static int sftp_reqfind(void *av, void *bv)
276 {
277 unsigned *a = (unsigned *) av;
278 struct sftp_request *b = (struct sftp_request *)bv;
279 if (*a < b->id)
280 return -1;
281 if (*a > b->id)
282 return +1;
283 return 0;
284 }
285
286 static tree234 *sftp_requests;
287
288 static struct sftp_request *sftp_alloc_request(void)
289 {
290 unsigned low, high, mid;
291 int tsize;
292 struct sftp_request *r;
293
294 if (sftp_requests == NULL)
295 sftp_requests = newtree234(sftp_reqcmp);
296
297 /*
298 * First-fit allocation of request IDs: always pick the lowest
299 * unused one. To do this, binary-search using the counted
300 * B-tree to find the largest ID which is in a contiguous
301 * sequence from the beginning. (Precisely everything in that
302 * sequence must have ID equal to its tree index plus
303 * REQUEST_ID_OFFSET.)
304 */
305 tsize = count234(sftp_requests);
306
307 low = -1;
308 high = tsize;
309 while (high - low > 1) {
310 mid = (high + low) / 2;
311 r = index234(sftp_requests, mid);
312 if (r->id == mid + REQUEST_ID_OFFSET)
313 low = mid; /* this one is fine */
314 else
315 high = mid; /* this one is past it */
316 }
317 /*
318 * Now low points to either -1, or the tree index of the
319 * largest ID in the initial sequence.
320 */
321 {
322 unsigned i = low + 1 + REQUEST_ID_OFFSET;
323 assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
324 }
325
326 /*
327 * So the request ID we need to create is
328 * low + 1 + REQUEST_ID_OFFSET.
329 */
330 r = snew(struct sftp_request);
331 r->id = low + 1 + REQUEST_ID_OFFSET;
332 r->registered = 0;
333 r->userdata = NULL;
334 add234(sftp_requests, r);
335 return r;
336 }
337
338 void sftp_cleanup_request(void)
339 {
340 if (sftp_requests != NULL) {
341 freetree234(sftp_requests);
342 sftp_requests = NULL;
343 }
344 }
345
346 void sftp_register(struct sftp_request *req)
347 {
348 req->registered = 1;
349 }
350
351 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
352 {
353 unsigned long id;
354 struct sftp_request *req;
355
356 if (!pktin) {
357 fxp_internal_error("did not receive a valid SFTP packet\n");
358 return NULL;
359 }
360
361 if (!sftp_pkt_getuint32(pktin, &id)) {
362 fxp_internal_error("did not receive a valid SFTP packet\n");
363 return NULL;
364 }
365 req = find234(sftp_requests, &id, sftp_reqfind);
366
367 if (!req || !req->registered) {
368 fxp_internal_error("request ID mismatch\n");
369 sftp_pkt_free(pktin);
370 return NULL;
371 }
372
373 del234(sftp_requests, req);
374
375 return req;
376 }
377
378 /* ----------------------------------------------------------------------
379 * String handling routines.
380 */
381
382 static char *mkstr(char *s, int len)
383 {
384 char *p = snewn(len + 1, char);
385 memcpy(p, s, len);
386 p[len] = '\0';
387 return p;
388 }
389
390 /* ----------------------------------------------------------------------
391 * SFTP primitives.
392 */
393
394 /*
395 * Deal with (and free) an FXP_STATUS packet. Return 1 if
396 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
397 * Also place the status into fxp_errtype.
398 */
399 static int fxp_got_status(struct sftp_packet *pktin)
400 {
401 static const char *const messages[] = {
402 /* SSH_FX_OK. The only time we will display a _message_ for this
403 * is if we were expecting something other than FXP_STATUS on
404 * success, so this is actually an error message! */
405 "unexpected OK response",
406 "end of file",
407 "no such file or directory",
408 "permission denied",
409 "failure",
410 "bad message",
411 "no connection",
412 "connection lost",
413 "operation unsupported",
414 };
415
416 if (pktin->type != SSH_FXP_STATUS) {
417 fxp_error_message = "expected FXP_STATUS packet";
418 fxp_errtype = -1;
419 } else {
420 unsigned long ul;
421 if (!sftp_pkt_getuint32(pktin, &ul)) {
422 fxp_error_message = "malformed FXP_STATUS packet";
423 fxp_errtype = -1;
424 } else {
425 fxp_errtype = ul;
426 if (fxp_errtype < 0 ||
427 fxp_errtype >= sizeof(messages) / sizeof(*messages))
428 fxp_error_message = "unknown error code";
429 else
430 fxp_error_message = messages[fxp_errtype];
431 }
432 }
433
434 if (fxp_errtype == SSH_FX_OK)
435 return 1;
436 else if (fxp_errtype == SSH_FX_EOF)
437 return 0;
438 else
439 return -1;
440 }
441
442 static void fxp_internal_error(char *msg)
443 {
444 fxp_error_message = msg;
445 fxp_errtype = -1;
446 }
447
448 const char *fxp_error(void)
449 {
450 return fxp_error_message;
451 }
452
453 int fxp_error_type(void)
454 {
455 return fxp_errtype;
456 }
457
458 /*
459 * Perform exchange of init/version packets. Return 0 on failure.
460 */
461 int fxp_init(void)
462 {
463 struct sftp_packet *pktout, *pktin;
464 unsigned long remotever;
465
466 pktout = sftp_pkt_init(SSH_FXP_INIT);
467 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
468 sftp_send(pktout);
469
470 pktin = sftp_recv();
471 if (!pktin) {
472 fxp_internal_error("could not connect");
473 return 0;
474 }
475 if (pktin->type != SSH_FXP_VERSION) {
476 fxp_internal_error("did not receive FXP_VERSION");
477 sftp_pkt_free(pktin);
478 return 0;
479 }
480 if (!sftp_pkt_getuint32(pktin, &remotever)) {
481 fxp_internal_error("malformed FXP_VERSION packet");
482 sftp_pkt_free(pktin);
483 return 0;
484 }
485 if (remotever > SFTP_PROTO_VERSION) {
486 fxp_internal_error
487 ("remote protocol is more advanced than we support");
488 sftp_pkt_free(pktin);
489 return 0;
490 }
491 /*
492 * In principle, this packet might also contain extension-
493 * string pairs. We should work through them and look for any
494 * we recognise. In practice we don't currently do so because
495 * we know we don't recognise _any_.
496 */
497 sftp_pkt_free(pktin);
498
499 return 1;
500 }
501
502 /*
503 * Canonify a pathname.
504 */
505 struct sftp_request *fxp_realpath_send(char *path)
506 {
507 struct sftp_request *req = sftp_alloc_request();
508 struct sftp_packet *pktout;
509
510 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
511 sftp_pkt_adduint32(pktout, req->id);
512 sftp_pkt_addstring_start(pktout);
513 sftp_pkt_addstring_str(pktout, path);
514 sftp_send(pktout);
515
516 return req;
517 }
518
519 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
520 {
521 sfree(req);
522
523 if (pktin->type == SSH_FXP_NAME) {
524 unsigned long count;
525 char *path;
526 int len;
527
528 if (!sftp_pkt_getuint32(pktin, &count) || count != 1) {
529 fxp_internal_error("REALPATH did not return name count of 1\n");
530 sftp_pkt_free(pktin);
531 return NULL;
532 }
533 if (!sftp_pkt_getstring(pktin, &path, &len)) {
534 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
535 sftp_pkt_free(pktin);
536 return NULL;
537 }
538 path = mkstr(path, len);
539 sftp_pkt_free(pktin);
540 return path;
541 } else {
542 fxp_got_status(pktin);
543 sftp_pkt_free(pktin);
544 return NULL;
545 }
546 }
547
548 /*
549 * Open a file.
550 */
551 struct sftp_request *fxp_open_send(char *path, int type,
552 struct fxp_attrs *attrs)
553 {
554 struct sftp_request *req = sftp_alloc_request();
555 struct sftp_packet *pktout;
556
557 pktout = sftp_pkt_init(SSH_FXP_OPEN);
558 sftp_pkt_adduint32(pktout, req->id);
559 sftp_pkt_addstring(pktout, path);
560 sftp_pkt_adduint32(pktout, type);
561 if (attrs)
562 sftp_pkt_addattrs(pktout, *attrs);
563 else
564 sftp_pkt_adduint32(pktout, 0); /* empty ATTRS structure */
565 sftp_send(pktout);
566
567 return req;
568 }
569
570 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
571 struct sftp_request *req)
572 {
573 sfree(req);
574
575 if (pktin->type == SSH_FXP_HANDLE) {
576 char *hstring;
577 struct fxp_handle *handle;
578 int len;
579
580 if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
581 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
582 sftp_pkt_free(pktin);
583 return NULL;
584 }
585 handle = snew(struct fxp_handle);
586 handle->hstring = mkstr(hstring, len);
587 handle->hlen = len;
588 sftp_pkt_free(pktin);
589 return handle;
590 } else {
591 fxp_got_status(pktin);
592 sftp_pkt_free(pktin);
593 return NULL;
594 }
595 }
596
597 /*
598 * Open a directory.
599 */
600 struct sftp_request *fxp_opendir_send(char *path)
601 {
602 struct sftp_request *req = sftp_alloc_request();
603 struct sftp_packet *pktout;
604
605 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
606 sftp_pkt_adduint32(pktout, req->id);
607 sftp_pkt_addstring(pktout, path);
608 sftp_send(pktout);
609
610 return req;
611 }
612
613 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
614 struct sftp_request *req)
615 {
616 sfree(req);
617 if (pktin->type == SSH_FXP_HANDLE) {
618 char *hstring;
619 struct fxp_handle *handle;
620 int len;
621
622 if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
623 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
624 sftp_pkt_free(pktin);
625 return NULL;
626 }
627 handle = snew(struct fxp_handle);
628 handle->hstring = mkstr(hstring, len);
629 handle->hlen = len;
630 sftp_pkt_free(pktin);
631 return handle;
632 } else {
633 fxp_got_status(pktin);
634 sftp_pkt_free(pktin);
635 return NULL;
636 }
637 }
638
639 /*
640 * Close a file/dir.
641 */
642 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
643 {
644 struct sftp_request *req = sftp_alloc_request();
645 struct sftp_packet *pktout;
646
647 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
648 sftp_pkt_adduint32(pktout, req->id);
649 sftp_pkt_addstring_start(pktout);
650 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
651 sftp_send(pktout);
652
653 sfree(handle->hstring);
654 sfree(handle);
655
656 return req;
657 }
658
659 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
660 {
661 sfree(req);
662 fxp_got_status(pktin);
663 sftp_pkt_free(pktin);
664 }
665
666 struct sftp_request *fxp_mkdir_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_MKDIR);
672 sftp_pkt_adduint32(pktout, req->id);
673 sftp_pkt_addstring(pktout, path);
674 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
675 sftp_send(pktout);
676
677 return req;
678 }
679
680 int fxp_mkdir_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_rmdir_send(char *path)
693 {
694 struct sftp_request *req = sftp_alloc_request();
695 struct sftp_packet *pktout;
696
697 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
698 sftp_pkt_adduint32(pktout, req->id);
699 sftp_pkt_addstring(pktout, path);
700 sftp_send(pktout);
701
702 return req;
703 }
704
705 int fxp_rmdir_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_remove_send(char *fname)
718 {
719 struct sftp_request *req = sftp_alloc_request();
720 struct sftp_packet *pktout;
721
722 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
723 sftp_pkt_adduint32(pktout, req->id);
724 sftp_pkt_addstring(pktout, fname);
725 sftp_send(pktout);
726
727 return req;
728 }
729
730 int fxp_remove_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 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
743 {
744 struct sftp_request *req = sftp_alloc_request();
745 struct sftp_packet *pktout;
746
747 pktout = sftp_pkt_init(SSH_FXP_RENAME);
748 sftp_pkt_adduint32(pktout, req->id);
749 sftp_pkt_addstring(pktout, srcfname);
750 sftp_pkt_addstring(pktout, dstfname);
751 sftp_send(pktout);
752
753 return req;
754 }
755
756 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
757 {
758 int id;
759 sfree(req);
760 id = fxp_got_status(pktin);
761 sftp_pkt_free(pktin);
762 if (id != 1) {
763 return 0;
764 }
765 return 1;
766 }
767
768 /*
769 * Retrieve the attributes of a file. We have fxp_stat which works
770 * on filenames, and fxp_fstat which works on open file handles.
771 */
772 struct sftp_request *fxp_stat_send(char *fname)
773 {
774 struct sftp_request *req = sftp_alloc_request();
775 struct sftp_packet *pktout;
776
777 pktout = sftp_pkt_init(SSH_FXP_STAT);
778 sftp_pkt_adduint32(pktout, req->id);
779 sftp_pkt_addstring(pktout, fname);
780 sftp_send(pktout);
781
782 return req;
783 }
784
785 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
786 struct fxp_attrs *attrs)
787 {
788 sfree(req);
789 if (pktin->type == SSH_FXP_ATTRS) {
790 if (!sftp_pkt_getattrs(pktin, attrs)) {
791 fxp_internal_error("malformed SSH_FXP_ATTRS packet");
792 sftp_pkt_free(pktin);
793 return 0;
794 }
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 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
805 {
806 struct sftp_request *req = sftp_alloc_request();
807 struct sftp_packet *pktout;
808
809 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
810 sftp_pkt_adduint32(pktout, req->id);
811 sftp_pkt_addstring_start(pktout);
812 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
813 sftp_send(pktout);
814
815 return req;
816 }
817
818 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
819 struct fxp_attrs *attrs)
820 {
821 sfree(req);
822 if (pktin->type == SSH_FXP_ATTRS) {
823 if (!sftp_pkt_getattrs(pktin, attrs)) {
824 fxp_internal_error("malformed SSH_FXP_ATTRS packet");
825 sftp_pkt_free(pktin);
826 return 0;
827 }
828 sftp_pkt_free(pktin);
829 return 1;
830 } else {
831 fxp_got_status(pktin);
832 sftp_pkt_free(pktin);
833 return 0;
834 }
835 }
836
837 /*
838 * Set the attributes of a file.
839 */
840 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
841 {
842 struct sftp_request *req = sftp_alloc_request();
843 struct sftp_packet *pktout;
844
845 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
846 sftp_pkt_adduint32(pktout, req->id);
847 sftp_pkt_addstring(pktout, fname);
848 sftp_pkt_addattrs(pktout, attrs);
849 sftp_send(pktout);
850
851 return req;
852 }
853
854 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
855 {
856 int id;
857 sfree(req);
858 id = fxp_got_status(pktin);
859 sftp_pkt_free(pktin);
860 if (id != 1) {
861 return 0;
862 }
863 return 1;
864 }
865
866 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
867 struct fxp_attrs attrs)
868 {
869 struct sftp_request *req = sftp_alloc_request();
870 struct sftp_packet *pktout;
871
872 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
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_addattrs(pktout, attrs);
877 sftp_send(pktout);
878
879 return req;
880 }
881
882 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
883 {
884 int id;
885 sfree(req);
886 id = fxp_got_status(pktin);
887 sftp_pkt_free(pktin);
888 if (id != 1) {
889 return 0;
890 }
891 return 1;
892 }
893
894 /*
895 * Read from a file. Returns the number of bytes read, or -1 on an
896 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
897 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
898 * error indicator. It might even depend on the SFTP server.)
899 */
900 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
901 uint64 offset, int len)
902 {
903 struct sftp_request *req = sftp_alloc_request();
904 struct sftp_packet *pktout;
905
906 pktout = sftp_pkt_init(SSH_FXP_READ);
907 sftp_pkt_adduint32(pktout, req->id);
908 sftp_pkt_addstring_start(pktout);
909 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
910 sftp_pkt_adduint64(pktout, offset);
911 sftp_pkt_adduint32(pktout, len);
912 sftp_send(pktout);
913
914 return req;
915 }
916
917 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
918 char *buffer, int len)
919 {
920 sfree(req);
921 if (pktin->type == SSH_FXP_DATA) {
922 char *str;
923 int rlen;
924
925 if (!sftp_pkt_getstring(pktin, &str, &rlen)) {
926 fxp_internal_error("READ returned malformed SSH_FXP_DATA packet");
927 sftp_pkt_free(pktin);
928 return -1;
929 }
930
931 if (rlen > len || rlen < 0) {
932 fxp_internal_error("READ returned more bytes than requested");
933 sftp_pkt_free(pktin);
934 return -1;
935 }
936
937 memcpy(buffer, str, rlen);
938 sftp_pkt_free(pktin);
939 return rlen;
940 } else {
941 fxp_got_status(pktin);
942 sftp_pkt_free(pktin);
943 return -1;
944 }
945 }
946
947 /*
948 * Read from a directory.
949 */
950 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
951 {
952 struct sftp_request *req = sftp_alloc_request();
953 struct sftp_packet *pktout;
954
955 pktout = sftp_pkt_init(SSH_FXP_READDIR);
956 sftp_pkt_adduint32(pktout, req->id);
957 sftp_pkt_addstring_start(pktout);
958 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
959 sftp_send(pktout);
960
961 return req;
962 }
963
964 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
965 struct sftp_request *req)
966 {
967 sfree(req);
968 if (pktin->type == SSH_FXP_NAME) {
969 struct fxp_names *ret;
970 unsigned long i;
971
972 /*
973 * Sanity-check the number of names. Minimum is obviously
974 * zero. Maximum is the remaining space in the packet
975 * divided by the very minimum length of a name, which is
976 * 12 bytes (4 for an empty filename, 4 for an empty
977 * longname, 4 for a set of attribute flags indicating that
978 * no other attributes are supplied).
979 */
980 if (!sftp_pkt_getuint32(pktin, &i) ||
981 i > (pktin->length-pktin->savedpos)/12) {
982 fxp_internal_error("malformed FXP_NAME packet");
983 sftp_pkt_free(pktin);
984 return NULL;
985 }
986
987 /*
988 * Ensure the implicit multiplication in the snewn() call
989 * doesn't suffer integer overflow and cause us to malloc
990 * too little space.
991 */
992 if (i > INT_MAX / sizeof(struct fxp_name)) {
993 fxp_internal_error("unreasonably large FXP_NAME packet");
994 sftp_pkt_free(pktin);
995 return NULL;
996 }
997
998 ret = snew(struct fxp_names);
999 ret->nnames = i;
1000 ret->names = snewn(ret->nnames, struct fxp_name);
1001 for (i = 0; i < (unsigned long)ret->nnames; i++) {
1002 char *str1, *str2;
1003 int len1, len2;
1004 if (!sftp_pkt_getstring(pktin, &str1, &len1) ||
1005 !sftp_pkt_getstring(pktin, &str2, &len2) ||
1006 !sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {
1007 fxp_internal_error("malformed FXP_NAME packet");
1008 while (i--) {
1009 sfree(ret->names[i].filename);
1010 sfree(ret->names[i].longname);
1011 }
1012 sfree(ret->names);
1013 sfree(ret);
1014 sfree(pktin);
1015 return NULL;
1016 }
1017 ret->names[i].filename = mkstr(str1, len1);
1018 ret->names[i].longname = mkstr(str2, len2);
1019 }
1020 sftp_pkt_free(pktin);
1021 return ret;
1022 } else {
1023 fxp_got_status(pktin);
1024 sftp_pkt_free(pktin);
1025 return NULL;
1026 }
1027 }
1028
1029 /*
1030 * Write to a file. Returns 0 on error, 1 on OK.
1031 */
1032 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
1033 char *buffer, uint64 offset, int len)
1034 {
1035 struct sftp_request *req = sftp_alloc_request();
1036 struct sftp_packet *pktout;
1037
1038 pktout = sftp_pkt_init(SSH_FXP_WRITE);
1039 sftp_pkt_adduint32(pktout, req->id);
1040 sftp_pkt_addstring_start(pktout);
1041 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
1042 sftp_pkt_adduint64(pktout, offset);
1043 sftp_pkt_addstring_start(pktout);
1044 sftp_pkt_addstring_data(pktout, buffer, len);
1045 sftp_send(pktout);
1046
1047 return req;
1048 }
1049
1050 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
1051 {
1052 sfree(req);
1053 fxp_got_status(pktin);
1054 sftp_pkt_free(pktin);
1055 return fxp_errtype == SSH_FX_OK;
1056 }
1057
1058 /*
1059 * Free up an fxp_names structure.
1060 */
1061 void fxp_free_names(struct fxp_names *names)
1062 {
1063 int i;
1064
1065 for (i = 0; i < names->nnames; i++) {
1066 sfree(names->names[i].filename);
1067 sfree(names->names[i].longname);
1068 }
1069 sfree(names->names);
1070 sfree(names);
1071 }
1072
1073 /*
1074 * Duplicate an fxp_name structure.
1075 */
1076 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1077 {
1078 struct fxp_name *ret;
1079 ret = snew(struct fxp_name);
1080 ret->filename = dupstr(name->filename);
1081 ret->longname = dupstr(name->longname);
1082 ret->attrs = name->attrs; /* structure copy */
1083 return ret;
1084 }
1085
1086 /*
1087 * Free up an fxp_name structure.
1088 */
1089 void fxp_free_name(struct fxp_name *name)
1090 {
1091 sfree(name->filename);
1092 sfree(name->longname);
1093 sfree(name);
1094 }
1095
1096 /*
1097 * Store user data in an sftp_request structure.
1098 */
1099 void *fxp_get_userdata(struct sftp_request *req)
1100 {
1101 return req->userdata;
1102 }
1103
1104 void fxp_set_userdata(struct sftp_request *req, void *data)
1105 {
1106 req->userdata = data;
1107 }
1108
1109 /*
1110 * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1111 * the queueing of multiple read/write requests.
1112 */
1113
1114 struct req {
1115 char *buffer;
1116 int len, retlen, complete;
1117 uint64 offset;
1118 struct req *next, *prev;
1119 };
1120
1121 struct fxp_xfer {
1122 uint64 offset, furthestdata, filesize;
1123 int req_totalsize, req_maxsize, eof, err;
1124 struct fxp_handle *fh;
1125 struct req *head, *tail;
1126 };
1127
1128 static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1129 {
1130 struct fxp_xfer *xfer = snew(struct fxp_xfer);
1131
1132 xfer->fh = fh;
1133 xfer->offset = offset;
1134 xfer->head = xfer->tail = NULL;
1135 xfer->req_totalsize = 0;
1136 xfer->req_maxsize = 1048576;
1137 xfer->err = 0;
1138 xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1139 xfer->furthestdata = uint64_make(0, 0);
1140
1141 return xfer;
1142 }
1143
1144 int xfer_done(struct fxp_xfer *xfer)
1145 {
1146 /*
1147 * We're finished if we've seen EOF _and_ there are no
1148 * outstanding requests.
1149 */
1150 return (xfer->eof || xfer->err) && !xfer->head;
1151 }
1152
1153 void xfer_download_queue(struct fxp_xfer *xfer)
1154 {
1155 while (xfer->req_totalsize < xfer->req_maxsize &&
1156 !xfer->eof && !xfer->err) {
1157 /*
1158 * Queue a new read request.
1159 */
1160 struct req *rr;
1161 struct sftp_request *req;
1162
1163 rr = snew(struct req);
1164 rr->offset = xfer->offset;
1165 rr->complete = 0;
1166 if (xfer->tail) {
1167 xfer->tail->next = rr;
1168 rr->prev = xfer->tail;
1169 } else {
1170 xfer->head = rr;
1171 rr->prev = NULL;
1172 }
1173 xfer->tail = rr;
1174 rr->next = NULL;
1175
1176 rr->len = 32768;
1177 rr->buffer = snewn(rr->len, char);
1178 sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1179 fxp_set_userdata(req, rr);
1180
1181 xfer->offset = uint64_add32(xfer->offset, rr->len);
1182 xfer->req_totalsize += rr->len;
1183
1184 #ifdef DEBUG_DOWNLOAD
1185 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1186 #endif
1187 }
1188 }
1189
1190 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1191 {
1192 struct fxp_xfer *xfer = xfer_init(fh, offset);
1193
1194 xfer->eof = FALSE;
1195 xfer_download_queue(xfer);
1196
1197 return xfer;
1198 }
1199
1200 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1201 {
1202 struct sftp_request *rreq;
1203 struct req *rr;
1204
1205 rreq = sftp_find_request(pktin);
1206 rr = (struct req *)fxp_get_userdata(rreq);
1207 if (!rr)
1208 return 0; /* this packet isn't ours */
1209 rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1210 #ifdef DEBUG_DOWNLOAD
1211 printf("read request %p has returned [%d]\n", rr, rr->retlen);
1212 #endif
1213
1214 if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1215 xfer->eof = TRUE;
1216 rr->complete = -1;
1217 #ifdef DEBUG_DOWNLOAD
1218 printf("setting eof\n");
1219 #endif
1220 } else if (rr->retlen < 0) {
1221 /* some error other than EOF; signal it back to caller */
1222 xfer_set_error(xfer);
1223 rr->complete = -1;
1224 return -1;
1225 }
1226
1227 rr->complete = 1;
1228
1229 /*
1230 * Special case: if we have received fewer bytes than we
1231 * actually read, we should do something. For the moment I'll
1232 * just throw an ersatz FXP error to signal this; the SFTP
1233 * draft I've got says that it can't happen except on special
1234 * files, in which case seeking probably has very little
1235 * meaning and so queueing an additional read request to fill
1236 * up the gap sounds like the wrong answer. I'm not sure what I
1237 * should be doing here - if it _was_ a special file, I suspect
1238 * I simply shouldn't have been queueing multiple requests in
1239 * the first place...
1240 */
1241 if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1242 xfer->furthestdata = rr->offset;
1243 #ifdef DEBUG_DOWNLOAD
1244 { char buf[40];
1245 uint64_decimal(xfer->furthestdata, buf);
1246 printf("setting furthestdata = %s\n", buf); }
1247 #endif
1248 }
1249
1250 if (rr->retlen < rr->len) {
1251 uint64 filesize = uint64_add32(rr->offset,
1252 (rr->retlen < 0 ? 0 : rr->retlen));
1253 #ifdef DEBUG_DOWNLOAD
1254 { char buf[40];
1255 uint64_decimal(filesize, buf);
1256 printf("short block! trying filesize = %s\n", buf); }
1257 #endif
1258 if (uint64_compare(xfer->filesize, filesize) > 0) {
1259 xfer->filesize = filesize;
1260 #ifdef DEBUG_DOWNLOAD
1261 printf("actually changing filesize\n");
1262 #endif
1263 }
1264 }
1265
1266 if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1267 fxp_error_message = "received a short buffer from FXP_READ, but not"
1268 " at EOF";
1269 fxp_errtype = -1;
1270 xfer_set_error(xfer);
1271 return -1;
1272 }
1273
1274 return 1;
1275 }
1276
1277 void xfer_set_error(struct fxp_xfer *xfer)
1278 {
1279 xfer->err = 1;
1280 }
1281
1282 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1283 {
1284 void *retbuf = NULL;
1285 int retlen = 0;
1286
1287 /*
1288 * Discard anything at the head of the rr queue with complete <
1289 * 0; return the first thing with complete > 0.
1290 */
1291 while (xfer->head && xfer->head->complete && !retbuf) {
1292 struct req *rr = xfer->head;
1293
1294 if (rr->complete > 0) {
1295 retbuf = rr->buffer;
1296 retlen = rr->retlen;
1297 #ifdef DEBUG_DOWNLOAD
1298 printf("handing back data from read request %p\n", rr);
1299 #endif
1300 }
1301 #ifdef DEBUG_DOWNLOAD
1302 else
1303 printf("skipping failed read request %p\n", rr);
1304 #endif
1305
1306 xfer->head = xfer->head->next;
1307 if (xfer->head)
1308 xfer->head->prev = NULL;
1309 else
1310 xfer->tail = NULL;
1311 xfer->req_totalsize -= rr->len;
1312 sfree(rr);
1313 }
1314
1315 if (retbuf) {
1316 *buf = retbuf;
1317 *len = retlen;
1318 return 1;
1319 } else
1320 return 0;
1321 }
1322
1323 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1324 {
1325 struct fxp_xfer *xfer = xfer_init(fh, offset);
1326
1327 /*
1328 * We set `eof' to 1 because this will cause xfer_done() to
1329 * return true iff there are no outstanding requests. During an
1330 * upload, our caller will be responsible for working out
1331 * whether all the data has been sent, so all it needs to know
1332 * from us is whether the outstanding requests have been
1333 * handled once that's done.
1334 */
1335 xfer->eof = 1;
1336
1337 return xfer;
1338 }
1339
1340 int xfer_upload_ready(struct fxp_xfer *xfer)
1341 {
1342 if (xfer->req_totalsize < xfer->req_maxsize)
1343 return 1;
1344 else
1345 return 0;
1346 }
1347
1348 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1349 {
1350 struct req *rr;
1351 struct sftp_request *req;
1352
1353 rr = snew(struct req);
1354 rr->offset = xfer->offset;
1355 rr->complete = 0;
1356 if (xfer->tail) {
1357 xfer->tail->next = rr;
1358 rr->prev = xfer->tail;
1359 } else {
1360 xfer->head = rr;
1361 rr->prev = NULL;
1362 }
1363 xfer->tail = rr;
1364 rr->next = NULL;
1365
1366 rr->len = len;
1367 rr->buffer = NULL;
1368 sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1369 fxp_set_userdata(req, rr);
1370
1371 xfer->offset = uint64_add32(xfer->offset, rr->len);
1372 xfer->req_totalsize += rr->len;
1373
1374 #ifdef DEBUG_UPLOAD
1375 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1376 #endif
1377 }
1378
1379 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1380 {
1381 struct sftp_request *rreq;
1382 struct req *rr, *prev, *next;
1383 int ret;
1384
1385 rreq = sftp_find_request(pktin);
1386 rr = (struct req *)fxp_get_userdata(rreq);
1387 if (!rr)
1388 return 0; /* this packet isn't ours */
1389 ret = fxp_write_recv(pktin, rreq);
1390 #ifdef DEBUG_UPLOAD
1391 printf("write request %p has returned [%d]\n", rr, ret);
1392 #endif
1393
1394 /*
1395 * Remove this one from the queue.
1396 */
1397 prev = rr->prev;
1398 next = rr->next;
1399 if (prev)
1400 prev->next = next;
1401 else
1402 xfer->head = next;
1403 if (next)
1404 next->prev = prev;
1405 else
1406 xfer->tail = prev;
1407 xfer->req_totalsize -= rr->len;
1408 sfree(rr);
1409
1410 if (!ret)
1411 return -1;
1412
1413 return 1;
1414 }
1415
1416 void xfer_cleanup(struct fxp_xfer *xfer)
1417 {
1418 struct req *rr;
1419 while (xfer->head) {
1420 rr = xfer->head;
1421 xfer->head = xfer->head->next;
1422 sfree(rr->buffer);
1423 sfree(rr);
1424 }
1425 sfree(xfer);
1426 }