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