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