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