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