source: trunk/third/openssh/sftp-client.c @ 18759

Revision 18759, 25.7 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18758, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 2001,2002 Damien Miller.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25/* XXX: memleaks */
26/* XXX: signed vs unsigned */
27/* XXX: remove all logging, only return status codes */
28/* XXX: copy between two remote sites */
29
30#include "includes.h"
31RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm Exp $");
32
33#include "openbsd-compat/sys-queue.h"
34
35#include "buffer.h"
36#include "bufaux.h"
37#include "getput.h"
38#include "xmalloc.h"
39#include "log.h"
40#include "atomicio.h"
41
42#include "sftp.h"
43#include "sftp-common.h"
44#include "sftp-client.h"
45
46/* Minimum amount of data to read at at time */
47#define MIN_READ_SIZE   512
48
49struct sftp_conn {
50        int fd_in;
51        int fd_out;
52        u_int transfer_buflen;
53        u_int num_requests;
54        u_int version;
55        u_int msg_id;
56};
57
58static void
59send_msg(int fd, Buffer *m)
60{
61        int mlen = buffer_len(m);
62        int len;
63        Buffer oqueue;
64
65        buffer_init(&oqueue);
66        buffer_put_int(&oqueue, mlen);
67        buffer_append(&oqueue, buffer_ptr(m), mlen);
68        buffer_consume(m, mlen);
69
70        len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
71        if (len <= 0)
72                fatal("Couldn't send packet: %s", strerror(errno));
73
74        buffer_free(&oqueue);
75}
76
77static void
78get_msg(int fd, Buffer *m)
79{
80        u_int len, msg_len;
81        unsigned char buf[4096];
82
83        len = atomicio(read, fd, buf, 4);
84        if (len == 0)
85                fatal("Connection closed");
86        else if (len == -1)
87                fatal("Couldn't read packet: %s", strerror(errno));
88
89        msg_len = GET_32BIT(buf);
90        if (msg_len > 256 * 1024)
91                fatal("Received message too long %u", msg_len);
92
93        while (msg_len) {
94                len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
95                if (len == 0)
96                        fatal("Connection closed");
97                else if (len == -1)
98                        fatal("Couldn't read packet: %s", strerror(errno));
99
100                msg_len -= len;
101                buffer_append(m, buf, len);
102        }
103}
104
105static void
106send_string_request(int fd, u_int id, u_int code, char *s,
107    u_int len)
108{
109        Buffer msg;
110
111        buffer_init(&msg);
112        buffer_put_char(&msg, code);
113        buffer_put_int(&msg, id);
114        buffer_put_string(&msg, s, len);
115        send_msg(fd, &msg);
116        debug3("Sent message fd %d T:%u I:%u", fd, code, id);
117        buffer_free(&msg);
118}
119
120static void
121send_string_attrs_request(int fd, u_int id, u_int code, char *s,
122    u_int len, Attrib *a)
123{
124        Buffer msg;
125
126        buffer_init(&msg);
127        buffer_put_char(&msg, code);
128        buffer_put_int(&msg, id);
129        buffer_put_string(&msg, s, len);
130        encode_attrib(&msg, a);
131        send_msg(fd, &msg);
132        debug3("Sent message fd %d T:%u I:%u", fd, code, id);
133        buffer_free(&msg);
134}
135
136static u_int
137get_status(int fd, u_int expected_id)
138{
139        Buffer msg;
140        u_int type, id, status;
141
142        buffer_init(&msg);
143        get_msg(fd, &msg);
144        type = buffer_get_char(&msg);
145        id = buffer_get_int(&msg);
146
147        if (id != expected_id)
148                fatal("ID mismatch (%u != %u)", id, expected_id);
149        if (type != SSH2_FXP_STATUS)
150                fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
151                    SSH2_FXP_STATUS, type);
152
153        status = buffer_get_int(&msg);
154        buffer_free(&msg);
155
156        debug3("SSH2_FXP_STATUS %u", status);
157
158        return(status);
159}
160
161static char *
162get_handle(int fd, u_int expected_id, u_int *len)
163{
164        Buffer msg;
165        u_int type, id;
166        char *handle;
167
168        buffer_init(&msg);
169        get_msg(fd, &msg);
170        type = buffer_get_char(&msg);
171        id = buffer_get_int(&msg);
172
173        if (id != expected_id)
174                fatal("ID mismatch (%u != %u)", id, expected_id);
175        if (type == SSH2_FXP_STATUS) {
176                int status = buffer_get_int(&msg);
177
178                error("Couldn't get handle: %s", fx2txt(status));
179                return(NULL);
180        } else if (type != SSH2_FXP_HANDLE)
181                fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
182                    SSH2_FXP_HANDLE, type);
183
184        handle = buffer_get_string(&msg, len);
185        buffer_free(&msg);
186
187        return(handle);
188}
189
190static Attrib *
191get_decode_stat(int fd, u_int expected_id, int quiet)
192{
193        Buffer msg;
194        u_int type, id;
195        Attrib *a;
196
197        buffer_init(&msg);
198        get_msg(fd, &msg);
199
200        type = buffer_get_char(&msg);
201        id = buffer_get_int(&msg);
202
203        debug3("Received stat reply T:%u I:%u", type, id);
204        if (id != expected_id)
205                fatal("ID mismatch (%u != %u)", id, expected_id);
206        if (type == SSH2_FXP_STATUS) {
207                int status = buffer_get_int(&msg);
208
209                if (quiet)
210                        debug("Couldn't stat remote file: %s", fx2txt(status));
211                else
212                        error("Couldn't stat remote file: %s", fx2txt(status));
213                return(NULL);
214        } else if (type != SSH2_FXP_ATTRS) {
215                fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
216                    SSH2_FXP_ATTRS, type);
217        }
218        a = decode_attrib(&msg);
219        buffer_free(&msg);
220
221        return(a);
222}
223
224struct sftp_conn *
225do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
226{
227        u_int type;
228        int version;
229        Buffer msg;
230        struct sftp_conn *ret;
231
232        buffer_init(&msg);
233        buffer_put_char(&msg, SSH2_FXP_INIT);
234        buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
235        send_msg(fd_out, &msg);
236
237        buffer_clear(&msg);
238
239        get_msg(fd_in, &msg);
240
241        /* Expecting a VERSION reply */
242        if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
243                error("Invalid packet back from SSH2_FXP_INIT (type %u)",
244                    type);
245                buffer_free(&msg);
246                return(NULL);
247        }
248        version = buffer_get_int(&msg);
249
250        debug2("Remote version: %d", version);
251
252        /* Check for extensions */
253        while (buffer_len(&msg) > 0) {
254                char *name = buffer_get_string(&msg, NULL);
255                char *value = buffer_get_string(&msg, NULL);
256
257                debug2("Init extension: \"%s\"", name);
258                xfree(name);
259                xfree(value);
260        }
261
262        buffer_free(&msg);
263
264        ret = xmalloc(sizeof(*ret));
265        ret->fd_in = fd_in;
266        ret->fd_out = fd_out;
267        ret->transfer_buflen = transfer_buflen;
268        ret->num_requests = num_requests;
269        ret->version = version;
270        ret->msg_id = 1;
271
272        /* Some filexfer v.0 servers don't support large packets */
273        if (version == 0)
274                ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
275
276        return(ret);
277}
278
279u_int
280sftp_proto_version(struct sftp_conn *conn)
281{
282        return(conn->version);
283}
284
285int
286do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
287{
288        u_int id, status;
289        Buffer msg;
290
291        buffer_init(&msg);
292
293        id = conn->msg_id++;
294        buffer_put_char(&msg, SSH2_FXP_CLOSE);
295        buffer_put_int(&msg, id);
296        buffer_put_string(&msg, handle, handle_len);
297        send_msg(conn->fd_out, &msg);
298        debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
299
300        status = get_status(conn->fd_in, id);
301        if (status != SSH2_FX_OK)
302                error("Couldn't close file: %s", fx2txt(status));
303
304        buffer_free(&msg);
305
306        return(status);
307}
308
309
310static int
311do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
312    SFTP_DIRENT ***dir)
313{
314        Buffer msg;
315        u_int type, id, handle_len, i, expected_id, ents = 0;
316        char *handle;
317
318        id = conn->msg_id++;
319
320        buffer_init(&msg);
321        buffer_put_char(&msg, SSH2_FXP_OPENDIR);
322        buffer_put_int(&msg, id);
323        buffer_put_cstring(&msg, path);
324        send_msg(conn->fd_out, &msg);
325
326        buffer_clear(&msg);
327
328        handle = get_handle(conn->fd_in, id, &handle_len);
329        if (handle == NULL)
330                return(-1);
331
332        if (dir) {
333                ents = 0;
334                *dir = xmalloc(sizeof(**dir));
335                (*dir)[0] = NULL;
336        }
337
338        for (;;) {
339                int count;
340
341                id = expected_id = conn->msg_id++;
342
343                debug3("Sending SSH2_FXP_READDIR I:%u", id);
344
345                buffer_clear(&msg);
346                buffer_put_char(&msg, SSH2_FXP_READDIR);
347                buffer_put_int(&msg, id);
348                buffer_put_string(&msg, handle, handle_len);
349                send_msg(conn->fd_out, &msg);
350
351                buffer_clear(&msg);
352
353                get_msg(conn->fd_in, &msg);
354
355                type = buffer_get_char(&msg);
356                id = buffer_get_int(&msg);
357
358                debug3("Received reply T:%u I:%u", type, id);
359
360                if (id != expected_id)
361                        fatal("ID mismatch (%u != %u)", id, expected_id);
362
363                if (type == SSH2_FXP_STATUS) {
364                        int status = buffer_get_int(&msg);
365
366                        debug3("Received SSH2_FXP_STATUS %d", status);
367
368                        if (status == SSH2_FX_EOF) {
369                                break;
370                        } else {
371                                error("Couldn't read directory: %s",
372                                    fx2txt(status));
373                                do_close(conn, handle, handle_len);
374                                return(status);
375                        }
376                } else if (type != SSH2_FXP_NAME)
377                        fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
378                            SSH2_FXP_NAME, type);
379
380                count = buffer_get_int(&msg);
381                if (count == 0)
382                        break;
383                debug3("Received %d SSH2_FXP_NAME responses", count);
384                for (i = 0; i < count; i++) {
385                        char *filename, *longname;
386                        Attrib *a;
387
388                        filename = buffer_get_string(&msg, NULL);
389                        longname = buffer_get_string(&msg, NULL);
390                        a = decode_attrib(&msg);
391
392                        if (printflag)
393                                printf("%s\n", longname);
394
395                        if (dir) {
396                                *dir = xrealloc(*dir, sizeof(**dir) *
397                                    (ents + 2));
398                                (*dir)[ents] = xmalloc(sizeof(***dir));
399                                (*dir)[ents]->filename = xstrdup(filename);
400                                (*dir)[ents]->longname = xstrdup(longname);
401                                memcpy(&(*dir)[ents]->a, a, sizeof(*a));
402                                (*dir)[++ents] = NULL;
403                        }
404
405                        xfree(filename);
406                        xfree(longname);
407                }
408        }
409
410        buffer_free(&msg);
411        do_close(conn, handle, handle_len);
412        xfree(handle);
413
414        return(0);
415}
416
417int
418do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
419{
420        return(do_lsreaddir(conn, path, 0, dir));
421}
422
423void free_sftp_dirents(SFTP_DIRENT **s)
424{
425        int i;
426
427        for (i = 0; s[i]; i++) {
428                xfree(s[i]->filename);
429                xfree(s[i]->longname);
430                xfree(s[i]);
431        }
432        xfree(s);
433}
434
435int
436do_rm(struct sftp_conn *conn, char *path)
437{
438        u_int status, id;
439
440        debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
441
442        id = conn->msg_id++;
443        send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
444            strlen(path));
445        status = get_status(conn->fd_in, id);
446        if (status != SSH2_FX_OK)
447                error("Couldn't delete file: %s", fx2txt(status));
448        return(status);
449}
450
451int
452do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
453{
454        u_int status, id;
455
456        id = conn->msg_id++;
457        send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
458            strlen(path), a);
459
460        status = get_status(conn->fd_in, id);
461        if (status != SSH2_FX_OK)
462                error("Couldn't create directory: %s", fx2txt(status));
463
464        return(status);
465}
466
467int
468do_rmdir(struct sftp_conn *conn, char *path)
469{
470        u_int status, id;
471
472        id = conn->msg_id++;
473        send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
474            strlen(path));
475
476        status = get_status(conn->fd_in, id);
477        if (status != SSH2_FX_OK)
478                error("Couldn't remove directory: %s", fx2txt(status));
479
480        return(status);
481}
482
483Attrib *
484do_stat(struct sftp_conn *conn, char *path, int quiet)
485{
486        u_int id;
487
488        id = conn->msg_id++;
489
490        send_string_request(conn->fd_out, id,
491            conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
492            path, strlen(path));
493
494        return(get_decode_stat(conn->fd_in, id, quiet));
495}
496
497Attrib *
498do_lstat(struct sftp_conn *conn, char *path, int quiet)
499{
500        u_int id;
501
502        if (conn->version == 0) {
503                if (quiet)
504                        debug("Server version does not support lstat operation");
505                else
506                        log("Server version does not support lstat operation");
507                return(do_stat(conn, path, quiet));
508        }
509
510        id = conn->msg_id++;
511        send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
512            strlen(path));
513
514        return(get_decode_stat(conn->fd_in, id, quiet));
515}
516
517Attrib *
518do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
519{
520        u_int id;
521
522        id = conn->msg_id++;
523        send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
524            handle_len);
525
526        return(get_decode_stat(conn->fd_in, id, quiet));
527}
528
529int
530do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
531{
532        u_int status, id;
533
534        id = conn->msg_id++;
535        send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
536            strlen(path), a);
537
538        status = get_status(conn->fd_in, id);
539        if (status != SSH2_FX_OK)
540                error("Couldn't setstat on \"%s\": %s", path,
541                    fx2txt(status));
542
543        return(status);
544}
545
546int
547do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
548    Attrib *a)
549{
550        u_int status, id;
551
552        id = conn->msg_id++;
553        send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
554            handle_len, a);
555
556        status = get_status(conn->fd_in, id);
557        if (status != SSH2_FX_OK)
558                error("Couldn't fsetstat: %s", fx2txt(status));
559
560        return(status);
561}
562
563char *
564do_realpath(struct sftp_conn *conn, char *path)
565{
566        Buffer msg;
567        u_int type, expected_id, count, id;
568        char *filename, *longname;
569        Attrib *a;
570
571        expected_id = id = conn->msg_id++;
572        send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
573            strlen(path));
574
575        buffer_init(&msg);
576
577        get_msg(conn->fd_in, &msg);
578        type = buffer_get_char(&msg);
579        id = buffer_get_int(&msg);
580
581        if (id != expected_id)
582                fatal("ID mismatch (%u != %u)", id, expected_id);
583
584        if (type == SSH2_FXP_STATUS) {
585                u_int status = buffer_get_int(&msg);
586
587                error("Couldn't canonicalise: %s", fx2txt(status));
588                return(NULL);
589        } else if (type != SSH2_FXP_NAME)
590                fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
591                    SSH2_FXP_NAME, type);
592
593        count = buffer_get_int(&msg);
594        if (count != 1)
595                fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
596
597        filename = buffer_get_string(&msg, NULL);
598        longname = buffer_get_string(&msg, NULL);
599        a = decode_attrib(&msg);
600
601        debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
602
603        xfree(longname);
604
605        buffer_free(&msg);
606
607        return(filename);
608}
609
610int
611do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
612{
613        Buffer msg;
614        u_int status, id;
615
616        buffer_init(&msg);
617
618        /* Send rename request */
619        id = conn->msg_id++;
620        buffer_put_char(&msg, SSH2_FXP_RENAME);
621        buffer_put_int(&msg, id);
622        buffer_put_cstring(&msg, oldpath);
623        buffer_put_cstring(&msg, newpath);
624        send_msg(conn->fd_out, &msg);
625        debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
626            newpath);
627        buffer_free(&msg);
628
629        status = get_status(conn->fd_in, id);
630        if (status != SSH2_FX_OK)
631                error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
632                    newpath, fx2txt(status));
633
634        return(status);
635}
636
637int
638do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
639{
640        Buffer msg;
641        u_int status, id;
642
643        if (conn->version < 3) {
644                error("This server does not support the symlink operation");
645                return(SSH2_FX_OP_UNSUPPORTED);
646        }
647
648        buffer_init(&msg);
649
650        /* Send rename request */
651        id = conn->msg_id++;
652        buffer_put_char(&msg, SSH2_FXP_SYMLINK);
653        buffer_put_int(&msg, id);
654        buffer_put_cstring(&msg, oldpath);
655        buffer_put_cstring(&msg, newpath);
656        send_msg(conn->fd_out, &msg);
657        debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
658            newpath);
659        buffer_free(&msg);
660
661        status = get_status(conn->fd_in, id);
662        if (status != SSH2_FX_OK)
663                error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
664                    newpath, fx2txt(status));
665
666        return(status);
667}
668
669char *
670do_readlink(struct sftp_conn *conn, char *path)
671{
672        Buffer msg;
673        u_int type, expected_id, count, id;
674        char *filename, *longname;
675        Attrib *a;
676
677        expected_id = id = conn->msg_id++;
678        send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
679            strlen(path));
680
681        buffer_init(&msg);
682
683        get_msg(conn->fd_in, &msg);
684        type = buffer_get_char(&msg);
685        id = buffer_get_int(&msg);
686
687        if (id != expected_id)
688                fatal("ID mismatch (%u != %u)", id, expected_id);
689
690        if (type == SSH2_FXP_STATUS) {
691                u_int status = buffer_get_int(&msg);
692
693                error("Couldn't readlink: %s", fx2txt(status));
694                return(NULL);
695        } else if (type != SSH2_FXP_NAME)
696                fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
697                    SSH2_FXP_NAME, type);
698
699        count = buffer_get_int(&msg);
700        if (count != 1)
701                fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
702
703        filename = buffer_get_string(&msg, NULL);
704        longname = buffer_get_string(&msg, NULL);
705        a = decode_attrib(&msg);
706
707        debug3("SSH_FXP_READLINK %s -> %s", path, filename);
708
709        xfree(longname);
710
711        buffer_free(&msg);
712
713        return(filename);
714}
715
716static void
717send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
718    char *handle, u_int handle_len)
719{
720        Buffer msg;
721
722        buffer_init(&msg);
723        buffer_clear(&msg);
724        buffer_put_char(&msg, SSH2_FXP_READ);
725        buffer_put_int(&msg, id);
726        buffer_put_string(&msg, handle, handle_len);
727        buffer_put_int64(&msg, offset);
728        buffer_put_int(&msg, len);
729        send_msg(fd_out, &msg);
730        buffer_free(&msg);
731}
732
733int
734do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
735    int pflag)
736{
737        Attrib junk, *a;
738        Buffer msg;
739        char *handle;
740        int local_fd, status, num_req, max_req, write_error;
741        int read_error, write_errno;
742        u_int64_t offset, size;
743        u_int handle_len, mode, type, id, buflen;
744        struct request {
745                u_int id;
746                u_int len;
747                u_int64_t offset;
748                TAILQ_ENTRY(request) tq;
749        };
750        TAILQ_HEAD(reqhead, request) requests;
751        struct request *req;
752
753        TAILQ_INIT(&requests);
754
755        a = do_stat(conn, remote_path, 0);
756        if (a == NULL)
757                return(-1);
758
759        /* XXX: should we preserve set[ug]id? */
760        if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
761                mode = S_IWRITE | (a->perm & 0777);
762        else
763                mode = 0666;
764
765        if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
766            (a->perm & S_IFDIR)) {
767                error("Cannot download a directory: %s", remote_path);
768                return(-1);
769        }
770
771        if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
772                size = a->size;
773        else
774                size = 0;
775
776        buflen = conn->transfer_buflen;
777        buffer_init(&msg);
778
779        /* Send open request */
780        id = conn->msg_id++;
781        buffer_put_char(&msg, SSH2_FXP_OPEN);
782        buffer_put_int(&msg, id);
783        buffer_put_cstring(&msg, remote_path);
784        buffer_put_int(&msg, SSH2_FXF_READ);
785        attrib_clear(&junk); /* Send empty attributes */
786        encode_attrib(&msg, &junk);
787        send_msg(conn->fd_out, &msg);
788        debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
789
790        handle = get_handle(conn->fd_in, id, &handle_len);
791        if (handle == NULL) {
792                buffer_free(&msg);
793                return(-1);
794        }
795
796        local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
797        if (local_fd == -1) {
798                error("Couldn't open local file \"%s\" for writing: %s",
799                    local_path, strerror(errno));
800                buffer_free(&msg);
801                xfree(handle);
802                return(-1);
803        }
804
805        /* Read from remote and write to local */
806        write_error = read_error = write_errno = num_req = offset = 0;
807        max_req = 1;
808        while (num_req > 0 || max_req > 0) {
809                char *data;
810                u_int len;
811
812                /* Send some more requests */
813                while (num_req < max_req) {
814                        debug3("Request range %llu -> %llu (%d/%d)",
815                            (unsigned long long)offset,
816                            (unsigned long long)offset + buflen - 1,
817                            num_req, max_req);
818                        req = xmalloc(sizeof(*req));
819                        req->id = conn->msg_id++;
820                        req->len = buflen;
821                        req->offset = offset;
822                        offset += buflen;
823                        num_req++;
824                        TAILQ_INSERT_TAIL(&requests, req, tq);
825                        send_read_request(conn->fd_out, req->id, req->offset,
826                            req->len, handle, handle_len);
827                }
828
829                buffer_clear(&msg);
830                get_msg(conn->fd_in, &msg);
831                type = buffer_get_char(&msg);
832                id = buffer_get_int(&msg);
833                debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
834
835                /* Find the request in our queue */
836                for(req = TAILQ_FIRST(&requests);
837                    req != NULL && req->id != id;
838                    req = TAILQ_NEXT(req, tq))
839                        ;
840                if (req == NULL)
841                        fatal("Unexpected reply %u", id);
842
843                switch (type) {
844                case SSH2_FXP_STATUS:
845                        status = buffer_get_int(&msg);
846                        if (status != SSH2_FX_EOF)
847                                read_error = 1;
848                        max_req = 0;
849                        TAILQ_REMOVE(&requests, req, tq);
850                        xfree(req);
851                        num_req--;
852                        break;
853                case SSH2_FXP_DATA:
854                        data = buffer_get_string(&msg, &len);
855                        debug3("Received data %llu -> %llu",
856                            (unsigned long long)req->offset,
857                            (unsigned long long)req->offset + len - 1);
858                        if (len > req->len)
859                                fatal("Received more data than asked for "
860                                      "%u > %u", len, req->len);
861                        if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
862                             atomicio(write, local_fd, data, len) != len) &&
863                            !write_error) {
864                                write_errno = errno;
865                                write_error = 1;
866                                max_req = 0;
867                        }
868                        xfree(data);
869
870                        if (len == req->len) {
871                                TAILQ_REMOVE(&requests, req, tq);
872                                xfree(req);
873                                num_req--;
874                        } else {
875                                /* Resend the request for the missing data */
876                                debug3("Short data block, re-requesting "
877                                    "%llu -> %llu (%2d)",
878                                    (unsigned long long)req->offset + len,
879                                    (unsigned long long)req->offset +
880                                    req->len - 1, num_req);
881                                req->id = conn->msg_id++;
882                                req->len -= len;
883                                req->offset += len;
884                                send_read_request(conn->fd_out, req->id,
885                                    req->offset, req->len, handle, handle_len);
886                                /* Reduce the request size */
887                                if (len < buflen)
888                                        buflen = MAX(MIN_READ_SIZE, len);
889                        }
890                        if (max_req > 0) { /* max_req = 0 iff EOF received */
891                                if (size > 0 && offset > size) {
892                                        /* Only one request at a time
893                                         * after the expected EOF */
894                                        debug3("Finish at %llu (%2d)",
895                                            (unsigned long long)offset,
896                                            num_req);
897                                        max_req = 1;
898                                }
899                                else if (max_req < conn->num_requests + 1) {
900                                        ++max_req;
901                                }
902                        }
903                        break;
904                default:
905                        fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
906                            SSH2_FXP_DATA, type);
907                }
908        }
909
910        /* Sanity check */
911        if (TAILQ_FIRST(&requests) != NULL)
912                fatal("Transfer complete, but requests still in queue");
913
914        if (read_error) {
915                error("Couldn't read from remote file \"%s\" : %s",
916                    remote_path, fx2txt(status));
917                do_close(conn, handle, handle_len);
918        } else if (write_error) {
919                error("Couldn't write to \"%s\": %s", local_path,
920                    strerror(write_errno));
921                status = -1;
922                do_close(conn, handle, handle_len);
923        } else {
924                status = do_close(conn, handle, handle_len);
925
926                /* Override umask and utimes if asked */
927#ifdef HAVE_FCHMOD
928                if (pflag && fchmod(local_fd, mode) == -1)
929#else
930                if (pflag && chmod(local_path, mode) == -1)
931#endif /* HAVE_FCHMOD */
932                        error("Couldn't set mode on \"%s\": %s", local_path,
933                              strerror(errno));
934                if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
935                        struct timeval tv[2];
936                        tv[0].tv_sec = a->atime;
937                        tv[1].tv_sec = a->mtime;
938                        tv[0].tv_usec = tv[1].tv_usec = 0;
939                        if (utimes(local_path, tv) == -1)
940                                error("Can't set times on \"%s\": %s",
941                                      local_path, strerror(errno));
942                }
943        }
944        close(local_fd);
945        buffer_free(&msg);
946        xfree(handle);
947
948        return(status);
949}
950
951int
952do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
953    int pflag)
954{
955        int local_fd, status;
956        u_int handle_len, id, type;
957        u_int64_t offset;
958        char *handle, *data;
959        Buffer msg;
960        struct stat sb;
961        Attrib a;
962        u_int32_t startid;
963        u_int32_t ackid;
964        struct outstanding_ack {
965                u_int id;
966                u_int len;
967                u_int64_t offset;
968                TAILQ_ENTRY(outstanding_ack) tq;
969        };
970        TAILQ_HEAD(ackhead, outstanding_ack) acks;
971        struct outstanding_ack *ack;
972
973        TAILQ_INIT(&acks);
974
975        if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
976                error("Couldn't open local file \"%s\" for reading: %s",
977                    local_path, strerror(errno));
978                return(-1);
979        }
980        if (fstat(local_fd, &sb) == -1) {
981                error("Couldn't fstat local file \"%s\": %s",
982                    local_path, strerror(errno));
983                close(local_fd);
984                return(-1);
985        }
986        stat_to_attrib(&sb, &a);
987
988        a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
989        a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
990        a.perm &= 0777;
991        if (!pflag)
992                a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
993
994        buffer_init(&msg);
995
996        /* Send open request */
997        id = conn->msg_id++;
998        buffer_put_char(&msg, SSH2_FXP_OPEN);
999        buffer_put_int(&msg, id);
1000        buffer_put_cstring(&msg, remote_path);
1001        buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1002        encode_attrib(&msg, &a);
1003        send_msg(conn->fd_out, &msg);
1004        debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1005
1006        buffer_clear(&msg);
1007
1008        handle = get_handle(conn->fd_in, id, &handle_len);
1009        if (handle == NULL) {
1010                close(local_fd);
1011                buffer_free(&msg);
1012                return(-1);
1013        }
1014
1015        startid = ackid = id + 1;
1016        data = xmalloc(conn->transfer_buflen);
1017
1018        /* Read from local and write to remote */
1019        offset = 0;
1020        for (;;) {
1021                int len;
1022
1023                /*
1024                 * Can't use atomicio here because it returns 0 on EOF, thus losing
1025                 * the last block of the file
1026                 */
1027                do
1028                        len = read(local_fd, data, conn->transfer_buflen);
1029                while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1030
1031                if (len == -1)
1032                        fatal("Couldn't read from \"%s\": %s", local_path,
1033                            strerror(errno));
1034
1035                if (len != 0) {
1036                        ack = xmalloc(sizeof(*ack));
1037                        ack->id = ++id;
1038                        ack->offset = offset;
1039                        ack->len = len;
1040                        TAILQ_INSERT_TAIL(&acks, ack, tq);
1041
1042                        buffer_clear(&msg);
1043                        buffer_put_char(&msg, SSH2_FXP_WRITE);
1044                        buffer_put_int(&msg, ack->id);
1045                        buffer_put_string(&msg, handle, handle_len);
1046                        buffer_put_int64(&msg, offset);
1047                        buffer_put_string(&msg, data, len);
1048                        send_msg(conn->fd_out, &msg);
1049                        debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1050                               id, (unsigned long long)offset, len);
1051                } else if (TAILQ_FIRST(&acks) == NULL)
1052                        break;
1053
1054                if (ack == NULL)
1055                        fatal("Unexpected ACK %u", id);
1056
1057                if (id == startid || len == 0 ||
1058                    id - ackid >= conn->num_requests) {
1059                        u_int r_id;
1060
1061                        buffer_clear(&msg);
1062                        get_msg(conn->fd_in, &msg);
1063                        type = buffer_get_char(&msg);
1064                        r_id = buffer_get_int(&msg);
1065
1066                        if (type != SSH2_FXP_STATUS)
1067                                fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1068                                    "got %d", SSH2_FXP_STATUS, type);
1069
1070                        status = buffer_get_int(&msg);
1071                        debug3("SSH2_FXP_STATUS %d", status);
1072
1073                        /* Find the request in our queue */
1074                        for(ack = TAILQ_FIRST(&acks);
1075                            ack != NULL && ack->id != r_id;
1076                            ack = TAILQ_NEXT(ack, tq))
1077                                ;
1078                        if (ack == NULL)
1079                                fatal("Can't find request for ID %u", r_id);
1080                        TAILQ_REMOVE(&acks, ack, tq);
1081
1082                        if (status != SSH2_FX_OK) {
1083                                error("Couldn't write to remote file \"%s\": %s",
1084                                      remote_path, fx2txt(status));
1085                                do_close(conn, handle, handle_len);
1086                                close(local_fd);
1087                                goto done;
1088                        }
1089                        debug3("In write loop, ack for %u %u bytes at %llu",
1090                           ack->id, ack->len, (unsigned long long)ack->offset);
1091                        ++ackid;
1092                        xfree(ack);
1093                }
1094                offset += len;
1095        }
1096        xfree(data);
1097
1098        if (close(local_fd) == -1) {
1099                error("Couldn't close local file \"%s\": %s", local_path,
1100                    strerror(errno));
1101                do_close(conn, handle, handle_len);
1102                status = -1;
1103                goto done;
1104        }
1105
1106        /* Override umask and utimes if asked */
1107        if (pflag)
1108                do_fsetstat(conn, handle, handle_len, &a);
1109
1110        status = do_close(conn, handle, handle_len);
1111
1112done:
1113        xfree(handle);
1114        buffer_free(&msg);
1115        return(status);
1116}
Note: See TracBrowser for help on using the repository browser.