]> andersk Git - openssh.git/blob - sftp-client.c
- djm@cvs.openbsd.org 2006/04/16 00:54:10
[openssh.git] / sftp-client.c
1 /* $OpenBSD: sftp-client.c,v 1.65 2006/04/16 00:54:10 djm Exp $ */
2 /*
3  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* XXX: memleaks */
19 /* XXX: signed vs unsigned */
20 /* XXX: remove all logging, only return status codes */
21 /* XXX: copy between two remote sites */
22
23 #include "includes.h"
24
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_STAT_H
27 # include <sys/stat.h>
28 #endif
29 # include <signal.h>
30
31 #include "openbsd-compat/sys-queue.h"
32
33 #include "buffer.h"
34 #include "bufaux.h"
35 #include "xmalloc.h"
36 #include "log.h"
37 #include "atomicio.h"
38 #include "progressmeter.h"
39 #include "misc.h"
40
41 #include "sftp.h"
42 #include "sftp-common.h"
43 #include "sftp-client.h"
44
45 extern volatile sig_atomic_t interrupted;
46 extern int showprogress;
47
48 /* Minimum amount of data to read at a time */
49 #define MIN_READ_SIZE   512
50
51 struct sftp_conn {
52         int fd_in;
53         int fd_out;
54         u_int transfer_buflen;
55         u_int num_requests;
56         u_int version;
57         u_int msg_id;
58 };
59
60 static void
61 send_msg(int fd, Buffer *m)
62 {
63         u_char mlen[4];
64         struct iovec iov[2];
65
66         if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
67                 fatal("Outbound message too long %u", buffer_len(m));
68
69         /* Send length first */
70         put_u32(mlen, buffer_len(m));
71         iov[0].iov_base = mlen;
72         iov[0].iov_len = sizeof(mlen);
73         iov[1].iov_base = buffer_ptr(m);
74         iov[1].iov_len = buffer_len(m);
75         
76         if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
77                 fatal("Couldn't send packet: %s", strerror(errno));
78
79         buffer_clear(m);
80 }
81
82 static void
83 get_msg(int fd, Buffer *m)
84 {
85         u_int msg_len;
86
87         buffer_append_space(m, 4);
88         if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
89                 if (errno == EPIPE)
90                         fatal("Connection closed");
91                 else
92                         fatal("Couldn't read packet: %s", strerror(errno));
93         }
94
95         msg_len = buffer_get_int(m);
96         if (msg_len > SFTP_MAX_MSG_LENGTH)
97                 fatal("Received message too long %u", msg_len);
98
99         buffer_append_space(m, msg_len);
100         if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
101                 if (errno == EPIPE)
102                         fatal("Connection closed");
103                 else
104                         fatal("Read packet: %s", strerror(errno));
105         }
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                 buffer_free(&msg);
183                 return(NULL);
184         } else if (type != SSH2_FXP_HANDLE)
185                 fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
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:%u I:%u", type, id);
208         if (id != expected_id)
209                 fatal("ID mismatch (%u != %u)", 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                 buffer_free(&msg);
218                 return(NULL);
219         } else if (type != SSH2_FXP_ATTRS) {
220                 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
221                     SSH2_FXP_ATTRS, type);
222         }
223         a = decode_attrib(&msg);
224         buffer_free(&msg);
225
226         return(a);
227 }
228
229 struct sftp_conn *
230 do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
231 {
232         u_int type;
233         int version;
234         Buffer msg;
235         struct sftp_conn *ret;
236
237         buffer_init(&msg);
238         buffer_put_char(&msg, SSH2_FXP_INIT);
239         buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
240         send_msg(fd_out, &msg);
241
242         buffer_clear(&msg);
243
244         get_msg(fd_in, &msg);
245
246         /* Expecting a VERSION reply */
247         if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
248                 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
249                     type);
250                 buffer_free(&msg);
251                 return(NULL);
252         }
253         version = buffer_get_int(&msg);
254
255         debug2("Remote version: %d", version);
256
257         /* Check for extensions */
258         while (buffer_len(&msg) > 0) {
259                 char *name = buffer_get_string(&msg, NULL);
260                 char *value = buffer_get_string(&msg, NULL);
261
262                 debug2("Init extension: \"%s\"", name);
263                 xfree(name);
264                 xfree(value);
265         }
266
267         buffer_free(&msg);
268
269         ret = xmalloc(sizeof(*ret));
270         ret->fd_in = fd_in;
271         ret->fd_out = fd_out;
272         ret->transfer_buflen = transfer_buflen;
273         ret->num_requests = num_requests;
274         ret->version = version;
275         ret->msg_id = 1;
276
277         /* Some filexfer v.0 servers don't support large packets */
278         if (version == 0)
279                 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
280
281         return(ret);
282 }
283
284 u_int
285 sftp_proto_version(struct sftp_conn *conn)
286 {
287         return(conn->version);
288 }
289
290 int
291 do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
292 {
293         u_int id, status;
294         Buffer msg;
295
296         buffer_init(&msg);
297
298         id = conn->msg_id++;
299         buffer_put_char(&msg, SSH2_FXP_CLOSE);
300         buffer_put_int(&msg, id);
301         buffer_put_string(&msg, handle, handle_len);
302         send_msg(conn->fd_out, &msg);
303         debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
304
305         status = get_status(conn->fd_in, id);
306         if (status != SSH2_FX_OK)
307                 error("Couldn't close file: %s", fx2txt(status));
308
309         buffer_free(&msg);
310
311         return(status);
312 }
313
314
315 static int
316 do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
317     SFTP_DIRENT ***dir)
318 {
319         Buffer msg;
320         u_int count, type, id, handle_len, i, expected_id, ents = 0;
321         char *handle;
322
323         id = conn->msg_id++;
324
325         buffer_init(&msg);
326         buffer_put_char(&msg, SSH2_FXP_OPENDIR);
327         buffer_put_int(&msg, id);
328         buffer_put_cstring(&msg, path);
329         send_msg(conn->fd_out, &msg);
330
331         buffer_clear(&msg);
332
333         handle = get_handle(conn->fd_in, id, &handle_len);
334         if (handle == NULL)
335                 return(-1);
336
337         if (dir) {
338                 ents = 0;
339                 *dir = xmalloc(sizeof(**dir));
340                 (*dir)[0] = NULL;
341         }
342
343         for (; !interrupted;) {
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, ents + 2, sizeof(**dir));
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         /* Don't return partial matches on interrupt */
418         if (interrupted && dir != NULL && *dir != NULL) {
419                 free_sftp_dirents(*dir);
420                 *dir = xmalloc(sizeof(**dir));
421                 **dir = NULL;
422         }
423
424         return(0);
425 }
426
427 int
428 do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
429 {
430         return(do_lsreaddir(conn, path, 0, dir));
431 }
432
433 void free_sftp_dirents(SFTP_DIRENT **s)
434 {
435         int i;
436
437         for (i = 0; s[i]; i++) {
438                 xfree(s[i]->filename);
439                 xfree(s[i]->longname);
440                 xfree(s[i]);
441         }
442         xfree(s);
443 }
444
445 int
446 do_rm(struct sftp_conn *conn, char *path)
447 {
448         u_int status, id;
449
450         debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
451
452         id = conn->msg_id++;
453         send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
454             strlen(path));
455         status = get_status(conn->fd_in, id);
456         if (status != SSH2_FX_OK)
457                 error("Couldn't delete file: %s", fx2txt(status));
458         return(status);
459 }
460
461 int
462 do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
463 {
464         u_int status, id;
465
466         id = conn->msg_id++;
467         send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
468             strlen(path), a);
469
470         status = get_status(conn->fd_in, id);
471         if (status != SSH2_FX_OK)
472                 error("Couldn't create directory: %s", fx2txt(status));
473
474         return(status);
475 }
476
477 int
478 do_rmdir(struct sftp_conn *conn, char *path)
479 {
480         u_int status, id;
481
482         id = conn->msg_id++;
483         send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
484             strlen(path));
485
486         status = get_status(conn->fd_in, id);
487         if (status != SSH2_FX_OK)
488                 error("Couldn't remove directory: %s", fx2txt(status));
489
490         return(status);
491 }
492
493 Attrib *
494 do_stat(struct sftp_conn *conn, char *path, int quiet)
495 {
496         u_int id;
497
498         id = conn->msg_id++;
499
500         send_string_request(conn->fd_out, id,
501             conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
502             path, strlen(path));
503
504         return(get_decode_stat(conn->fd_in, id, quiet));
505 }
506
507 Attrib *
508 do_lstat(struct sftp_conn *conn, char *path, int quiet)
509 {
510         u_int id;
511
512         if (conn->version == 0) {
513                 if (quiet)
514                         debug("Server version does not support lstat operation");
515                 else
516                         logit("Server version does not support lstat operation");
517                 return(do_stat(conn, path, quiet));
518         }
519
520         id = conn->msg_id++;
521         send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
522             strlen(path));
523
524         return(get_decode_stat(conn->fd_in, id, quiet));
525 }
526
527 Attrib *
528 do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
529 {
530         u_int id;
531
532         id = conn->msg_id++;
533         send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
534             handle_len);
535
536         return(get_decode_stat(conn->fd_in, id, quiet));
537 }
538
539 int
540 do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
541 {
542         u_int status, id;
543
544         id = conn->msg_id++;
545         send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
546             strlen(path), a);
547
548         status = get_status(conn->fd_in, id);
549         if (status != SSH2_FX_OK)
550                 error("Couldn't setstat on \"%s\": %s", path,
551                     fx2txt(status));
552
553         return(status);
554 }
555
556 int
557 do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
558     Attrib *a)
559 {
560         u_int status, id;
561
562         id = conn->msg_id++;
563         send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
564             handle_len, a);
565
566         status = get_status(conn->fd_in, id);
567         if (status != SSH2_FX_OK)
568                 error("Couldn't fsetstat: %s", fx2txt(status));
569
570         return(status);
571 }
572
573 char *
574 do_realpath(struct sftp_conn *conn, char *path)
575 {
576         Buffer msg;
577         u_int type, expected_id, count, id;
578         char *filename, *longname;
579         Attrib *a;
580
581         expected_id = id = conn->msg_id++;
582         send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
583             strlen(path));
584
585         buffer_init(&msg);
586
587         get_msg(conn->fd_in, &msg);
588         type = buffer_get_char(&msg);
589         id = buffer_get_int(&msg);
590
591         if (id != expected_id)
592                 fatal("ID mismatch (%u != %u)", id, expected_id);
593
594         if (type == SSH2_FXP_STATUS) {
595                 u_int status = buffer_get_int(&msg);
596
597                 error("Couldn't canonicalise: %s", fx2txt(status));
598                 return(NULL);
599         } else if (type != SSH2_FXP_NAME)
600                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
601                     SSH2_FXP_NAME, type);
602
603         count = buffer_get_int(&msg);
604         if (count != 1)
605                 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
606
607         filename = buffer_get_string(&msg, NULL);
608         longname = buffer_get_string(&msg, NULL);
609         a = decode_attrib(&msg);
610
611         debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
612
613         xfree(longname);
614
615         buffer_free(&msg);
616
617         return(filename);
618 }
619
620 int
621 do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
622 {
623         Buffer msg;
624         u_int status, id;
625
626         buffer_init(&msg);
627
628         /* Send rename request */
629         id = conn->msg_id++;
630         buffer_put_char(&msg, SSH2_FXP_RENAME);
631         buffer_put_int(&msg, id);
632         buffer_put_cstring(&msg, oldpath);
633         buffer_put_cstring(&msg, newpath);
634         send_msg(conn->fd_out, &msg);
635         debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
636             newpath);
637         buffer_free(&msg);
638
639         status = get_status(conn->fd_in, id);
640         if (status != SSH2_FX_OK)
641                 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
642                     newpath, fx2txt(status));
643
644         return(status);
645 }
646
647 int
648 do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
649 {
650         Buffer msg;
651         u_int status, id;
652
653         if (conn->version < 3) {
654                 error("This server does not support the symlink operation");
655                 return(SSH2_FX_OP_UNSUPPORTED);
656         }
657
658         buffer_init(&msg);
659
660         /* Send symlink request */
661         id = conn->msg_id++;
662         buffer_put_char(&msg, SSH2_FXP_SYMLINK);
663         buffer_put_int(&msg, id);
664         buffer_put_cstring(&msg, oldpath);
665         buffer_put_cstring(&msg, newpath);
666         send_msg(conn->fd_out, &msg);
667         debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
668             newpath);
669         buffer_free(&msg);
670
671         status = get_status(conn->fd_in, id);
672         if (status != SSH2_FX_OK)
673                 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
674                     newpath, fx2txt(status));
675
676         return(status);
677 }
678
679 char *
680 do_readlink(struct sftp_conn *conn, char *path)
681 {
682         Buffer msg;
683         u_int type, expected_id, count, id;
684         char *filename, *longname;
685         Attrib *a;
686
687         expected_id = id = conn->msg_id++;
688         send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
689             strlen(path));
690
691         buffer_init(&msg);
692
693         get_msg(conn->fd_in, &msg);
694         type = buffer_get_char(&msg);
695         id = buffer_get_int(&msg);
696
697         if (id != expected_id)
698                 fatal("ID mismatch (%u != %u)", id, expected_id);
699
700         if (type == SSH2_FXP_STATUS) {
701                 u_int status = buffer_get_int(&msg);
702
703                 error("Couldn't readlink: %s", fx2txt(status));
704                 return(NULL);
705         } else if (type != SSH2_FXP_NAME)
706                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
707                     SSH2_FXP_NAME, type);
708
709         count = buffer_get_int(&msg);
710         if (count != 1)
711                 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
712
713         filename = buffer_get_string(&msg, NULL);
714         longname = buffer_get_string(&msg, NULL);
715         a = decode_attrib(&msg);
716
717         debug3("SSH_FXP_READLINK %s -> %s", path, filename);
718
719         xfree(longname);
720
721         buffer_free(&msg);
722
723         return(filename);
724 }
725
726 static void
727 send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
728     char *handle, u_int handle_len)
729 {
730         Buffer msg;
731
732         buffer_init(&msg);
733         buffer_clear(&msg);
734         buffer_put_char(&msg, SSH2_FXP_READ);
735         buffer_put_int(&msg, id);
736         buffer_put_string(&msg, handle, handle_len);
737         buffer_put_int64(&msg, offset);
738         buffer_put_int(&msg, len);
739         send_msg(fd_out, &msg);
740         buffer_free(&msg);
741 }
742
743 int
744 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
745     int pflag)
746 {
747         Attrib junk, *a;
748         Buffer msg;
749         char *handle;
750         int local_fd, status = 0, write_error;
751         int read_error, write_errno;
752         u_int64_t offset, size;
753         u_int handle_len, mode, type, id, buflen, num_req, max_req;
754         off_t progress_counter;
755         struct request {
756                 u_int id;
757                 u_int len;
758                 u_int64_t offset;
759                 TAILQ_ENTRY(request) tq;
760         };
761         TAILQ_HEAD(reqhead, request) requests;
762         struct request *req;
763
764         TAILQ_INIT(&requests);
765
766         a = do_stat(conn, remote_path, 0);
767         if (a == NULL)
768                 return(-1);
769
770         /* XXX: should we preserve set[ug]id? */
771         if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
772                 mode = a->perm & 0777;
773         else
774                 mode = 0666;
775
776         if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
777             (!S_ISREG(a->perm))) {
778                 error("Cannot download non-regular file: %s", remote_path);
779                 return(-1);
780         }
781
782         if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
783                 size = a->size;
784         else
785                 size = 0;
786
787         buflen = conn->transfer_buflen;
788         buffer_init(&msg);
789
790         /* Send open request */
791         id = conn->msg_id++;
792         buffer_put_char(&msg, SSH2_FXP_OPEN);
793         buffer_put_int(&msg, id);
794         buffer_put_cstring(&msg, remote_path);
795         buffer_put_int(&msg, SSH2_FXF_READ);
796         attrib_clear(&junk); /* Send empty attributes */
797         encode_attrib(&msg, &junk);
798         send_msg(conn->fd_out, &msg);
799         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
800
801         handle = get_handle(conn->fd_in, id, &handle_len);
802         if (handle == NULL) {
803                 buffer_free(&msg);
804                 return(-1);
805         }
806
807         local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
808             mode | S_IWRITE);
809         if (local_fd == -1) {
810                 error("Couldn't open local file \"%s\" for writing: %s",
811                     local_path, strerror(errno));
812                 buffer_free(&msg);
813                 xfree(handle);
814                 return(-1);
815         }
816
817         /* Read from remote and write to local */
818         write_error = read_error = write_errno = num_req = offset = 0;
819         max_req = 1;
820         progress_counter = 0;
821
822         if (showprogress && size != 0)
823                 start_progress_meter(remote_path, size, &progress_counter);
824
825         while (num_req > 0 || max_req > 0) {
826                 char *data;
827                 u_int len;
828
829                 /*
830                  * Simulate EOF on interrupt: stop sending new requests and
831                  * allow outstanding requests to drain gracefully
832                  */
833                 if (interrupted) {
834                         if (num_req == 0) /* If we haven't started yet... */
835                                 break;
836                         max_req = 0;
837                 }
838
839                 /* Send some more requests */
840                 while (num_req < max_req) {
841                         debug3("Request range %llu -> %llu (%d/%d)",
842                             (unsigned long long)offset,
843                             (unsigned long long)offset + buflen - 1,
844                             num_req, max_req);
845                         req = xmalloc(sizeof(*req));
846                         req->id = conn->msg_id++;
847                         req->len = buflen;
848                         req->offset = offset;
849                         offset += buflen;
850                         num_req++;
851                         TAILQ_INSERT_TAIL(&requests, req, tq);
852                         send_read_request(conn->fd_out, req->id, req->offset,
853                             req->len, handle, handle_len);
854                 }
855
856                 buffer_clear(&msg);
857                 get_msg(conn->fd_in, &msg);
858                 type = buffer_get_char(&msg);
859                 id = buffer_get_int(&msg);
860                 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
861
862                 /* Find the request in our queue */
863                 for (req = TAILQ_FIRST(&requests);
864                     req != NULL && req->id != id;
865                     req = TAILQ_NEXT(req, tq))
866                         ;
867                 if (req == NULL)
868                         fatal("Unexpected reply %u", id);
869
870                 switch (type) {
871                 case SSH2_FXP_STATUS:
872                         status = buffer_get_int(&msg);
873                         if (status != SSH2_FX_EOF)
874                                 read_error = 1;
875                         max_req = 0;
876                         TAILQ_REMOVE(&requests, req, tq);
877                         xfree(req);
878                         num_req--;
879                         break;
880                 case SSH2_FXP_DATA:
881                         data = buffer_get_string(&msg, &len);
882                         debug3("Received data %llu -> %llu",
883                             (unsigned long long)req->offset,
884                             (unsigned long long)req->offset + len - 1);
885                         if (len > req->len)
886                                 fatal("Received more data than asked for "
887                                     "%u > %u", len, req->len);
888                         if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
889                             atomicio(vwrite, local_fd, data, len) != len) &&
890                             !write_error) {
891                                 write_errno = errno;
892                                 write_error = 1;
893                                 max_req = 0;
894                         }
895                         progress_counter += len;
896                         xfree(data);
897
898                         if (len == req->len) {
899                                 TAILQ_REMOVE(&requests, req, tq);
900                                 xfree(req);
901                                 num_req--;
902                         } else {
903                                 /* Resend the request for the missing data */
904                                 debug3("Short data block, re-requesting "
905                                     "%llu -> %llu (%2d)",
906                                     (unsigned long long)req->offset + len,
907                                     (unsigned long long)req->offset +
908                                     req->len - 1, num_req);
909                                 req->id = conn->msg_id++;
910                                 req->len -= len;
911                                 req->offset += len;
912                                 send_read_request(conn->fd_out, req->id,
913                                     req->offset, req->len, handle, handle_len);
914                                 /* Reduce the request size */
915                                 if (len < buflen)
916                                         buflen = MAX(MIN_READ_SIZE, len);
917                         }
918                         if (max_req > 0) { /* max_req = 0 iff EOF received */
919                                 if (size > 0 && offset > size) {
920                                         /* Only one request at a time
921                                          * after the expected EOF */
922                                         debug3("Finish at %llu (%2d)",
923                                             (unsigned long long)offset,
924                                             num_req);
925                                         max_req = 1;
926                                 } else if (max_req <= conn->num_requests) {
927                                         ++max_req;
928                                 }
929                         }
930                         break;
931                 default:
932                         fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
933                             SSH2_FXP_DATA, type);
934                 }
935         }
936
937         if (showprogress && size)
938                 stop_progress_meter();
939
940         /* Sanity check */
941         if (TAILQ_FIRST(&requests) != NULL)
942                 fatal("Transfer complete, but requests still in queue");
943
944         if (read_error) {
945                 error("Couldn't read from remote file \"%s\" : %s",
946                     remote_path, fx2txt(status));
947                 do_close(conn, handle, handle_len);
948         } else if (write_error) {
949                 error("Couldn't write to \"%s\": %s", local_path,
950                     strerror(write_errno));
951                 status = -1;
952                 do_close(conn, handle, handle_len);
953         } else {
954                 status = do_close(conn, handle, handle_len);
955
956                 /* Override umask and utimes if asked */
957 #ifdef HAVE_FCHMOD
958                 if (pflag && fchmod(local_fd, mode) == -1)
959 #else
960                 if (pflag && chmod(local_path, mode) == -1)
961 #endif /* HAVE_FCHMOD */
962                         error("Couldn't set mode on \"%s\": %s", local_path,
963                             strerror(errno));
964                 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
965                         struct timeval tv[2];
966                         tv[0].tv_sec = a->atime;
967                         tv[1].tv_sec = a->mtime;
968                         tv[0].tv_usec = tv[1].tv_usec = 0;
969                         if (utimes(local_path, tv) == -1)
970                                 error("Can't set times on \"%s\": %s",
971                                     local_path, strerror(errno));
972                 }
973         }
974         close(local_fd);
975         buffer_free(&msg);
976         xfree(handle);
977
978         return(status);
979 }
980
981 int
982 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
983     int pflag)
984 {
985         int local_fd, status;
986         u_int handle_len, id, type;
987         u_int64_t offset;
988         char *handle, *data;
989         Buffer msg;
990         struct stat sb;
991         Attrib a;
992         u_int32_t startid;
993         u_int32_t ackid;
994         struct outstanding_ack {
995                 u_int id;
996                 u_int len;
997                 u_int64_t offset;
998                 TAILQ_ENTRY(outstanding_ack) tq;
999         };
1000         TAILQ_HEAD(ackhead, outstanding_ack) acks;
1001         struct outstanding_ack *ack = NULL;
1002
1003         TAILQ_INIT(&acks);
1004
1005         if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1006                 error("Couldn't open local file \"%s\" for reading: %s",
1007                     local_path, strerror(errno));
1008                 return(-1);
1009         }
1010         if (fstat(local_fd, &sb) == -1) {
1011                 error("Couldn't fstat local file \"%s\": %s",
1012                     local_path, strerror(errno));
1013                 close(local_fd);
1014                 return(-1);
1015         }
1016         if (!S_ISREG(sb.st_mode)) {
1017                 error("%s is not a regular file", local_path);
1018                 close(local_fd);
1019                 return(-1);
1020         }
1021         stat_to_attrib(&sb, &a);
1022
1023         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1024         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1025         a.perm &= 0777;
1026         if (!pflag)
1027                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1028
1029         buffer_init(&msg);
1030
1031         /* Send open request */
1032         id = conn->msg_id++;
1033         buffer_put_char(&msg, SSH2_FXP_OPEN);
1034         buffer_put_int(&msg, id);
1035         buffer_put_cstring(&msg, remote_path);
1036         buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1037         encode_attrib(&msg, &a);
1038         send_msg(conn->fd_out, &msg);
1039         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1040
1041         buffer_clear(&msg);
1042
1043         handle = get_handle(conn->fd_in, id, &handle_len);
1044         if (handle == NULL) {
1045                 close(local_fd);
1046                 buffer_free(&msg);
1047                 return(-1);
1048         }
1049
1050         startid = ackid = id + 1;
1051         data = xmalloc(conn->transfer_buflen);
1052
1053         /* Read from local and write to remote */
1054         offset = 0;
1055         if (showprogress)
1056                 start_progress_meter(local_path, sb.st_size, &offset);
1057
1058         for (;;) {
1059                 int len;
1060
1061                 /*
1062                  * Can't use atomicio here because it returns 0 on EOF,
1063                  * thus losing the last block of the file.
1064                  * Simulate an EOF on interrupt, allowing ACKs from the
1065                  * server to drain.
1066                  */
1067                 if (interrupted)
1068                         len = 0;
1069                 else do
1070                         len = read(local_fd, data, conn->transfer_buflen);
1071                 while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1072
1073                 if (len == -1)
1074                         fatal("Couldn't read from \"%s\": %s", local_path,
1075                             strerror(errno));
1076
1077                 if (len != 0) {
1078                         ack = xmalloc(sizeof(*ack));
1079                         ack->id = ++id;
1080                         ack->offset = offset;
1081                         ack->len = len;
1082                         TAILQ_INSERT_TAIL(&acks, ack, tq);
1083
1084                         buffer_clear(&msg);
1085                         buffer_put_char(&msg, SSH2_FXP_WRITE);
1086                         buffer_put_int(&msg, ack->id);
1087                         buffer_put_string(&msg, handle, handle_len);
1088                         buffer_put_int64(&msg, offset);
1089                         buffer_put_string(&msg, data, len);
1090                         send_msg(conn->fd_out, &msg);
1091                         debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1092                             id, (unsigned long long)offset, len);
1093                 } else if (TAILQ_FIRST(&acks) == NULL)
1094                         break;
1095
1096                 if (ack == NULL)
1097                         fatal("Unexpected ACK %u", id);
1098
1099                 if (id == startid || len == 0 ||
1100                     id - ackid >= conn->num_requests) {
1101                         u_int r_id;
1102
1103                         buffer_clear(&msg);
1104                         get_msg(conn->fd_in, &msg);
1105                         type = buffer_get_char(&msg);
1106                         r_id = buffer_get_int(&msg);
1107
1108                         if (type != SSH2_FXP_STATUS)
1109                                 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1110                                     "got %d", SSH2_FXP_STATUS, type);
1111
1112                         status = buffer_get_int(&msg);
1113                         debug3("SSH2_FXP_STATUS %d", status);
1114
1115                         /* Find the request in our queue */
1116                         for (ack = TAILQ_FIRST(&acks);
1117                             ack != NULL && ack->id != r_id;
1118                             ack = TAILQ_NEXT(ack, tq))
1119                                 ;
1120                         if (ack == NULL)
1121                                 fatal("Can't find request for ID %u", r_id);
1122                         TAILQ_REMOVE(&acks, ack, tq);
1123
1124                         if (status != SSH2_FX_OK) {
1125                                 error("Couldn't write to remote file \"%s\": %s",
1126                                     remote_path, fx2txt(status));
1127                                 do_close(conn, handle, handle_len);
1128                                 close(local_fd);
1129                                 xfree(data);
1130                                 xfree(ack);
1131                                 goto done;
1132                         }
1133                         debug3("In write loop, ack for %u %u bytes at %llu",
1134                             ack->id, ack->len, (unsigned long long)ack->offset);
1135                         ++ackid;
1136                         xfree(ack);
1137                 }
1138                 offset += len;
1139         }
1140         if (showprogress)
1141                 stop_progress_meter();
1142         xfree(data);
1143
1144         if (close(local_fd) == -1) {
1145                 error("Couldn't close local file \"%s\": %s", local_path,
1146                     strerror(errno));
1147                 do_close(conn, handle, handle_len);
1148                 status = -1;
1149                 goto done;
1150         }
1151
1152         /* Override umask and utimes if asked */
1153         if (pflag)
1154                 do_fsetstat(conn, handle, handle_len, &a);
1155
1156         status = do_close(conn, handle, handle_len);
1157
1158 done:
1159         xfree(handle);
1160         buffer_free(&msg);
1161         return(status);
1162 }
This page took 0.124231 seconds and 5 git commands to generate.