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