]> andersk Git - openssh.git/blob - sftp-client.c
- itojun@cvs.openbsd.org 2002/03/11 03:19:53
[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.27 2002/03/11 03:19:53 itojun 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                 return(-1);
803         }
804
805         local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
806         if (local_fd == -1) {
807                 error("Couldn't open local file \"%s\" for writing: %s",
808                     local_path, strerror(errno));
809                 buffer_free(&msg);
810                 xfree(handle);
811                 return(-1);
812         }
813
814         /* Read from remote and write to local */
815         write_error = read_error = write_errno = num_req = offset = 0;
816         max_req = 1;
817         while (num_req > 0 || max_req > 0) {
818                 char *data;
819                 u_int len;
820
821                 /* Send some more requests */
822                 while (num_req < max_req) {
823                         debug3("Request range %llu -> %llu (%d/%d)", 
824                             (unsigned long long)offset,
825                             (unsigned long long)offset + buflen - 1,
826                             num_req, max_req);
827                         req = xmalloc(sizeof(*req));
828                         req->id = conn->msg_id++;
829                         req->len = buflen;
830                         req->offset = offset;
831                         offset += buflen;
832                         num_req++;
833                         TAILQ_INSERT_TAIL(&requests, req, tq);
834                         send_read_request(conn->fd_out, req->id, req->offset, 
835                             req->len, handle, handle_len);
836                 }
837
838                 buffer_clear(&msg);
839                 get_msg(conn->fd_in, &msg);
840                 type = buffer_get_char(&msg);
841                 id = buffer_get_int(&msg);
842                 debug3("Received reply T:%d I:%d R:%d", type, id, max_req);
843
844                 /* Find the request in our queue */
845                 for(req = TAILQ_FIRST(&requests);
846                     req != NULL && req->id != id;
847                     req = TAILQ_NEXT(req, tq))
848                         ;
849                 if (req == NULL)
850                         fatal("Unexpected reply %u", id);
851
852                 switch (type) {
853                 case SSH2_FXP_STATUS:
854                         status = buffer_get_int(&msg);
855                         if (status != SSH2_FX_EOF)
856                                 read_error = 1;
857                         max_req = 0;
858                         TAILQ_REMOVE(&requests, req, tq);
859                         xfree(req);
860                         num_req--;
861                         break;
862                 case SSH2_FXP_DATA:
863                         data = buffer_get_string(&msg, &len);
864                         debug3("Received data %llu -> %llu",
865                             (unsigned long long)req->offset, 
866                             (unsigned long long)req->offset + len - 1);
867                         if (len > req->len)
868                                 fatal("Received more data than asked for "
869                                       "%d > %d", len, req->len);
870                         if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
871                              atomicio(write, local_fd, data, len) != len) &&
872                             !write_error) {
873                                 write_errno = errno;
874                                 write_error = 1;
875                                 max_req = 0;
876                         }
877                         xfree(data);
878
879                         if (len == req->len) {
880                                 TAILQ_REMOVE(&requests, req, tq);
881                                 xfree(req);
882                                 num_req--;
883                         } else {
884                                 /* Resend the request for the missing data */
885                                 debug3("Short data block, re-requesting "
886                                     "%llu -> %llu (%2d)",
887                                     (unsigned long long)req->offset + len, 
888                                     (unsigned long long)req->offset +
889                                     req->len - 1, num_req);
890                                 req->id = conn->msg_id++;
891                                 req->len -= len;
892                                 req->offset += len;
893                                 send_read_request(conn->fd_out, req->id, 
894                                     req->offset, req->len, handle, handle_len);
895                                 /* Reduce the request size */
896                                 if (len < buflen)
897                                         buflen = MAX(MIN_READ_SIZE, len);
898                         }
899                         if (max_req > 0) { /* max_req = 0 iff EOF received */
900                                 if (size > 0 && offset > size) {
901                                         /* Only one request at a time
902                                          * after the expected EOF */
903                                         debug3("Finish at %llu (%2d)",
904                                             (unsigned long long)offset,
905                                             num_req);
906                                         max_req = 1;
907                                 }
908                                 else if (max_req < conn->num_requests + 1) {
909                                         ++max_req;
910                                 }
911                         }
912                         break;
913                 default:
914                         fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
915                             SSH2_FXP_DATA, type);
916                 }
917         }
918
919         /* Sanity check */
920         if (TAILQ_FIRST(&requests) != NULL)
921                 fatal("Transfer complete, but requests still in queue");
922
923         if (read_error) {
924                 error("Couldn't read from remote file \"%s\" : %s", 
925                     remote_path, fx2txt(status));
926                 do_close(conn, handle, handle_len);
927         } else if (write_error) {
928                 error("Couldn't write to \"%s\": %s", local_path,
929                     strerror(write_errno));
930                 status = -1;
931                 do_close(conn, handle, handle_len);
932         } else {
933                 status = do_close(conn, handle, handle_len);
934
935                 /* Override umask and utimes if asked */
936 #ifdef HAVE_FCHMOD
937                 if (pflag && fchmod(local_fd, mode) == -1)
938 #else 
939                 if (pflag && chmod(local_path, mode) == -1)
940 #endif /* HAVE_FCHMOD */
941                         error("Couldn't set mode on \"%s\": %s", local_path,
942                               strerror(errno));
943                 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
944                         struct timeval tv[2];
945                         tv[0].tv_sec = a->atime;
946                         tv[1].tv_sec = a->mtime;
947                         tv[0].tv_usec = tv[1].tv_usec = 0;
948                         if (utimes(local_path, tv) == -1)
949                                 error("Can't set times on \"%s\": %s",
950                                       local_path, strerror(errno));
951                 }
952         }
953         close(local_fd);
954         buffer_free(&msg);
955         xfree(handle);
956
957         return(status);
958 }
959
960 int
961 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
962     int pflag)
963 {
964         int local_fd, status;
965         u_int handle_len, id, type;
966         u_int64_t offset;
967         char *handle, *data;
968         Buffer msg;
969         struct stat sb;
970         Attrib a;
971         u_int32_t startid;
972         u_int32_t ackid;
973         struct outstanding_ack {
974                 u_int id;
975                 u_int len;
976                 u_int64_t offset;
977                 TAILQ_ENTRY(outstanding_ack) tq; 
978         };
979         TAILQ_HEAD(ackhead, outstanding_ack) acks;
980         struct outstanding_ack *ack;
981
982         TAILQ_INIT(&acks);
983
984         if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
985                 error("Couldn't open local file \"%s\" for reading: %s",
986                     local_path, strerror(errno));
987                 return(-1);
988         }
989         if (fstat(local_fd, &sb) == -1) {
990                 error("Couldn't fstat local file \"%s\": %s",
991                     local_path, strerror(errno));
992                 close(local_fd);
993                 return(-1);
994         }
995         stat_to_attrib(&sb, &a);
996
997         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
998         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
999         a.perm &= 0777;
1000         if (!pflag)
1001                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1002
1003         buffer_init(&msg);
1004
1005         /* Send open request */
1006         id = conn->msg_id++;
1007         buffer_put_char(&msg, SSH2_FXP_OPEN);
1008         buffer_put_int(&msg, id);
1009         buffer_put_cstring(&msg, remote_path);
1010         buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1011         encode_attrib(&msg, &a);
1012         send_msg(conn->fd_out, &msg);
1013         debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
1014
1015         buffer_clear(&msg);
1016
1017         handle = get_handle(conn->fd_in, id, &handle_len);
1018         if (handle == NULL) {
1019                 close(local_fd);
1020                 buffer_free(&msg);
1021                 return(-1);
1022         }
1023
1024         startid = ackid = id + 1;
1025         data = xmalloc(conn->transfer_buflen);
1026
1027         /* Read from local and write to remote */
1028         offset = 0;
1029         for (;;) {
1030                 int len;
1031
1032                 /*
1033                  * Can't use atomicio here because it returns 0 on EOF, thus losing
1034                  * the last block of the file
1035                  */
1036                 do
1037                         len = read(local_fd, data, conn->transfer_buflen);
1038                 while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1039
1040                 if (len == -1)
1041                         fatal("Couldn't read from \"%s\": %s", local_path,
1042                             strerror(errno));
1043
1044                 if (len != 0) {
1045                         ack = xmalloc(sizeof(*ack));
1046                         ack->id = ++id;
1047                         ack->offset = offset;
1048                         ack->len = len;
1049                         TAILQ_INSERT_TAIL(&acks, ack, tq);
1050
1051                         buffer_clear(&msg);
1052                         buffer_put_char(&msg, SSH2_FXP_WRITE);
1053                         buffer_put_int(&msg, ack->id);
1054                         buffer_put_string(&msg, handle, handle_len);
1055                         buffer_put_int64(&msg, offset);
1056                         buffer_put_string(&msg, data, len);
1057                         send_msg(conn->fd_out, &msg);
1058                         debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
1059                                id, (unsigned long long)offset, len);
1060                 } else if (TAILQ_FIRST(&acks) == NULL)
1061                         break;
1062
1063                 if (ack == NULL)
1064                         fatal("Unexpected ACK %u", id);
1065
1066                 if (id == startid || len == 0 || 
1067                     id - ackid >= conn->num_requests) {
1068                         buffer_clear(&msg);
1069                         get_msg(conn->fd_in, &msg);
1070                         type = buffer_get_char(&msg);
1071                         id = buffer_get_int(&msg);
1072
1073                         if (type != SSH2_FXP_STATUS)
1074                                 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1075                                     "got %d", SSH2_FXP_STATUS, type);
1076
1077                         status = buffer_get_int(&msg);
1078                         debug3("SSH2_FXP_STATUS %d", status);
1079
1080                         /* Find the request in our queue */
1081                         for(ack = TAILQ_FIRST(&acks);
1082                             ack != NULL && ack->id != id;
1083                             ack = TAILQ_NEXT(ack, tq))
1084                                 ;
1085                         if (ack == NULL)
1086                                 fatal("Can't find request for ID %d", id);
1087                         TAILQ_REMOVE(&acks, ack, tq);
1088
1089                         if (status != SSH2_FX_OK) {
1090                                 error("Couldn't write to remote file \"%s\": %s",
1091                                       remote_path, fx2txt(status));
1092                                 do_close(conn, handle, handle_len);
1093                                 close(local_fd);
1094                                 goto done;
1095                         }
1096                         debug3("In write loop, ack for %u %d bytes at %llu", 
1097                            ack->id, ack->len, (unsigned long long)ack->offset);
1098                         ++ackid;
1099                         free(ack);
1100                 }
1101                 offset += len;
1102         }
1103         xfree(data);
1104
1105         if (close(local_fd) == -1) {
1106                 error("Couldn't close local file \"%s\": %s", local_path,
1107                     strerror(errno));
1108                 do_close(conn, handle, handle_len);
1109                 status = -1;
1110                 goto done;
1111         }
1112
1113         /* Override umask and utimes if asked */
1114         if (pflag)
1115                 do_fsetstat(conn, handle, handle_len, &a);
1116
1117         status = do_close(conn, handle, handle_len);
1118
1119 done:
1120         xfree(handle);
1121         buffer_free(&msg);
1122         return(status);
1123 }
This page took 0.155353 seconds and 5 git commands to generate.