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