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