Fix error handling in sftp (the sftp_recv return value was being
[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>
4c7f0d61 9
78139cd8 10#include "misc.h"
4c7f0d61 11#include "int64.h"
12#include "sftp.h"
13
4c7f0d61 14#define GET_32BIT(cp) \
15 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
16 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
17 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
18 ((unsigned long)(unsigned char)(cp)[3]))
19
20#define PUT_32BIT(cp, value) { \
21 (cp)[0] = (unsigned char)((value) >> 24); \
22 (cp)[1] = (unsigned char)((value) >> 16); \
23 (cp)[2] = (unsigned char)((value) >> 8); \
24 (cp)[3] = (unsigned char)(value); }
25
26struct sftp_packet {
27 char *data;
28 int length, maxlen;
29 int savedpos;
30 int type;
31};
32
9954aaa3 33static const char *fxp_error_message;
34static int fxp_errtype;
35
4c7f0d61 36/* ----------------------------------------------------------------------
37 * SFTP packet construction functions.
38 */
32874aea 39static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
40{
4c7f0d61 41 if (pkt->maxlen < length) {
32874aea 42 pkt->maxlen = length + 256;
4c7f0d61 43 pkt->data = srealloc(pkt->data, pkt->maxlen);
44 }
45}
32874aea 46static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
47{
4c7f0d61 48 pkt->length += len;
49 sftp_pkt_ensure(pkt, pkt->length);
32874aea 50 memcpy(pkt->data + pkt->length - len, data, len);
4c7f0d61 51}
32874aea 52static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
53{
4c7f0d61 54 sftp_pkt_adddata(pkt, &byte, 1);
55}
32874aea 56static struct sftp_packet *sftp_pkt_init(int pkt_type)
57{
4c7f0d61 58 struct sftp_packet *pkt;
59 pkt = smalloc(sizeof(struct sftp_packet));
60 pkt->data = NULL;
61 pkt->savedpos = -1;
62 pkt->length = 0;
63 pkt->maxlen = 0;
32874aea 64 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
9954aaa3 65 fxp_error_message = NULL;
4c7f0d61 66 return pkt;
67}
32874aea 68static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
69{
4c7f0d61 70 sftp_pkt_adddata(pkt, &value, 1);
71}
32874aea 72static void sftp_pkt_adduint32(struct sftp_packet *pkt,
73 unsigned long value)
74{
4c7f0d61 75 unsigned char x[4];
76 PUT_32BIT(x, value);
77 sftp_pkt_adddata(pkt, x, 4);
78}
32874aea 79static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
80{
4c7f0d61 81 unsigned char x[8];
82 PUT_32BIT(x, value.hi);
32874aea 83 PUT_32BIT(x + 4, value.lo);
4c7f0d61 84 sftp_pkt_adddata(pkt, x, 8);
85}
32874aea 86static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
87{
4c7f0d61 88 sftp_pkt_adduint32(pkt, 0);
89 pkt->savedpos = pkt->length;
90}
32874aea 91static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
92{
4c7f0d61 93 sftp_pkt_adddata(pkt, data, strlen(data));
32874aea 94 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 95}
96static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
32874aea 97 char *data, int len)
98{
4c7f0d61 99 sftp_pkt_adddata(pkt, data, len);
32874aea 100 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 101}
32874aea 102static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
103{
4c7f0d61 104 sftp_pkt_addstring_start(pkt);
105 sftp_pkt_addstring_str(pkt, data);
106}
d92624dc 107static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
108{
109 sftp_pkt_adduint32(pkt, attrs.flags);
110 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
111 sftp_pkt_adduint32(pkt, attrs.size.hi);
112 sftp_pkt_adduint32(pkt, attrs.size.lo);
113 }
114 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
115 sftp_pkt_adduint32(pkt, attrs.uid);
116 sftp_pkt_adduint32(pkt, attrs.gid);
117 }
118 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
119 sftp_pkt_adduint32(pkt, attrs.permissions);
120 }
121 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
122 sftp_pkt_adduint32(pkt, attrs.atime);
123 sftp_pkt_adduint32(pkt, attrs.mtime);
124 }
125 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
126 /*
127 * We currently don't support sending any extended
128 * attributes.
129 */
130 }
131}
4c7f0d61 132
133/* ----------------------------------------------------------------------
134 * SFTP packet decode functions.
135 */
136
32874aea 137static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
138{
d8770b12 139 unsigned char value;
4c7f0d61 140 if (pkt->length - pkt->savedpos < 1)
32874aea 141 return 0; /* arrgh, no way to decline (FIXME?) */
4c7f0d61 142 value = (unsigned char) pkt->data[pkt->savedpos];
143 pkt->savedpos++;
144 return value;
145}
32874aea 146static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
147{
4c7f0d61 148 unsigned long value;
149 if (pkt->length - pkt->savedpos < 4)
32874aea 150 return 0; /* arrgh, no way to decline (FIXME?) */
151 value = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 152 pkt->savedpos += 4;
153 return value;
154}
155static void sftp_pkt_getstring(struct sftp_packet *pkt,
32874aea 156 char **p, int *length)
157{
4c7f0d61 158 *p = NULL;
159 if (pkt->length - pkt->savedpos < 4)
32874aea 160 return;
161 *length = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 162 pkt->savedpos += 4;
163 if (pkt->length - pkt->savedpos < *length)
32874aea 164 return;
165 *p = pkt->data + pkt->savedpos;
4c7f0d61 166 pkt->savedpos += *length;
167}
32874aea 168static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
169{
4c7f0d61 170 struct fxp_attrs ret;
171 ret.flags = sftp_pkt_getuint32(pkt);
172 if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
173 unsigned long hi, lo;
174 hi = sftp_pkt_getuint32(pkt);
175 lo = sftp_pkt_getuint32(pkt);
176 ret.size = uint64_make(hi, lo);
177 }
178 if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
179 ret.uid = sftp_pkt_getuint32(pkt);
180 ret.gid = sftp_pkt_getuint32(pkt);
181 }
182 if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
183 ret.permissions = sftp_pkt_getuint32(pkt);
184 }
185 if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
186 ret.atime = sftp_pkt_getuint32(pkt);
187 ret.mtime = sftp_pkt_getuint32(pkt);
188 }
189 if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
190 int count;
191 count = sftp_pkt_getuint32(pkt);
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 */
199 sftp_pkt_getstring(pkt, &str, &len);
200 sftp_pkt_getstring(pkt, &str, &len);
201 }
202 }
203 return ret;
204}
32874aea 205static void sftp_pkt_free(struct sftp_packet *pkt)
206{
207 if (pkt->data)
208 sfree(pkt->data);
4c7f0d61 209 sfree(pkt);
210}
211
212/* ----------------------------------------------------------------------
4a8fc3c4 213 * Send and receive packet functions.
4c7f0d61 214 */
32874aea 215int sftp_send(struct sftp_packet *pkt)
216{
4a8fc3c4 217 int ret;
4c7f0d61 218 char x[4];
219 PUT_32BIT(x, pkt->length);
32874aea 220 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
4c7f0d61 221 sftp_pkt_free(pkt);
4a8fc3c4 222 return ret;
4c7f0d61 223}
32874aea 224struct sftp_packet *sftp_recv(void)
225{
4c7f0d61 226 struct sftp_packet *pkt;
227 char x[4];
4c7f0d61 228
4a8fc3c4 229 if (!sftp_recvdata(x, 4))
230 return NULL;
4c7f0d61 231
232 pkt = smalloc(sizeof(struct sftp_packet));
233 pkt->savedpos = 0;
234 pkt->length = pkt->maxlen = GET_32BIT(x);
235 pkt->data = smalloc(pkt->length);
236
4a8fc3c4 237 if (!sftp_recvdata(pkt->data, pkt->length)) {
238 sftp_pkt_free(pkt);
239 return NULL;
4c7f0d61 240 }
241
242 pkt->type = sftp_pkt_getbyte(pkt);
243
244 return pkt;
245}
246
247/* ----------------------------------------------------------------------
248 * String handling routines.
249 */
250
32874aea 251static char *mkstr(char *s, int len)
252{
253 char *p = smalloc(len + 1);
4c7f0d61 254 memcpy(p, s, len);
255 p[len] = '\0';
256 return p;
257}
258
259/* ----------------------------------------------------------------------
260 * SFTP primitives.
261 */
262
4c7f0d61 263/*
264 * Deal with (and free) an FXP_STATUS packet. Return 1 if
265 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
266 * Also place the status into fxp_errtype.
267 */
32874aea 268static int fxp_got_status(struct sftp_packet *pktin)
269{
4c7f0d61 270 static const char *const messages[] = {
271 /* SSH_FX_OK. The only time we will display a _message_ for this
272 * is if we were expecting something other than FXP_STATUS on
273 * success, so this is actually an error message! */
274 "unexpected OK response",
275 "end of file",
276 "no such file or directory",
277 "permission denied",
278 "failure",
279 "bad message",
280 "no connection",
281 "connection lost",
282 "operation unsupported",
283 };
284
285 if (pktin->type != SSH_FXP_STATUS) {
286 fxp_error_message = "expected FXP_STATUS packet";
287 fxp_errtype = -1;
288 } else {
289 fxp_errtype = sftp_pkt_getuint32(pktin);
290 if (fxp_errtype < 0 ||
32874aea 291 fxp_errtype >= sizeof(messages) / sizeof(*messages))
292 fxp_error_message = "unknown error code";
4c7f0d61 293 else
294 fxp_error_message = messages[fxp_errtype];
295 }
296
297 if (fxp_errtype == SSH_FX_OK)
298 return 1;
299 else if (fxp_errtype == SSH_FX_EOF)
300 return 0;
301 else
302 return -1;
303}
304
32874aea 305static void fxp_internal_error(char *msg)
306{
4c7f0d61 307 fxp_error_message = msg;
308 fxp_errtype = -1;
309}
310
32874aea 311const char *fxp_error(void)
312{
4c7f0d61 313 return fxp_error_message;
314}
315
32874aea 316int fxp_error_type(void)
317{
4c7f0d61 318 return fxp_errtype;
319}
320
321/*
322 * Perform exchange of init/version packets. Return 0 on failure.
323 */
32874aea 324int fxp_init(void)
325{
4c7f0d61 326 struct sftp_packet *pktout, *pktin;
327 int remotever;
328
329 pktout = sftp_pkt_init(SSH_FXP_INIT);
330 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
331 sftp_send(pktout);
332
333 pktin = sftp_recv();
0d694692 334 if (!pktin) {
335 fxp_internal_error("could not connect");
336 return 0;
337 }
4c7f0d61 338 if (pktin->type != SSH_FXP_VERSION) {
339 fxp_internal_error("did not receive FXP_VERSION");
340 return 0;
341 }
342 remotever = sftp_pkt_getuint32(pktin);
343 if (remotever > SFTP_PROTO_VERSION) {
32874aea 344 fxp_internal_error
345 ("remote protocol is more advanced than we support");
4c7f0d61 346 return 0;
347 }
348 /*
349 * In principle, this packet might also contain extension-
350 * string pairs. We should work through them and look for any
351 * we recognise. In practice we don't currently do so because
352 * we know we don't recognise _any_.
353 */
354 sftp_pkt_free(pktin);
355
356 return 1;
357}
358
359/*
f9e162aa 360 * Canonify a pathname.
4c7f0d61 361 */
32874aea 362char *fxp_realpath(char *path)
363{
4c7f0d61 364 struct sftp_packet *pktin, *pktout;
365 int id;
366
367 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
368 sftp_pkt_adduint32(pktout, 0x123); /* request id */
369 sftp_pkt_addstring_start(pktout);
370 sftp_pkt_addstring_str(pktout, path);
4c7f0d61 371 sftp_send(pktout);
372 pktin = sftp_recv();
f65c4643 373 if (!pktin) {
374 fxp_internal_error("did not receive a valid SFTP packet\n");
375 return NULL;
376 }
4c7f0d61 377 id = sftp_pkt_getuint32(pktin);
378 if (id != 0x123) {
379 fxp_internal_error("request ID mismatch\n");
380 return NULL;
381 }
382 if (pktin->type == SSH_FXP_NAME) {
383 int count;
384 char *path;
385 int len;
386
387 count = sftp_pkt_getuint32(pktin);
388 if (count != 1) {
389 fxp_internal_error("REALPATH returned name count != 1\n");
390 return NULL;
391 }
392 sftp_pkt_getstring(pktin, &path, &len);
393 if (!path) {
394 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
395 return NULL;
396 }
397 path = mkstr(path, len);
398 sftp_pkt_free(pktin);
399 return path;
400 } else {
401 fxp_got_status(pktin);
402 return NULL;
403 }
404}
405
406/*
407 * Open a file.
408 */
32874aea 409struct fxp_handle *fxp_open(char *path, int type)
410{
4c7f0d61 411 struct sftp_packet *pktin, *pktout;
412 int id;
413
414 pktout = sftp_pkt_init(SSH_FXP_OPEN);
415 sftp_pkt_adduint32(pktout, 0x567); /* request id */
416 sftp_pkt_addstring(pktout, path);
417 sftp_pkt_adduint32(pktout, type);
418 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
419 sftp_send(pktout);
420 pktin = sftp_recv();
f65c4643 421 if (!pktin) {
422 fxp_internal_error("did not receive a valid SFTP packet\n");
423 return NULL;
424 }
4c7f0d61 425 id = sftp_pkt_getuint32(pktin);
426 if (id != 0x567) {
427 fxp_internal_error("request ID mismatch\n");
428 return NULL;
429 }
430 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 431 char *hstring;
432 struct fxp_handle *handle;
433 int len;
434
435 sftp_pkt_getstring(pktin, &hstring, &len);
436 if (!hstring) {
437 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
438 return NULL;
439 }
440 handle = smalloc(sizeof(struct fxp_handle));
441 handle->hstring = mkstr(hstring, len);
f9e162aa 442 handle->hlen = len;
4c7f0d61 443 sftp_pkt_free(pktin);
444 return handle;
445 } else {
446 fxp_got_status(pktin);
447 return NULL;
448 }
449}
450
451/*
452 * Open a directory.
453 */
32874aea 454struct fxp_handle *fxp_opendir(char *path)
455{
4c7f0d61 456 struct sftp_packet *pktin, *pktout;
457 int id;
458
459 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
460 sftp_pkt_adduint32(pktout, 0x456); /* request id */
461 sftp_pkt_addstring(pktout, path);
462 sftp_send(pktout);
463 pktin = sftp_recv();
f65c4643 464 if (!pktin) {
465 fxp_internal_error("did not receive a valid SFTP packet\n");
466 return NULL;
467 }
4c7f0d61 468 id = sftp_pkt_getuint32(pktin);
469 if (id != 0x456) {
470 fxp_internal_error("request ID mismatch\n");
471 return NULL;
472 }
473 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 474 char *hstring;
475 struct fxp_handle *handle;
476 int len;
477
478 sftp_pkt_getstring(pktin, &hstring, &len);
479 if (!hstring) {
480 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
481 return NULL;
482 }
483 handle = smalloc(sizeof(struct fxp_handle));
484 handle->hstring = mkstr(hstring, len);
f9e162aa 485 handle->hlen = len;
4c7f0d61 486 sftp_pkt_free(pktin);
487 return handle;
488 } else {
489 fxp_got_status(pktin);
490 return NULL;
491 }
492}
493
494/*
495 * Close a file/dir.
496 */
32874aea 497void fxp_close(struct fxp_handle *handle)
498{
4c7f0d61 499 struct sftp_packet *pktin, *pktout;
500 int id;
501
502 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
503 sftp_pkt_adduint32(pktout, 0x789); /* request id */
f9e162aa 504 sftp_pkt_addstring_start(pktout);
505 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 506 sftp_send(pktout);
507 pktin = sftp_recv();
f65c4643 508 if (!pktin) {
509 fxp_internal_error("did not receive a valid SFTP packet\n");
510 return;
511 }
4c7f0d61 512 id = sftp_pkt_getuint32(pktin);
513 if (id != 0x789) {
514 fxp_internal_error("request ID mismatch\n");
515 return;
516 }
517 fxp_got_status(pktin);
518 sfree(handle->hstring);
519 sfree(handle);
520}
521
9954aaa3 522int fxp_mkdir(char *path)
523{
524 struct sftp_packet *pktin, *pktout;
525 int id;
526
527 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
528 sftp_pkt_adduint32(pktout, 0x234); /* request id */
d92624dc 529 sftp_pkt_addstring(pktout, path);
9954aaa3 530 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
531 sftp_send(pktout);
532 pktin = sftp_recv();
f65c4643 533 if (!pktin) {
534 fxp_internal_error("did not receive a valid SFTP packet\n");
535 return 0;
536 }
9954aaa3 537 id = sftp_pkt_getuint32(pktin);
538 if (id != 0x234) {
539 fxp_internal_error("request ID mismatch\n");
540 return 0;
541 }
542 id = fxp_got_status(pktin);
543 if (id != 1) {
544 return 0;
545 }
546 return 1;
547}
548
549int fxp_rmdir(char *path)
550{
551 struct sftp_packet *pktin, *pktout;
552 int id;
553
554 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
555 sftp_pkt_adduint32(pktout, 0x345); /* request id */
d92624dc 556 sftp_pkt_addstring(pktout, path);
9954aaa3 557 sftp_send(pktout);
558 pktin = sftp_recv();
f65c4643 559 if (!pktin) {
560 fxp_internal_error("did not receive a valid SFTP packet\n");
561 return 0;
562 }
9954aaa3 563 id = sftp_pkt_getuint32(pktin);
564 if (id != 0x345) {
565 fxp_internal_error("request ID mismatch\n");
566 return 0;
567 }
568 id = fxp_got_status(pktin);
569 if (id != 1) {
570 return 0;
571 }
572 return 1;
573}
574
d92624dc 575int fxp_remove(char *fname)
9954aaa3 576{
577 struct sftp_packet *pktin, *pktout;
578 int id;
579
580 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
581 sftp_pkt_adduint32(pktout, 0x678); /* request id */
d92624dc 582 sftp_pkt_addstring(pktout, fname);
583 sftp_send(pktout);
584 pktin = sftp_recv();
f65c4643 585 if (!pktin) {
586 fxp_internal_error("did not receive a valid SFTP packet\n");
587 return 0;
588 }
d92624dc 589 id = sftp_pkt_getuint32(pktin);
590 if (id != 0x678) {
591 fxp_internal_error("request ID mismatch\n");
592 return 0;
593 }
594 id = fxp_got_status(pktin);
595 if (id != 1) {
596 return 0;
597 }
598 return 1;
599}
600
601int fxp_rename(char *srcfname, char *dstfname)
602{
603 struct sftp_packet *pktin, *pktout;
604 int id;
605
606 pktout = sftp_pkt_init(SSH_FXP_RENAME);
607 sftp_pkt_adduint32(pktout, 0x678); /* request id */
608 sftp_pkt_addstring(pktout, srcfname);
609 sftp_pkt_addstring(pktout, dstfname);
610 sftp_send(pktout);
611 pktin = sftp_recv();
f65c4643 612 if (!pktin) {
613 fxp_internal_error("did not receive a valid SFTP packet\n");
614 return 0;
615 }
d92624dc 616 id = sftp_pkt_getuint32(pktin);
617 if (id != 0x678) {
618 fxp_internal_error("request ID mismatch\n");
619 return 0;
620 }
621 id = fxp_got_status(pktin);
622 if (id != 1) {
623 return 0;
624 }
625 return 1;
626}
627
628/*
629 * Retrieve the attributes of a file. We have fxp_stat which works
630 * on filenames, and fxp_fstat which works on open file handles.
631 */
632int fxp_stat(char *fname, struct fxp_attrs *attrs)
633{
634 struct sftp_packet *pktin, *pktout;
635 int id;
636
637 pktout = sftp_pkt_init(SSH_FXP_STAT);
638 sftp_pkt_adduint32(pktout, 0x678); /* request id */
639 sftp_pkt_addstring(pktout, fname);
640 sftp_send(pktout);
641 pktin = sftp_recv();
f65c4643 642 if (!pktin) {
643 fxp_internal_error("did not receive a valid SFTP packet\n");
644 return 0;
645 }
d92624dc 646 id = sftp_pkt_getuint32(pktin);
647 if (id != 0x678) {
648 fxp_internal_error("request ID mismatch\n");
649 return 0;
650 }
651
652 if (pktin->type == SSH_FXP_ATTRS) {
653 *attrs = sftp_pkt_getattrs(pktin);
654 return 1;
655 } else {
656 fxp_got_status(pktin);
657 return 0;
658 }
659}
660
661int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
662{
663 struct sftp_packet *pktin, *pktout;
664 int id;
665
666 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
667 sftp_pkt_adduint32(pktout, 0x678); /* request id */
9954aaa3 668 sftp_pkt_addstring_start(pktout);
d92624dc 669 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
670 sftp_send(pktout);
671 pktin = sftp_recv();
f65c4643 672 if (!pktin) {
673 fxp_internal_error("did not receive a valid SFTP packet\n");
674 return 0;
675 }
d92624dc 676 id = sftp_pkt_getuint32(pktin);
677 if (id != 0x678) {
678 fxp_internal_error("request ID mismatch\n");
679 return 0;
680 }
681
682 if (pktin->type == SSH_FXP_ATTRS) {
683 *attrs = sftp_pkt_getattrs(pktin);
684 return 1;
685 } else {
686 fxp_got_status(pktin);
687 return 0;
688 }
689}
690
691/*
692 * Set the attributes of a file.
693 */
694int fxp_setstat(char *fname, struct fxp_attrs attrs)
695{
696 struct sftp_packet *pktin, *pktout;
697 int id;
698
699 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
700 sftp_pkt_adduint32(pktout, 0x678); /* request id */
701 sftp_pkt_addstring(pktout, fname);
702 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 703 sftp_send(pktout);
704 pktin = sftp_recv();
f65c4643 705 if (!pktin) {
706 fxp_internal_error("did not receive a valid SFTP packet\n");
707 return 0;
708 }
9954aaa3 709 id = sftp_pkt_getuint32(pktin);
710 if (id != 0x678) {
711 fxp_internal_error("request ID mismatch\n");
712 return 0;
713 }
714 id = fxp_got_status(pktin);
715 if (id != 1) {
716 return 0;
717 }
718 return 1;
719}
fd5e5847 720int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs)
721{
722 struct sftp_packet *pktin, *pktout;
723 int id;
724
725 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
726 sftp_pkt_adduint32(pktout, 0x678); /* request id */
727 sftp_pkt_addstring_start(pktout);
728 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
729 sftp_pkt_addattrs(pktout, attrs);
730 sftp_send(pktout);
731 pktin = sftp_recv();
f65c4643 732 if (!pktin) {
733 fxp_internal_error("did not receive a valid SFTP packet\n");
734 return 0;
735 }
fd5e5847 736 id = sftp_pkt_getuint32(pktin);
737 if (id != 0x678) {
738 fxp_internal_error("request ID mismatch\n");
739 return 0;
740 }
741 id = fxp_got_status(pktin);
742 if (id != 1) {
743 return 0;
744 }
745 return 1;
746}
9954aaa3 747
4c7f0d61 748/*
749 * Read from a file. Returns the number of bytes read, or -1 on an
750 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
751 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
752 * error indicator. It might even depend on the SFTP server.)
753 */
32874aea 754int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
755 int len)
756{
4c7f0d61 757 struct sftp_packet *pktin, *pktout;
758 int id;
759
760 pktout = sftp_pkt_init(SSH_FXP_READ);
761 sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
f9e162aa 762 sftp_pkt_addstring_start(pktout);
763 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 764 sftp_pkt_adduint64(pktout, offset);
765 sftp_pkt_adduint32(pktout, len);
766 sftp_send(pktout);
767 pktin = sftp_recv();
f65c4643 768 if (!pktin) {
769 fxp_internal_error("did not receive a valid SFTP packet\n");
770 return -1;
771 }
4c7f0d61 772 id = sftp_pkt_getuint32(pktin);
773 if (id != 0xBCD) {
774 fxp_internal_error("request ID mismatch");
4a8fc3c4 775 return -1;
4c7f0d61 776 }
777 if (pktin->type == SSH_FXP_DATA) {
778 char *str;
779 int rlen;
780
781 sftp_pkt_getstring(pktin, &str, &rlen);
782
783 if (rlen > len || rlen < 0) {
784 fxp_internal_error("READ returned more bytes than requested");
785 return -1;
786 }
787
788 memcpy(buffer, str, rlen);
789 sfree(pktin);
790 return rlen;
791 } else {
792 fxp_got_status(pktin);
793 return -1;
794 }
795}
796
797/*
798 * Read from a directory.
799 */
32874aea 800struct fxp_names *fxp_readdir(struct fxp_handle *handle)
801{
4c7f0d61 802 struct sftp_packet *pktin, *pktout;
803 int id;
804
805 pktout = sftp_pkt_init(SSH_FXP_READDIR);
806 sftp_pkt_adduint32(pktout, 0xABC); /* request id */
f9e162aa 807 sftp_pkt_addstring_start(pktout);
808 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 809 sftp_send(pktout);
810 pktin = sftp_recv();
f65c4643 811 if (!pktin) {
812 fxp_internal_error("did not receive a valid SFTP packet\n");
813 return NULL;
814 }
4c7f0d61 815 id = sftp_pkt_getuint32(pktin);
816 if (id != 0xABC) {
817 fxp_internal_error("request ID mismatch\n");
4a8fc3c4 818 return NULL;
4c7f0d61 819 }
820 if (pktin->type == SSH_FXP_NAME) {
821 struct fxp_names *ret;
822 int i;
823 ret = smalloc(sizeof(struct fxp_names));
824 ret->nnames = sftp_pkt_getuint32(pktin);
825 ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
826 for (i = 0; i < ret->nnames; i++) {
827 char *str;
828 int len;
829 sftp_pkt_getstring(pktin, &str, &len);
830 ret->names[i].filename = mkstr(str, len);
831 sftp_pkt_getstring(pktin, &str, &len);
832 ret->names[i].longname = mkstr(str, len);
833 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
834 }
835 return ret;
836 } else {
837 fxp_got_status(pktin);
838 return NULL;
839 }
840}
841
842/*
843 * Write to a file. Returns 0 on error, 1 on OK.
844 */
32874aea 845int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
846 int len)
847{
4c7f0d61 848 struct sftp_packet *pktin, *pktout;
849 int id;
850
851 pktout = sftp_pkt_init(SSH_FXP_WRITE);
852 sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
f9e162aa 853 sftp_pkt_addstring_start(pktout);
854 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 855 sftp_pkt_adduint64(pktout, offset);
856 sftp_pkt_addstring_start(pktout);
857 sftp_pkt_addstring_data(pktout, buffer, len);
858 sftp_send(pktout);
859 pktin = sftp_recv();
f65c4643 860 if (!pktin) {
861 fxp_internal_error("did not receive a valid SFTP packet\n");
862 return 0;
863 }
4c7f0d61 864 id = sftp_pkt_getuint32(pktin);
4a8fc3c4 865 if (id != 0xDCB) {
866 fxp_internal_error("request ID mismatch\n");
d8770b12 867 return 0;
4a8fc3c4 868 }
4c7f0d61 869 fxp_got_status(pktin);
870 return fxp_errtype == SSH_FX_OK;
871}
872
873/*
874 * Free up an fxp_names structure.
875 */
32874aea 876void fxp_free_names(struct fxp_names *names)
877{
4c7f0d61 878 int i;
879
880 for (i = 0; i < names->nnames; i++) {
881 sfree(names->names[i].filename);
882 sfree(names->names[i].longname);
883 }
884 sfree(names->names);
885 sfree(names);
886}