Yikes! sftp.c wasn't using the misc.h wrappered malloc functions,
[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();
373 id = sftp_pkt_getuint32(pktin);
374 if (id != 0x123) {
375 fxp_internal_error("request ID mismatch\n");
376 return NULL;
377 }
378 if (pktin->type == SSH_FXP_NAME) {
379 int count;
380 char *path;
381 int len;
382
383 count = sftp_pkt_getuint32(pktin);
384 if (count != 1) {
385 fxp_internal_error("REALPATH returned name count != 1\n");
386 return NULL;
387 }
388 sftp_pkt_getstring(pktin, &path, &len);
389 if (!path) {
390 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
391 return NULL;
392 }
393 path = mkstr(path, len);
394 sftp_pkt_free(pktin);
395 return path;
396 } else {
397 fxp_got_status(pktin);
398 return NULL;
399 }
400}
401
402/*
403 * Open a file.
404 */
32874aea 405struct fxp_handle *fxp_open(char *path, int type)
406{
4c7f0d61 407 struct sftp_packet *pktin, *pktout;
408 int id;
409
410 pktout = sftp_pkt_init(SSH_FXP_OPEN);
411 sftp_pkt_adduint32(pktout, 0x567); /* request id */
412 sftp_pkt_addstring(pktout, path);
413 sftp_pkt_adduint32(pktout, type);
414 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
415 sftp_send(pktout);
416 pktin = sftp_recv();
417 id = sftp_pkt_getuint32(pktin);
418 if (id != 0x567) {
419 fxp_internal_error("request ID mismatch\n");
420 return NULL;
421 }
422 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 423 char *hstring;
424 struct fxp_handle *handle;
425 int len;
426
427 sftp_pkt_getstring(pktin, &hstring, &len);
428 if (!hstring) {
429 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
430 return NULL;
431 }
432 handle = smalloc(sizeof(struct fxp_handle));
433 handle->hstring = mkstr(hstring, len);
f9e162aa 434 handle->hlen = len;
4c7f0d61 435 sftp_pkt_free(pktin);
436 return handle;
437 } else {
438 fxp_got_status(pktin);
439 return NULL;
440 }
441}
442
443/*
444 * Open a directory.
445 */
32874aea 446struct fxp_handle *fxp_opendir(char *path)
447{
4c7f0d61 448 struct sftp_packet *pktin, *pktout;
449 int id;
450
451 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
452 sftp_pkt_adduint32(pktout, 0x456); /* request id */
453 sftp_pkt_addstring(pktout, path);
454 sftp_send(pktout);
455 pktin = sftp_recv();
456 id = sftp_pkt_getuint32(pktin);
457 if (id != 0x456) {
458 fxp_internal_error("request ID mismatch\n");
459 return NULL;
460 }
461 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 462 char *hstring;
463 struct fxp_handle *handle;
464 int len;
465
466 sftp_pkt_getstring(pktin, &hstring, &len);
467 if (!hstring) {
468 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
469 return NULL;
470 }
471 handle = smalloc(sizeof(struct fxp_handle));
472 handle->hstring = mkstr(hstring, len);
f9e162aa 473 handle->hlen = len;
4c7f0d61 474 sftp_pkt_free(pktin);
475 return handle;
476 } else {
477 fxp_got_status(pktin);
478 return NULL;
479 }
480}
481
482/*
483 * Close a file/dir.
484 */
32874aea 485void fxp_close(struct fxp_handle *handle)
486{
4c7f0d61 487 struct sftp_packet *pktin, *pktout;
488 int id;
489
490 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
491 sftp_pkt_adduint32(pktout, 0x789); /* request id */
f9e162aa 492 sftp_pkt_addstring_start(pktout);
493 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 494 sftp_send(pktout);
495 pktin = sftp_recv();
496 id = sftp_pkt_getuint32(pktin);
497 if (id != 0x789) {
498 fxp_internal_error("request ID mismatch\n");
499 return;
500 }
501 fxp_got_status(pktin);
502 sfree(handle->hstring);
503 sfree(handle);
504}
505
9954aaa3 506int fxp_mkdir(char *path)
507{
508 struct sftp_packet *pktin, *pktout;
509 int id;
510
511 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
512 sftp_pkt_adduint32(pktout, 0x234); /* request id */
d92624dc 513 sftp_pkt_addstring(pktout, path);
9954aaa3 514 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
515 sftp_send(pktout);
516 pktin = sftp_recv();
517 id = sftp_pkt_getuint32(pktin);
518 if (id != 0x234) {
519 fxp_internal_error("request ID mismatch\n");
520 return 0;
521 }
522 id = fxp_got_status(pktin);
523 if (id != 1) {
524 return 0;
525 }
526 return 1;
527}
528
529int fxp_rmdir(char *path)
530{
531 struct sftp_packet *pktin, *pktout;
532 int id;
533
534 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
535 sftp_pkt_adduint32(pktout, 0x345); /* request id */
d92624dc 536 sftp_pkt_addstring(pktout, path);
9954aaa3 537 sftp_send(pktout);
538 pktin = sftp_recv();
539 id = sftp_pkt_getuint32(pktin);
540 if (id != 0x345) {
541 fxp_internal_error("request ID mismatch\n");
542 return 0;
543 }
544 id = fxp_got_status(pktin);
545 if (id != 1) {
546 return 0;
547 }
548 return 1;
549}
550
d92624dc 551int fxp_remove(char *fname)
9954aaa3 552{
553 struct sftp_packet *pktin, *pktout;
554 int id;
555
556 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
557 sftp_pkt_adduint32(pktout, 0x678); /* request id */
d92624dc 558 sftp_pkt_addstring(pktout, fname);
559 sftp_send(pktout);
560 pktin = sftp_recv();
561 id = sftp_pkt_getuint32(pktin);
562 if (id != 0x678) {
563 fxp_internal_error("request ID mismatch\n");
564 return 0;
565 }
566 id = fxp_got_status(pktin);
567 if (id != 1) {
568 return 0;
569 }
570 return 1;
571}
572
573int fxp_rename(char *srcfname, char *dstfname)
574{
575 struct sftp_packet *pktin, *pktout;
576 int id;
577
578 pktout = sftp_pkt_init(SSH_FXP_RENAME);
579 sftp_pkt_adduint32(pktout, 0x678); /* request id */
580 sftp_pkt_addstring(pktout, srcfname);
581 sftp_pkt_addstring(pktout, dstfname);
582 sftp_send(pktout);
583 pktin = sftp_recv();
584 id = sftp_pkt_getuint32(pktin);
585 if (id != 0x678) {
586 fxp_internal_error("request ID mismatch\n");
587 return 0;
588 }
589 id = fxp_got_status(pktin);
590 if (id != 1) {
591 return 0;
592 }
593 return 1;
594}
595
596/*
597 * Retrieve the attributes of a file. We have fxp_stat which works
598 * on filenames, and fxp_fstat which works on open file handles.
599 */
600int fxp_stat(char *fname, struct fxp_attrs *attrs)
601{
602 struct sftp_packet *pktin, *pktout;
603 int id;
604
605 pktout = sftp_pkt_init(SSH_FXP_STAT);
606 sftp_pkt_adduint32(pktout, 0x678); /* request id */
607 sftp_pkt_addstring(pktout, fname);
608 sftp_send(pktout);
609 pktin = sftp_recv();
610 id = sftp_pkt_getuint32(pktin);
611 if (id != 0x678) {
612 fxp_internal_error("request ID mismatch\n");
613 return 0;
614 }
615
616 if (pktin->type == SSH_FXP_ATTRS) {
617 *attrs = sftp_pkt_getattrs(pktin);
618 return 1;
619 } else {
620 fxp_got_status(pktin);
621 return 0;
622 }
623}
624
625int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
626{
627 struct sftp_packet *pktin, *pktout;
628 int id;
629
630 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
631 sftp_pkt_adduint32(pktout, 0x678); /* request id */
9954aaa3 632 sftp_pkt_addstring_start(pktout);
d92624dc 633 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
634 sftp_send(pktout);
635 pktin = sftp_recv();
636 id = sftp_pkt_getuint32(pktin);
637 if (id != 0x678) {
638 fxp_internal_error("request ID mismatch\n");
639 return 0;
640 }
641
642 if (pktin->type == SSH_FXP_ATTRS) {
643 *attrs = sftp_pkt_getattrs(pktin);
644 return 1;
645 } else {
646 fxp_got_status(pktin);
647 return 0;
648 }
649}
650
651/*
652 * Set the attributes of a file.
653 */
654int fxp_setstat(char *fname, struct fxp_attrs attrs)
655{
656 struct sftp_packet *pktin, *pktout;
657 int id;
658
659 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
660 sftp_pkt_adduint32(pktout, 0x678); /* request id */
661 sftp_pkt_addstring(pktout, fname);
662 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 663 sftp_send(pktout);
664 pktin = sftp_recv();
665 id = sftp_pkt_getuint32(pktin);
666 if (id != 0x678) {
667 fxp_internal_error("request ID mismatch\n");
668 return 0;
669 }
670 id = fxp_got_status(pktin);
671 if (id != 1) {
672 return 0;
673 }
674 return 1;
675}
fd5e5847 676int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs)
677{
678 struct sftp_packet *pktin, *pktout;
679 int id;
680
681 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
682 sftp_pkt_adduint32(pktout, 0x678); /* request id */
683 sftp_pkt_addstring_start(pktout);
684 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
685 sftp_pkt_addattrs(pktout, attrs);
686 sftp_send(pktout);
687 pktin = sftp_recv();
688 id = sftp_pkt_getuint32(pktin);
689 if (id != 0x678) {
690 fxp_internal_error("request ID mismatch\n");
691 return 0;
692 }
693 id = fxp_got_status(pktin);
694 if (id != 1) {
695 return 0;
696 }
697 return 1;
698}
9954aaa3 699
4c7f0d61 700/*
701 * Read from a file. Returns the number of bytes read, or -1 on an
702 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
703 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
704 * error indicator. It might even depend on the SFTP server.)
705 */
32874aea 706int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
707 int len)
708{
4c7f0d61 709 struct sftp_packet *pktin, *pktout;
710 int id;
711
712 pktout = sftp_pkt_init(SSH_FXP_READ);
713 sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
f9e162aa 714 sftp_pkt_addstring_start(pktout);
715 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 716 sftp_pkt_adduint64(pktout, offset);
717 sftp_pkt_adduint32(pktout, len);
718 sftp_send(pktout);
719 pktin = sftp_recv();
720 id = sftp_pkt_getuint32(pktin);
721 if (id != 0xBCD) {
722 fxp_internal_error("request ID mismatch");
4a8fc3c4 723 return -1;
4c7f0d61 724 }
725 if (pktin->type == SSH_FXP_DATA) {
726 char *str;
727 int rlen;
728
729 sftp_pkt_getstring(pktin, &str, &rlen);
730
731 if (rlen > len || rlen < 0) {
732 fxp_internal_error("READ returned more bytes than requested");
733 return -1;
734 }
735
736 memcpy(buffer, str, rlen);
737 sfree(pktin);
738 return rlen;
739 } else {
740 fxp_got_status(pktin);
741 return -1;
742 }
743}
744
745/*
746 * Read from a directory.
747 */
32874aea 748struct fxp_names *fxp_readdir(struct fxp_handle *handle)
749{
4c7f0d61 750 struct sftp_packet *pktin, *pktout;
751 int id;
752
753 pktout = sftp_pkt_init(SSH_FXP_READDIR);
754 sftp_pkt_adduint32(pktout, 0xABC); /* request id */
f9e162aa 755 sftp_pkt_addstring_start(pktout);
756 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 757 sftp_send(pktout);
758 pktin = sftp_recv();
759 id = sftp_pkt_getuint32(pktin);
760 if (id != 0xABC) {
761 fxp_internal_error("request ID mismatch\n");
4a8fc3c4 762 return NULL;
4c7f0d61 763 }
764 if (pktin->type == SSH_FXP_NAME) {
765 struct fxp_names *ret;
766 int i;
767 ret = smalloc(sizeof(struct fxp_names));
768 ret->nnames = sftp_pkt_getuint32(pktin);
769 ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
770 for (i = 0; i < ret->nnames; i++) {
771 char *str;
772 int len;
773 sftp_pkt_getstring(pktin, &str, &len);
774 ret->names[i].filename = mkstr(str, len);
775 sftp_pkt_getstring(pktin, &str, &len);
776 ret->names[i].longname = mkstr(str, len);
777 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
778 }
779 return ret;
780 } else {
781 fxp_got_status(pktin);
782 return NULL;
783 }
784}
785
786/*
787 * Write to a file. Returns 0 on error, 1 on OK.
788 */
32874aea 789int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
790 int len)
791{
4c7f0d61 792 struct sftp_packet *pktin, *pktout;
793 int id;
794
795 pktout = sftp_pkt_init(SSH_FXP_WRITE);
796 sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
f9e162aa 797 sftp_pkt_addstring_start(pktout);
798 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 799 sftp_pkt_adduint64(pktout, offset);
800 sftp_pkt_addstring_start(pktout);
801 sftp_pkt_addstring_data(pktout, buffer, len);
802 sftp_send(pktout);
803 pktin = sftp_recv();
804 id = sftp_pkt_getuint32(pktin);
4a8fc3c4 805 if (id != 0xDCB) {
806 fxp_internal_error("request ID mismatch\n");
d8770b12 807 return 0;
4a8fc3c4 808 }
4c7f0d61 809 fxp_got_status(pktin);
810 return fxp_errtype == SSH_FX_OK;
811}
812
813/*
814 * Free up an fxp_names structure.
815 */
32874aea 816void fxp_free_names(struct fxp_names *names)
817{
4c7f0d61 818 int i;
819
820 for (i = 0; i < names->nnames; i++) {
821 sfree(names->names[i].filename);
822 sfree(names->names[i].longname);
823 }
824 sfree(names->names);
825 sfree(names);
826}