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