Andy Hood points out that `#ifdef MONITOR_DEFAULTTONEAREST' would
[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
337void sftp_register(struct sftp_request *req)
338{
339 req->registered = 1;
340}
341
342struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
343{
344 unsigned long id;
345 struct sftp_request *req;
346
347 if (!pktin) {
348 fxp_internal_error("did not receive a valid SFTP packet\n");
349 return NULL;
350 }
351
352 id = sftp_pkt_getuint32(pktin);
353 req = find234(sftp_requests, &id, sftp_reqfind);
354
355 if (!req || !req->registered) {
356 fxp_internal_error("request ID mismatch\n");
357 sftp_pkt_free(pktin);
358 return NULL;
359 }
360
7b7de4f4 361 del234(sftp_requests, req);
362
1bc24185 363 return req;
364}
365
366/* ----------------------------------------------------------------------
4c7f0d61 367 * String handling routines.
368 */
369
32874aea 370static char *mkstr(char *s, int len)
371{
3d88e64d 372 char *p = snewn(len + 1, char);
4c7f0d61 373 memcpy(p, s, len);
374 p[len] = '\0';
375 return p;
376}
377
378/* ----------------------------------------------------------------------
379 * SFTP primitives.
380 */
381
4c7f0d61 382/*
383 * Deal with (and free) an FXP_STATUS packet. Return 1 if
384 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
385 * Also place the status into fxp_errtype.
386 */
32874aea 387static int fxp_got_status(struct sftp_packet *pktin)
388{
4c7f0d61 389 static const char *const messages[] = {
390 /* SSH_FX_OK. The only time we will display a _message_ for this
391 * is if we were expecting something other than FXP_STATUS on
392 * success, so this is actually an error message! */
393 "unexpected OK response",
394 "end of file",
395 "no such file or directory",
396 "permission denied",
397 "failure",
398 "bad message",
399 "no connection",
400 "connection lost",
401 "operation unsupported",
402 };
403
404 if (pktin->type != SSH_FXP_STATUS) {
405 fxp_error_message = "expected FXP_STATUS packet";
406 fxp_errtype = -1;
407 } else {
408 fxp_errtype = sftp_pkt_getuint32(pktin);
409 if (fxp_errtype < 0 ||
32874aea 410 fxp_errtype >= sizeof(messages) / sizeof(*messages))
411 fxp_error_message = "unknown error code";
4c7f0d61 412 else
413 fxp_error_message = messages[fxp_errtype];
414 }
415
416 if (fxp_errtype == SSH_FX_OK)
417 return 1;
418 else if (fxp_errtype == SSH_FX_EOF)
419 return 0;
420 else
421 return -1;
422}
423
32874aea 424static void fxp_internal_error(char *msg)
425{
4c7f0d61 426 fxp_error_message = msg;
427 fxp_errtype = -1;
428}
429
32874aea 430const char *fxp_error(void)
431{
4c7f0d61 432 return fxp_error_message;
433}
434
32874aea 435int fxp_error_type(void)
436{
4c7f0d61 437 return fxp_errtype;
438}
439
440/*
441 * Perform exchange of init/version packets. Return 0 on failure.
442 */
32874aea 443int fxp_init(void)
444{
4c7f0d61 445 struct sftp_packet *pktout, *pktin;
446 int remotever;
447
448 pktout = sftp_pkt_init(SSH_FXP_INIT);
449 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
450 sftp_send(pktout);
451
452 pktin = sftp_recv();
0d694692 453 if (!pktin) {
454 fxp_internal_error("could not connect");
455 return 0;
456 }
4c7f0d61 457 if (pktin->type != SSH_FXP_VERSION) {
458 fxp_internal_error("did not receive FXP_VERSION");
41d3adbb 459 sftp_pkt_free(pktin);
4c7f0d61 460 return 0;
461 }
462 remotever = sftp_pkt_getuint32(pktin);
463 if (remotever > SFTP_PROTO_VERSION) {
32874aea 464 fxp_internal_error
465 ("remote protocol is more advanced than we support");
41d3adbb 466 sftp_pkt_free(pktin);
4c7f0d61 467 return 0;
468 }
469 /*
470 * In principle, this packet might also contain extension-
471 * string pairs. We should work through them and look for any
472 * we recognise. In practice we don't currently do so because
473 * we know we don't recognise _any_.
474 */
475 sftp_pkt_free(pktin);
476
477 return 1;
478}
479
480/*
f9e162aa 481 * Canonify a pathname.
4c7f0d61 482 */
1bc24185 483struct sftp_request *fxp_realpath_send(char *path)
32874aea 484{
1bc24185 485 struct sftp_request *req = sftp_alloc_request();
486 struct sftp_packet *pktout;
4c7f0d61 487
488 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
1bc24185 489 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 490 sftp_pkt_addstring_start(pktout);
491 sftp_pkt_addstring_str(pktout, path);
4c7f0d61 492 sftp_send(pktout);
1bc24185 493
494 return req;
495}
496
7b7de4f4 497char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 498{
7b7de4f4 499 sfree(req);
500
4c7f0d61 501 if (pktin->type == SSH_FXP_NAME) {
502 int count;
503 char *path;
504 int len;
505
506 count = sftp_pkt_getuint32(pktin);
507 if (count != 1) {
508 fxp_internal_error("REALPATH returned name count != 1\n");
41d3adbb 509 sftp_pkt_free(pktin);
4c7f0d61 510 return NULL;
511 }
512 sftp_pkt_getstring(pktin, &path, &len);
513 if (!path) {
514 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
41d3adbb 515 sftp_pkt_free(pktin);
4c7f0d61 516 return NULL;
517 }
518 path = mkstr(path, len);
519 sftp_pkt_free(pktin);
520 return path;
521 } else {
522 fxp_got_status(pktin);
41d3adbb 523 sftp_pkt_free(pktin);
4c7f0d61 524 return NULL;
525 }
526}
527
528/*
529 * Open a file.
530 */
1bc24185 531struct sftp_request *fxp_open_send(char *path, int type)
32874aea 532{
1bc24185 533 struct sftp_request *req = sftp_alloc_request();
534 struct sftp_packet *pktout;
4c7f0d61 535
536 pktout = sftp_pkt_init(SSH_FXP_OPEN);
1bc24185 537 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 538 sftp_pkt_addstring(pktout, path);
539 sftp_pkt_adduint32(pktout, type);
540 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
541 sftp_send(pktout);
1bc24185 542
543 return req;
544}
545
7b7de4f4 546struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
547 struct sftp_request *req)
1bc24185 548{
7b7de4f4 549 sfree(req);
550
4c7f0d61 551 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 552 char *hstring;
553 struct fxp_handle *handle;
554 int len;
555
556 sftp_pkt_getstring(pktin, &hstring, &len);
557 if (!hstring) {
558 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
41d3adbb 559 sftp_pkt_free(pktin);
4c7f0d61 560 return NULL;
561 }
3d88e64d 562 handle = snew(struct fxp_handle);
4c7f0d61 563 handle->hstring = mkstr(hstring, len);
f9e162aa 564 handle->hlen = len;
4c7f0d61 565 sftp_pkt_free(pktin);
566 return handle;
567 } else {
568 fxp_got_status(pktin);
41d3adbb 569 sftp_pkt_free(pktin);
4c7f0d61 570 return NULL;
571 }
572}
573
574/*
575 * Open a directory.
576 */
1bc24185 577struct sftp_request *fxp_opendir_send(char *path)
32874aea 578{
1bc24185 579 struct sftp_request *req = sftp_alloc_request();
580 struct sftp_packet *pktout;
4c7f0d61 581
582 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
1bc24185 583 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 584 sftp_pkt_addstring(pktout, path);
585 sftp_send(pktout);
1bc24185 586
587 return req;
588}
589
7b7de4f4 590struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
591 struct sftp_request *req)
1bc24185 592{
7b7de4f4 593 sfree(req);
4c7f0d61 594 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 595 char *hstring;
596 struct fxp_handle *handle;
597 int len;
598
599 sftp_pkt_getstring(pktin, &hstring, &len);
600 if (!hstring) {
601 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
41d3adbb 602 sftp_pkt_free(pktin);
4c7f0d61 603 return NULL;
604 }
3d88e64d 605 handle = snew(struct fxp_handle);
4c7f0d61 606 handle->hstring = mkstr(hstring, len);
f9e162aa 607 handle->hlen = len;
4c7f0d61 608 sftp_pkt_free(pktin);
609 return handle;
610 } else {
611 fxp_got_status(pktin);
41d3adbb 612 sftp_pkt_free(pktin);
4c7f0d61 613 return NULL;
614 }
615}
616
617/*
618 * Close a file/dir.
619 */
1bc24185 620struct sftp_request *fxp_close_send(struct fxp_handle *handle)
32874aea 621{
1bc24185 622 struct sftp_request *req = sftp_alloc_request();
623 struct sftp_packet *pktout;
4c7f0d61 624
625 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
1bc24185 626 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 627 sftp_pkt_addstring_start(pktout);
628 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 629 sftp_send(pktout);
1bc24185 630
4c7f0d61 631 sfree(handle->hstring);
632 sfree(handle);
1bc24185 633
634 return req;
635}
636
7b7de4f4 637void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 638{
7b7de4f4 639 sfree(req);
1bc24185 640 fxp_got_status(pktin);
641 sftp_pkt_free(pktin);
4c7f0d61 642}
643
1bc24185 644struct sftp_request *fxp_mkdir_send(char *path)
9954aaa3 645{
1bc24185 646 struct sftp_request *req = sftp_alloc_request();
647 struct sftp_packet *pktout;
9954aaa3 648
649 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
1bc24185 650 sftp_pkt_adduint32(pktout, req->id);
d92624dc 651 sftp_pkt_addstring(pktout, path);
9954aaa3 652 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
653 sftp_send(pktout);
1bc24185 654
655 return req;
656}
657
7b7de4f4 658int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 659{
7b7de4f4 660 int id;
661 sfree(req);
662 id = fxp_got_status(pktin);
41d3adbb 663 sftp_pkt_free(pktin);
9954aaa3 664 if (id != 1) {
665 return 0;
666 }
667 return 1;
668}
669
1bc24185 670struct sftp_request *fxp_rmdir_send(char *path)
9954aaa3 671{
1bc24185 672 struct sftp_request *req = sftp_alloc_request();
673 struct sftp_packet *pktout;
9954aaa3 674
675 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
1bc24185 676 sftp_pkt_adduint32(pktout, req->id);
d92624dc 677 sftp_pkt_addstring(pktout, path);
9954aaa3 678 sftp_send(pktout);
1bc24185 679
680 return req;
681}
682
7b7de4f4 683int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 684{
7b7de4f4 685 int id;
686 sfree(req);
687 id = fxp_got_status(pktin);
41d3adbb 688 sftp_pkt_free(pktin);
9954aaa3 689 if (id != 1) {
690 return 0;
691 }
692 return 1;
693}
694
1bc24185 695struct sftp_request *fxp_remove_send(char *fname)
9954aaa3 696{
1bc24185 697 struct sftp_request *req = sftp_alloc_request();
698 struct sftp_packet *pktout;
9954aaa3 699
700 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
1bc24185 701 sftp_pkt_adduint32(pktout, req->id);
d92624dc 702 sftp_pkt_addstring(pktout, fname);
703 sftp_send(pktout);
1bc24185 704
705 return req;
706}
707
7b7de4f4 708int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 709{
7b7de4f4 710 int id;
711 sfree(req);
712 id = fxp_got_status(pktin);
41d3adbb 713 sftp_pkt_free(pktin);
d92624dc 714 if (id != 1) {
715 return 0;
716 }
717 return 1;
718}
719
1bc24185 720struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
d92624dc 721{
1bc24185 722 struct sftp_request *req = sftp_alloc_request();
723 struct sftp_packet *pktout;
d92624dc 724
725 pktout = sftp_pkt_init(SSH_FXP_RENAME);
1bc24185 726 sftp_pkt_adduint32(pktout, req->id);
d92624dc 727 sftp_pkt_addstring(pktout, srcfname);
728 sftp_pkt_addstring(pktout, dstfname);
729 sftp_send(pktout);
1bc24185 730
731 return req;
732}
733
7b7de4f4 734int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 735{
7b7de4f4 736 int id;
737 sfree(req);
738 id = fxp_got_status(pktin);
41d3adbb 739 sftp_pkt_free(pktin);
d92624dc 740 if (id != 1) {
741 return 0;
742 }
743 return 1;
744}
745
746/*
747 * Retrieve the attributes of a file. We have fxp_stat which works
748 * on filenames, and fxp_fstat which works on open file handles.
749 */
1bc24185 750struct sftp_request *fxp_stat_send(char *fname)
d92624dc 751{
1bc24185 752 struct sftp_request *req = sftp_alloc_request();
753 struct sftp_packet *pktout;
d92624dc 754
755 pktout = sftp_pkt_init(SSH_FXP_STAT);
1bc24185 756 sftp_pkt_adduint32(pktout, req->id);
d92624dc 757 sftp_pkt_addstring(pktout, fname);
758 sftp_send(pktout);
d92624dc 759
1bc24185 760 return req;
761}
762
7b7de4f4 763int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
764 struct fxp_attrs *attrs)
1bc24185 765{
7b7de4f4 766 sfree(req);
d92624dc 767 if (pktin->type == SSH_FXP_ATTRS) {
768 *attrs = sftp_pkt_getattrs(pktin);
41d3adbb 769 sftp_pkt_free(pktin);
d92624dc 770 return 1;
771 } else {
772 fxp_got_status(pktin);
41d3adbb 773 sftp_pkt_free(pktin);
d92624dc 774 return 0;
775 }
776}
777
1bc24185 778struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
d92624dc 779{
1bc24185 780 struct sftp_request *req = sftp_alloc_request();
781 struct sftp_packet *pktout;
d92624dc 782
783 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
1bc24185 784 sftp_pkt_adduint32(pktout, req->id);
9954aaa3 785 sftp_pkt_addstring_start(pktout);
d92624dc 786 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
787 sftp_send(pktout);
d92624dc 788
1bc24185 789 return req;
790}
791
7b7de4f4 792int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
1bc24185 793 struct fxp_attrs *attrs)
794{
7b7de4f4 795 sfree(req);
d92624dc 796 if (pktin->type == SSH_FXP_ATTRS) {
797 *attrs = sftp_pkt_getattrs(pktin);
41d3adbb 798 sftp_pkt_free(pktin);
d92624dc 799 return 1;
800 } else {
801 fxp_got_status(pktin);
41d3adbb 802 sftp_pkt_free(pktin);
d92624dc 803 return 0;
804 }
805}
806
807/*
808 * Set the attributes of a file.
809 */
1bc24185 810struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
d92624dc 811{
1bc24185 812 struct sftp_request *req = sftp_alloc_request();
813 struct sftp_packet *pktout;
d92624dc 814
815 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
1bc24185 816 sftp_pkt_adduint32(pktout, req->id);
d92624dc 817 sftp_pkt_addstring(pktout, fname);
818 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 819 sftp_send(pktout);
1bc24185 820
821 return req;
822}
823
7b7de4f4 824int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 825{
7b7de4f4 826 int id;
827 sfree(req);
828 id = fxp_got_status(pktin);
41d3adbb 829 sftp_pkt_free(pktin);
9954aaa3 830 if (id != 1) {
831 return 0;
832 }
833 return 1;
834}
1bc24185 835
836struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
837 struct fxp_attrs attrs)
fd5e5847 838{
1bc24185 839 struct sftp_request *req = sftp_alloc_request();
840 struct sftp_packet *pktout;
fd5e5847 841
842 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
1bc24185 843 sftp_pkt_adduint32(pktout, req->id);
fd5e5847 844 sftp_pkt_addstring_start(pktout);
845 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
846 sftp_pkt_addattrs(pktout, attrs);
847 sftp_send(pktout);
1bc24185 848
849 return req;
850}
851
7b7de4f4 852int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 853{
7b7de4f4 854 int id;
855 sfree(req);
856 id = fxp_got_status(pktin);
41d3adbb 857 sftp_pkt_free(pktin);
fd5e5847 858 if (id != 1) {
859 return 0;
860 }
861 return 1;
862}
9954aaa3 863
4c7f0d61 864/*
865 * Read from a file. Returns the number of bytes read, or -1 on an
866 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
867 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
868 * error indicator. It might even depend on the SFTP server.)
869 */
1bc24185 870struct sftp_request *fxp_read_send(struct fxp_handle *handle,
871 uint64 offset, int len)
32874aea 872{
1bc24185 873 struct sftp_request *req = sftp_alloc_request();
874 struct sftp_packet *pktout;
4c7f0d61 875
876 pktout = sftp_pkt_init(SSH_FXP_READ);
1bc24185 877 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 878 sftp_pkt_addstring_start(pktout);
879 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 880 sftp_pkt_adduint64(pktout, offset);
881 sftp_pkt_adduint32(pktout, len);
882 sftp_send(pktout);
1bc24185 883
884 return req;
885}
886
7b7de4f4 887int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
888 char *buffer, int len)
1bc24185 889{
7b7de4f4 890 sfree(req);
4c7f0d61 891 if (pktin->type == SSH_FXP_DATA) {
892 char *str;
893 int rlen;
894
895 sftp_pkt_getstring(pktin, &str, &rlen);
896
897 if (rlen > len || rlen < 0) {
898 fxp_internal_error("READ returned more bytes than requested");
41d3adbb 899 sftp_pkt_free(pktin);
4c7f0d61 900 return -1;
901 }
902
903 memcpy(buffer, str, rlen);
41d3adbb 904 sftp_pkt_free(pktin);
4c7f0d61 905 return rlen;
906 } else {
907 fxp_got_status(pktin);
41d3adbb 908 sftp_pkt_free(pktin);
4c7f0d61 909 return -1;
910 }
911}
912
913/*
914 * Read from a directory.
915 */
1bc24185 916struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
32874aea 917{
1bc24185 918 struct sftp_request *req = sftp_alloc_request();
919 struct sftp_packet *pktout;
4c7f0d61 920
921 pktout = sftp_pkt_init(SSH_FXP_READDIR);
1bc24185 922 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 923 sftp_pkt_addstring_start(pktout);
924 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 925 sftp_send(pktout);
1bc24185 926
927 return req;
928}
929
7b7de4f4 930struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
931 struct sftp_request *req)
1bc24185 932{
7b7de4f4 933 sfree(req);
4c7f0d61 934 if (pktin->type == SSH_FXP_NAME) {
935 struct fxp_names *ret;
936 int i;
3d88e64d 937 ret = snew(struct fxp_names);
4c7f0d61 938 ret->nnames = sftp_pkt_getuint32(pktin);
3d88e64d 939 ret->names = snewn(ret->nnames, struct fxp_name);
4c7f0d61 940 for (i = 0; i < ret->nnames; i++) {
941 char *str;
942 int len;
943 sftp_pkt_getstring(pktin, &str, &len);
944 ret->names[i].filename = mkstr(str, len);
945 sftp_pkt_getstring(pktin, &str, &len);
946 ret->names[i].longname = mkstr(str, len);
947 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
948 }
41d3adbb 949 sftp_pkt_free(pktin);
4c7f0d61 950 return ret;
951 } else {
952 fxp_got_status(pktin);
41d3adbb 953 sftp_pkt_free(pktin);
4c7f0d61 954 return NULL;
955 }
956}
957
958/*
959 * Write to a file. Returns 0 on error, 1 on OK.
960 */
1bc24185 961struct sftp_request *fxp_write_send(struct fxp_handle *handle,
962 char *buffer, uint64 offset, int len)
32874aea 963{
1bc24185 964 struct sftp_request *req = sftp_alloc_request();
965 struct sftp_packet *pktout;
4c7f0d61 966
967 pktout = sftp_pkt_init(SSH_FXP_WRITE);
1bc24185 968 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 969 sftp_pkt_addstring_start(pktout);
970 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 971 sftp_pkt_adduint64(pktout, offset);
972 sftp_pkt_addstring_start(pktout);
973 sftp_pkt_addstring_data(pktout, buffer, len);
974 sftp_send(pktout);
1bc24185 975
976 return req;
977}
978
7b7de4f4 979int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 980{
7b7de4f4 981 sfree(req);
4c7f0d61 982 fxp_got_status(pktin);
41d3adbb 983 sftp_pkt_free(pktin);
4c7f0d61 984 return fxp_errtype == SSH_FX_OK;
985}
986
987/*
988 * Free up an fxp_names structure.
989 */
32874aea 990void fxp_free_names(struct fxp_names *names)
991{
4c7f0d61 992 int i;
993
994 for (i = 0; i < names->nnames; i++) {
995 sfree(names->names[i].filename);
996 sfree(names->names[i].longname);
997 }
998 sfree(names->names);
999 sfree(names);
1000}
7d2c1789 1001
1002/*
1003 * Duplicate an fxp_name structure.
1004 */
1005struct fxp_name *fxp_dup_name(struct fxp_name *name)
1006{
1007 struct fxp_name *ret;
3d88e64d 1008 ret = snew(struct fxp_name);
7d2c1789 1009 ret->filename = dupstr(name->filename);
1010 ret->longname = dupstr(name->longname);
1011 ret->attrs = name->attrs; /* structure copy */
1012 return ret;
1013}
1014
1015/*
1016 * Free up an fxp_name structure.
1017 */
1018void fxp_free_name(struct fxp_name *name)
1019{
7d2c1789 1020 sfree(name->filename);
1021 sfree(name->longname);
1022 sfree(name);
1023}
c606c42d 1024
1025/*
1026 * Store user data in an sftp_request structure.
1027 */
1028void *fxp_get_userdata(struct sftp_request *req)
1029{
1030 return req->userdata;
1031}
1032
1033void fxp_set_userdata(struct sftp_request *req, void *data)
1034{
1035 req->userdata = data;
1036}
1037
1038/*
1039 * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1040 * the queueing of multiple read/write requests.
1041 */
1042
1043struct req {
1044 char *buffer;
1045 int len, retlen, complete;
1046 uint64 offset;
1047 struct req *next, *prev;
1048};
1049
1050struct fxp_xfer {
1051 uint64 offset, furthestdata, filesize;
df0870fc 1052 int req_totalsize, req_maxsize, eof, err;
c606c42d 1053 struct fxp_handle *fh;
1054 struct req *head, *tail;
1055};
1056
1057static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1058{
1059 struct fxp_xfer *xfer = snew(struct fxp_xfer);
1060
1061 xfer->fh = fh;
1062 xfer->offset = offset;
1063 xfer->head = xfer->tail = NULL;
df0870fc 1064 xfer->req_totalsize = 0;
1065 xfer->req_maxsize = 16384;
c606c42d 1066 xfer->err = 0;
1067 xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1068 xfer->furthestdata = uint64_make(0, 0);
1069
1070 return xfer;
1071}
1072
df0870fc 1073int xfer_done(struct fxp_xfer *xfer)
c606c42d 1074{
1075 /*
1076 * We're finished if we've seen EOF _and_ there are no
1077 * outstanding requests.
1078 */
1079 return (xfer->eof || xfer->err) && !xfer->head;
1080}
1081
1082void xfer_download_queue(struct fxp_xfer *xfer)
1083{
df0870fc 1084 while (xfer->req_totalsize < xfer->req_maxsize && !xfer->eof) {
c606c42d 1085 /*
1086 * Queue a new read request.
1087 */
1088 struct req *rr;
1089 struct sftp_request *req;
1090
1091 rr = snew(struct req);
1092 rr->offset = xfer->offset;
1093 rr->complete = 0;
1094 if (xfer->tail) {
1095 xfer->tail->next = rr;
1096 rr->prev = xfer->tail;
1097 } else {
1098 xfer->head = rr;
1099 rr->prev = NULL;
1100 }
1101 xfer->tail = rr;
1102 rr->next = NULL;
1103
1104 rr->len = 4096;
1105 rr->buffer = snewn(rr->len, char);
1106 sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1107 fxp_set_userdata(req, rr);
1108
1109 xfer->offset = uint64_add32(xfer->offset, rr->len);
df0870fc 1110 xfer->req_totalsize += rr->len;
c606c42d 1111
1112#ifdef DEBUG_DOWNLOAD
1113 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1114#endif
1115 }
1116}
1117
1118struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1119{
1120 struct fxp_xfer *xfer = xfer_init(fh, offset);
1121
1122 xfer->eof = FALSE;
1123 xfer_download_queue(xfer);
1124
1125 return xfer;
1126}
1127
1128int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1129{
1130 struct sftp_request *rreq;
1131 struct req *rr;
1132
1133 rreq = sftp_find_request(pktin);
1134 rr = (struct req *)fxp_get_userdata(rreq);
1135 if (!rr)
1136 return 0; /* this packet isn't ours */
1137 rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1138#ifdef DEBUG_DOWNLOAD
1139 printf("read request %p has returned [%d]\n", rr, rr->retlen);
1140#endif
1141
1142 if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1143 xfer->eof = TRUE;
1144 rr->complete = -1;
1145#ifdef DEBUG_DOWNLOAD
1146 printf("setting eof\n");
1147#endif
1148 } else if (rr->retlen < 0) {
1149 /* some error other than EOF; signal it back to caller */
1150 return -1;
1151 }
1152
1153 rr->complete = 1;
1154
1155 /*
1156 * Special case: if we have received fewer bytes than we
1157 * actually read, we should do something. For the moment I'll
1158 * just throw an ersatz FXP error to signal this; the SFTP
1159 * draft I've got says that it can't happen except on special
1160 * files, in which case seeking probably has very little
1161 * meaning and so queueing an additional read request to fill
1162 * up the gap sounds like the wrong answer. I'm not sure what I
1163 * should be doing here - if it _was_ a special file, I suspect
1164 * I simply shouldn't have been queueing multiple requests in
1165 * the first place...
1166 */
1167 if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1168 xfer->furthestdata = rr->offset;
1169#ifdef DEBUG_DOWNLOAD
1170 { char buf[40];
1171 uint64_decimal(xfer->furthestdata, buf);
1172 printf("setting furthestdata = %s\n", buf); }
1173#endif
1174 }
1175
1176 if (rr->retlen < rr->len) {
1177 uint64 filesize = uint64_add32(rr->offset,
1178 (rr->retlen < 0 ? 0 : rr->retlen));
1179#ifdef DEBUG_DOWNLOAD
1180 { char buf[40];
1181 uint64_decimal(filesize, buf);
1182 printf("short block! trying filesize = %s\n", buf); }
1183#endif
1184 if (uint64_compare(xfer->filesize, filesize) > 0) {
1185 xfer->filesize = filesize;
1186#ifdef DEBUG_DOWNLOAD
1187 printf("actually changing filesize\n");
1188#endif
1189 }
1190 }
1191
1192 if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1193 fxp_error_message = "received a short buffer from FXP_READ, but not"
1194 " at EOF";
1195 fxp_errtype = -1;
1196 xfer_set_error(xfer);
1197 return -1;
1198 }
1199
1200 return 1;
1201}
1202
1203void xfer_set_error(struct fxp_xfer *xfer)
1204{
1205 xfer->err = 1;
1206}
1207
1208int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1209{
1210 void *retbuf = NULL;
1211 int retlen = 0;
1212
1213 /*
1214 * Discard anything at the head of the rr queue with complete <
1215 * 0; return the first thing with complete > 0.
1216 */
1217 while (xfer->head && xfer->head->complete && !retbuf) {
1218 struct req *rr = xfer->head;
1219
1220 if (rr->complete > 0) {
1221 retbuf = rr->buffer;
1222 retlen = rr->retlen;
1223#ifdef DEBUG_DOWNLOAD
1224 printf("handing back data from read request %p\n", rr);
1225#endif
1226 }
1227#ifdef DEBUG_DOWNLOAD
1228 else
1229 printf("skipping failed read request %p\n", rr);
1230#endif
1231
1232 xfer->head = xfer->head->next;
1233 if (xfer->head)
1234 xfer->head->prev = NULL;
1235 else
1236 xfer->tail = NULL;
df0870fc 1237 xfer->req_totalsize -= rr->len;
c606c42d 1238 sfree(rr);
c606c42d 1239 }
1240
1241 if (retbuf) {
1242 *buf = retbuf;
1243 *len = retlen;
1244 return 1;
1245 } else
1246 return 0;
1247}
1248
df0870fc 1249struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1250{
1251 struct fxp_xfer *xfer = xfer_init(fh, offset);
1252
1253 /*
1254 * We set `eof' to 1 because this will cause xfer_done() to
1255 * return true iff there are no outstanding requests. During an
1256 * upload, our caller will be responsible for working out
1257 * whether all the data has been sent, so all it needs to know
1258 * from us is whether the outstanding requests have been
1259 * handled once that's done.
1260 */
1261 xfer->eof = 1;
1262
1263 return xfer;
1264}
1265
1266int xfer_upload_ready(struct fxp_xfer *xfer)
1267{
1268 if (xfer->req_totalsize < xfer->req_maxsize)
1269 return 1;
1270 else
1271 return 0;
1272}
1273
1274void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1275{
1276 struct req *rr;
1277 struct sftp_request *req;
1278
1279 rr = snew(struct req);
1280 rr->offset = xfer->offset;
1281 rr->complete = 0;
1282 if (xfer->tail) {
1283 xfer->tail->next = rr;
1284 rr->prev = xfer->tail;
1285 } else {
1286 xfer->head = rr;
1287 rr->prev = NULL;
1288 }
1289 xfer->tail = rr;
1290 rr->next = NULL;
1291
1292 rr->len = len;
1293 rr->buffer = NULL;
1294 sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1295 fxp_set_userdata(req, rr);
1296
1297 xfer->offset = uint64_add32(xfer->offset, rr->len);
1298 xfer->req_totalsize += rr->len;
1299
1300#ifdef DEBUG_UPLOAD
1301 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1302#endif
1303}
1304
1305int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1306{
1307 struct sftp_request *rreq;
1308 struct req *rr, *prev, *next;
1309 int ret;
1310
1311 rreq = sftp_find_request(pktin);
1312 rr = (struct req *)fxp_get_userdata(rreq);
1313 if (!rr)
1314 return 0; /* this packet isn't ours */
1315 ret = fxp_write_recv(pktin, rreq);
1316#ifdef DEBUG_UPLOAD
1317 printf("write request %p has returned [%d]\n", rr, ret);
1318#endif
1319
1320 /*
1321 * Remove this one from the queue.
1322 */
1323 prev = rr->prev;
1324 next = rr->next;
1325 if (prev)
1326 prev->next = next;
1327 else
1328 xfer->head = next;
1329 if (next)
1330 next->prev = prev;
1331 else
1332 xfer->tail = prev;
1333 xfer->req_totalsize -= rr->len;
1334 sfree(rr);
1335
1336 if (!ret)
1337 return -1;
1338
1339 return 1;
1340}
1341
c606c42d 1342void xfer_cleanup(struct fxp_xfer *xfer)
1343{
1344 struct req *rr;
1345 while (xfer->head) {
1346 rr = xfer->head;
1347 xfer->head = xfer->head->next;
1348 sfree(rr->buffer);
1349 sfree(rr);
1350 }
1351 sfree(xfer);
1352}