gdb - Local mods (compile)
[dragonfly.git] / crypto / openssh / sftp-client.c
1 /* $OpenBSD: sftp-client.c,v 1.115 2014/04/21 14:36:16 logan 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 #ifdef HAVE_SYS_STATVFS_H
28 #include <sys/statvfs.h>
29 #endif
30 #include "openbsd-compat/sys-queue.h"
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
33 #endif
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 #endif
37 #include <sys/uio.h>
38
39 #include <dirent.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <signal.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #include "xmalloc.h"
50 #include "buffer.h"
51 #include "log.h"
52 #include "atomicio.h"
53 #include "progressmeter.h"
54 #include "misc.h"
55
56 #include "sftp.h"
57 #include "sftp-common.h"
58 #include "sftp-client.h"
59
60 extern volatile sig_atomic_t interrupted;
61 extern int showprogress;
62
63 /* Minimum amount of data to read at a time */
64 #define MIN_READ_SIZE   512
65
66 /* Maximum depth to descend in directory trees */
67 #define MAX_DIR_DEPTH 64
68
69 struct sftp_conn {
70         int fd_in;
71         int fd_out;
72         u_int transfer_buflen;
73         u_int num_requests;
74         u_int version;
75         u_int msg_id;
76 #define SFTP_EXT_POSIX_RENAME   0x00000001
77 #define SFTP_EXT_STATVFS        0x00000002
78 #define SFTP_EXT_FSTATVFS       0x00000004
79 #define SFTP_EXT_HARDLINK       0x00000008
80 #define SFTP_EXT_FSYNC          0x00000010
81         u_int exts;
82         u_int64_t limit_kbps;
83         struct bwlimit bwlimit_in, bwlimit_out;
84 };
85
86 static char *
87 get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
88     const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
89
90 /* ARGSUSED */
91 static int
92 sftpio(void *_bwlimit, size_t amount)
93 {
94         struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
95
96         bandwidth_limit(bwlimit, amount);
97         return 0;
98 }
99
100 static void
101 send_msg(struct sftp_conn *conn, Buffer *m)
102 {
103         u_char mlen[4];
104         struct iovec iov[2];
105
106         if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
107                 fatal("Outbound message too long %u", buffer_len(m));
108
109         /* Send length first */
110         put_u32(mlen, buffer_len(m));
111         iov[0].iov_base = mlen;
112         iov[0].iov_len = sizeof(mlen);
113         iov[1].iov_base = buffer_ptr(m);
114         iov[1].iov_len = buffer_len(m);
115
116         if (atomiciov6(writev, conn->fd_out, iov, 2,
117             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
118             buffer_len(m) + sizeof(mlen))
119                 fatal("Couldn't send packet: %s", strerror(errno));
120
121         buffer_clear(m);
122 }
123
124 static void
125 get_msg(struct sftp_conn *conn, Buffer *m)
126 {
127         u_int msg_len;
128
129         buffer_append_space(m, 4);
130         if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
131             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
132                 if (errno == EPIPE)
133                         fatal("Connection closed");
134                 else
135                         fatal("Couldn't read packet: %s", strerror(errno));
136         }
137
138         msg_len = buffer_get_int(m);
139         if (msg_len > SFTP_MAX_MSG_LENGTH)
140                 fatal("Received message too long %u", msg_len);
141
142         buffer_append_space(m, msg_len);
143         if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
144             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
145             != msg_len) {
146                 if (errno == EPIPE)
147                         fatal("Connection closed");
148                 else
149                         fatal("Read packet: %s", strerror(errno));
150         }
151 }
152
153 static void
154 send_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
155     u_int len)
156 {
157         Buffer msg;
158
159         buffer_init(&msg);
160         buffer_put_char(&msg, code);
161         buffer_put_int(&msg, id);
162         buffer_put_string(&msg, s, len);
163         send_msg(conn, &msg);
164         debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
165         buffer_free(&msg);
166 }
167
168 static void
169 send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
170     char *s, u_int len, Attrib *a)
171 {
172         Buffer msg;
173
174         buffer_init(&msg);
175         buffer_put_char(&msg, code);
176         buffer_put_int(&msg, id);
177         buffer_put_string(&msg, s, len);
178         encode_attrib(&msg, a);
179         send_msg(conn, &msg);
180         debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
181         buffer_free(&msg);
182 }
183
184 static u_int
185 get_status(struct sftp_conn *conn, u_int expected_id)
186 {
187         Buffer msg;
188         u_int type, id, status;
189
190         buffer_init(&msg);
191         get_msg(conn, &msg);
192         type = buffer_get_char(&msg);
193         id = buffer_get_int(&msg);
194
195         if (id != expected_id)
196                 fatal("ID mismatch (%u != %u)", id, expected_id);
197         if (type != SSH2_FXP_STATUS)
198                 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
199                     SSH2_FXP_STATUS, type);
200
201         status = buffer_get_int(&msg);
202         buffer_free(&msg);
203
204         debug3("SSH2_FXP_STATUS %u", status);
205
206         return status;
207 }
208
209 static char *
210 get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
211     const char *errfmt, ...)
212 {
213         Buffer msg;
214         u_int type, id;
215         char *handle, errmsg[256];
216         va_list args;
217         int status;
218
219         va_start(args, errfmt);
220         if (errfmt != NULL)
221                 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
222         va_end(args);
223
224         buffer_init(&msg);
225         get_msg(conn, &msg);
226         type = buffer_get_char(&msg);
227         id = buffer_get_int(&msg);
228
229         if (id != expected_id)
230                 fatal("%s: ID mismatch (%u != %u)",
231                     errfmt == NULL ? __func__ : errmsg, id, expected_id);
232         if (type == SSH2_FXP_STATUS) {
233                 status = buffer_get_int(&msg);
234                 if (errfmt != NULL)
235                         error("%s: %s", errmsg, fx2txt(status));
236                 buffer_free(&msg);
237                 return(NULL);
238         } else if (type != SSH2_FXP_HANDLE)
239                 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
240                     errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
241
242         handle = buffer_get_string(&msg, len);
243         buffer_free(&msg);
244
245         return(handle);
246 }
247
248 static Attrib *
249 get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
250 {
251         Buffer msg;
252         u_int type, id;
253         Attrib *a;
254
255         buffer_init(&msg);
256         get_msg(conn, &msg);
257
258         type = buffer_get_char(&msg);
259         id = buffer_get_int(&msg);
260
261         debug3("Received stat reply T:%u I:%u", type, id);
262         if (id != expected_id)
263                 fatal("ID mismatch (%u != %u)", id, expected_id);
264         if (type == SSH2_FXP_STATUS) {
265                 int status = buffer_get_int(&msg);
266
267                 if (quiet)
268                         debug("Couldn't stat remote file: %s", fx2txt(status));
269                 else
270                         error("Couldn't stat remote file: %s", fx2txt(status));
271                 buffer_free(&msg);
272                 return(NULL);
273         } else if (type != SSH2_FXP_ATTRS) {
274                 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
275                     SSH2_FXP_ATTRS, type);
276         }
277         a = decode_attrib(&msg);
278         buffer_free(&msg);
279
280         return(a);
281 }
282
283 static int
284 get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
285     u_int expected_id, int quiet)
286 {
287         Buffer msg;
288         u_int type, id, flag;
289
290         buffer_init(&msg);
291         get_msg(conn, &msg);
292
293         type = buffer_get_char(&msg);
294         id = buffer_get_int(&msg);
295
296         debug3("Received statvfs reply T:%u I:%u", type, id);
297         if (id != expected_id)
298                 fatal("ID mismatch (%u != %u)", id, expected_id);
299         if (type == SSH2_FXP_STATUS) {
300                 int status = buffer_get_int(&msg);
301
302                 if (quiet)
303                         debug("Couldn't statvfs: %s", fx2txt(status));
304                 else
305                         error("Couldn't statvfs: %s", fx2txt(status));
306                 buffer_free(&msg);
307                 return -1;
308         } else if (type != SSH2_FXP_EXTENDED_REPLY) {
309                 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
310                     SSH2_FXP_EXTENDED_REPLY, type);
311         }
312
313         memset(st, 0, sizeof(*st));
314         st->f_bsize = buffer_get_int64(&msg);
315         st->f_frsize = buffer_get_int64(&msg);
316         st->f_blocks = buffer_get_int64(&msg);
317         st->f_bfree = buffer_get_int64(&msg);
318         st->f_bavail = buffer_get_int64(&msg);
319         st->f_files = buffer_get_int64(&msg);
320         st->f_ffree = buffer_get_int64(&msg);
321         st->f_favail = buffer_get_int64(&msg);
322         st->f_fsid = buffer_get_int64(&msg);
323         flag = buffer_get_int64(&msg);
324         st->f_namemax = buffer_get_int64(&msg);
325
326         st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
327         st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
328
329         buffer_free(&msg);
330
331         return 0;
332 }
333
334 struct sftp_conn *
335 do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
336     u_int64_t limit_kbps)
337 {
338         u_int type;
339         Buffer msg;
340         struct sftp_conn *ret;
341
342         ret = xcalloc(1, sizeof(*ret));
343         ret->msg_id = 1;
344         ret->fd_in = fd_in;
345         ret->fd_out = fd_out;
346         ret->transfer_buflen = transfer_buflen;
347         ret->num_requests = num_requests;
348         ret->exts = 0;
349         ret->limit_kbps = 0;
350
351         buffer_init(&msg);
352         buffer_put_char(&msg, SSH2_FXP_INIT);
353         buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
354         send_msg(ret, &msg);
355
356         buffer_clear(&msg);
357
358         get_msg(ret, &msg);
359
360         /* Expecting a VERSION reply */
361         if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
362                 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
363                     type);
364                 buffer_free(&msg);
365                 return(NULL);
366         }
367         ret->version = buffer_get_int(&msg);
368
369         debug2("Remote version: %u", ret->version);
370
371         /* Check for extensions */
372         while (buffer_len(&msg) > 0) {
373                 char *name = buffer_get_string(&msg, NULL);
374                 char *value = buffer_get_string(&msg, NULL);
375                 int known = 0;
376
377                 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
378                     strcmp(value, "1") == 0) {
379                         ret->exts |= SFTP_EXT_POSIX_RENAME;
380                         known = 1;
381                 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
382                     strcmp(value, "2") == 0) {
383                         ret->exts |= SFTP_EXT_STATVFS;
384                         known = 1;
385                 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
386                     strcmp(value, "2") == 0) {
387                         ret->exts |= SFTP_EXT_FSTATVFS;
388                         known = 1;
389                 } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
390                     strcmp(value, "1") == 0) {
391                         ret->exts |= SFTP_EXT_HARDLINK;
392                         known = 1;
393                 } else if (strcmp(name, "fsync@openssh.com") == 0 &&
394                     strcmp(value, "1") == 0) {
395                         ret->exts |= SFTP_EXT_FSYNC;
396                         known = 1;
397                 }
398                 if (known) {
399                         debug2("Server supports extension \"%s\" revision %s",
400                             name, value);
401                 } else {
402                         debug2("Unrecognised server extension \"%s\"", name);
403                 }
404                 free(name);
405                 free(value);
406         }
407
408         buffer_free(&msg);
409
410         /* Some filexfer v.0 servers don't support large packets */
411         if (ret->version == 0)
412                 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
413
414         ret->limit_kbps = limit_kbps;
415         if (ret->limit_kbps > 0) {
416                 bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
417                     ret->transfer_buflen);
418                 bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
419                     ret->transfer_buflen);
420         }
421
422         return ret;
423 }
424
425 u_int
426 sftp_proto_version(struct sftp_conn *conn)
427 {
428         return conn->version;
429 }
430
431 int
432 do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
433 {
434         u_int id, status;
435         Buffer msg;
436
437         buffer_init(&msg);
438
439         id = conn->msg_id++;
440         buffer_put_char(&msg, SSH2_FXP_CLOSE);
441         buffer_put_int(&msg, id);
442         buffer_put_string(&msg, handle, handle_len);
443         send_msg(conn, &msg);
444         debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
445
446         status = get_status(conn, id);
447         if (status != SSH2_FX_OK)
448                 error("Couldn't close file: %s", fx2txt(status));
449
450         buffer_free(&msg);
451
452         return status;
453 }
454
455
456 static int
457 do_lsreaddir(struct sftp_conn *conn, char *path, int print_flag,
458     SFTP_DIRENT ***dir)
459 {
460         Buffer msg;
461         u_int count, type, id, handle_len, i, expected_id, ents = 0;
462         char *handle;
463         int status = SSH2_FX_FAILURE;
464
465         if (dir)
466                 *dir = NULL;
467
468         id = conn->msg_id++;
469
470         buffer_init(&msg);
471         buffer_put_char(&msg, SSH2_FXP_OPENDIR);
472         buffer_put_int(&msg, id);
473         buffer_put_cstring(&msg, path);
474         send_msg(conn, &msg);
475
476         handle = get_handle(conn, id, &handle_len,
477             "remote readdir(\"%s\")", path);
478         if (handle == NULL) {
479                 buffer_free(&msg);
480                 return -1;
481         }
482
483         if (dir) {
484                 ents = 0;
485                 *dir = xcalloc(1, sizeof(**dir));
486                 (*dir)[0] = NULL;
487         }
488
489         for (; !interrupted;) {
490                 id = expected_id = conn->msg_id++;
491
492                 debug3("Sending SSH2_FXP_READDIR I:%u", id);
493
494                 buffer_clear(&msg);
495                 buffer_put_char(&msg, SSH2_FXP_READDIR);
496                 buffer_put_int(&msg, id);
497                 buffer_put_string(&msg, handle, handle_len);
498                 send_msg(conn, &msg);
499
500                 buffer_clear(&msg);
501
502                 get_msg(conn, &msg);
503
504                 type = buffer_get_char(&msg);
505                 id = buffer_get_int(&msg);
506
507                 debug3("Received reply T:%u I:%u", type, id);
508
509                 if (id != expected_id)
510                         fatal("ID mismatch (%u != %u)", id, expected_id);
511
512                 if (type == SSH2_FXP_STATUS) {
513                         status = buffer_get_int(&msg);
514                         debug3("Received SSH2_FXP_STATUS %d", status);
515                         if (status == SSH2_FX_EOF)
516                                 break;
517                         error("Couldn't read directory: %s", fx2txt(status));
518                         goto out;
519                 } else if (type != SSH2_FXP_NAME)
520                         fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
521                             SSH2_FXP_NAME, type);
522
523                 count = buffer_get_int(&msg);
524                 if (count == 0)
525                         break;
526                 debug3("Received %d SSH2_FXP_NAME responses", count);
527                 for (i = 0; i < count; i++) {
528                         char *filename, *longname;
529                         Attrib *a;
530
531                         filename = buffer_get_string(&msg, NULL);
532                         longname = buffer_get_string(&msg, NULL);
533                         a = decode_attrib(&msg);
534
535                         if (print_flag)
536                                 printf("%s\n", longname);
537
538                         /*
539                          * Directory entries should never contain '/'
540                          * These can be used to attack recursive ops
541                          * (e.g. send '../../../../etc/passwd')
542                          */
543                         if (strchr(filename, '/') != NULL) {
544                                 error("Server sent suspect path \"%s\" "
545                                     "during readdir of \"%s\"", filename, path);
546                         } else if (dir) {
547                                 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
548                                 (*dir)[ents] = xcalloc(1, sizeof(***dir));
549                                 (*dir)[ents]->filename = xstrdup(filename);
550                                 (*dir)[ents]->longname = xstrdup(longname);
551                                 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
552                                 (*dir)[++ents] = NULL;
553                         }
554                         free(filename);
555                         free(longname);
556                 }
557         }
558         status = 0;
559
560  out:
561         buffer_free(&msg);
562         do_close(conn, handle, handle_len);
563         free(handle);
564
565         if (status != 0 && dir != NULL) {
566                 /* Don't return results on error */
567                 free_sftp_dirents(*dir);
568                 *dir = NULL;
569         } else if (interrupted && dir != NULL && *dir != NULL) {
570                 /* Don't return partial matches on interrupt */
571                 free_sftp_dirents(*dir);
572                 *dir = xcalloc(1, sizeof(**dir));
573                 **dir = NULL;
574         }
575
576         return status;
577 }
578
579 int
580 do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
581 {
582         return(do_lsreaddir(conn, path, 0, dir));
583 }
584
585 void free_sftp_dirents(SFTP_DIRENT **s)
586 {
587         int i;
588
589         if (s == NULL)
590                 return;
591         for (i = 0; s[i]; i++) {
592                 free(s[i]->filename);
593                 free(s[i]->longname);
594                 free(s[i]);
595         }
596         free(s);
597 }
598
599 int
600 do_rm(struct sftp_conn *conn, char *path)
601 {
602         u_int status, id;
603
604         debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
605
606         id = conn->msg_id++;
607         send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
608         status = get_status(conn, id);
609         if (status != SSH2_FX_OK)
610                 error("Couldn't delete file: %s", fx2txt(status));
611         return(status);
612 }
613
614 int
615 do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int print_flag)
616 {
617         u_int status, id;
618
619         id = conn->msg_id++;
620         send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
621             strlen(path), a);
622
623         status = get_status(conn, id);
624         if (status != SSH2_FX_OK && print_flag)
625                 error("Couldn't create directory: %s", fx2txt(status));
626
627         return(status);
628 }
629
630 int
631 do_rmdir(struct sftp_conn *conn, char *path)
632 {
633         u_int status, id;
634
635         id = conn->msg_id++;
636         send_string_request(conn, id, SSH2_FXP_RMDIR, path,
637             strlen(path));
638
639         status = get_status(conn, id);
640         if (status != SSH2_FX_OK)
641                 error("Couldn't remove directory: %s", fx2txt(status));
642
643         return(status);
644 }
645
646 Attrib *
647 do_stat(struct sftp_conn *conn, char *path, int quiet)
648 {
649         u_int id;
650
651         id = conn->msg_id++;
652
653         send_string_request(conn, id,
654             conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
655             path, strlen(path));
656
657         return(get_decode_stat(conn, id, quiet));
658 }
659
660 Attrib *
661 do_lstat(struct sftp_conn *conn, char *path, int quiet)
662 {
663         u_int id;
664
665         if (conn->version == 0) {
666                 if (quiet)
667                         debug("Server version does not support lstat operation");
668                 else
669                         logit("Server version does not support lstat operation");
670                 return(do_stat(conn, path, quiet));
671         }
672
673         id = conn->msg_id++;
674         send_string_request(conn, id, SSH2_FXP_LSTAT, path,
675             strlen(path));
676
677         return(get_decode_stat(conn, id, quiet));
678 }
679
680 #ifdef notyet
681 Attrib *
682 do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
683 {
684         u_int id;
685
686         id = conn->msg_id++;
687         send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
688             handle_len);
689
690         return(get_decode_stat(conn, id, quiet));
691 }
692 #endif
693
694 int
695 do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
696 {
697         u_int status, id;
698
699         id = conn->msg_id++;
700         send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
701             strlen(path), a);
702
703         status = get_status(conn, id);
704         if (status != SSH2_FX_OK)
705                 error("Couldn't setstat on \"%s\": %s", path,
706                     fx2txt(status));
707
708         return(status);
709 }
710
711 int
712 do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
713     Attrib *a)
714 {
715         u_int status, id;
716
717         id = conn->msg_id++;
718         send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
719             handle_len, a);
720
721         status = get_status(conn, id);
722         if (status != SSH2_FX_OK)
723                 error("Couldn't fsetstat: %s", fx2txt(status));
724
725         return(status);
726 }
727
728 char *
729 do_realpath(struct sftp_conn *conn, char *path)
730 {
731         Buffer msg;
732         u_int type, expected_id, count, id;
733         char *filename, *longname;
734         Attrib *a;
735
736         expected_id = id = conn->msg_id++;
737         send_string_request(conn, id, SSH2_FXP_REALPATH, path,
738             strlen(path));
739
740         buffer_init(&msg);
741
742         get_msg(conn, &msg);
743         type = buffer_get_char(&msg);
744         id = buffer_get_int(&msg);
745
746         if (id != expected_id)
747                 fatal("ID mismatch (%u != %u)", id, expected_id);
748
749         if (type == SSH2_FXP_STATUS) {
750                 u_int status = buffer_get_int(&msg);
751
752                 error("Couldn't canonicalize: %s", fx2txt(status));
753                 buffer_free(&msg);
754                 return NULL;
755         } else if (type != SSH2_FXP_NAME)
756                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
757                     SSH2_FXP_NAME, type);
758
759         count = buffer_get_int(&msg);
760         if (count != 1)
761                 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
762
763         filename = buffer_get_string(&msg, NULL);
764         longname = buffer_get_string(&msg, NULL);
765         a = decode_attrib(&msg);
766
767         debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
768             (unsigned long)a->size);
769
770         free(longname);
771
772         buffer_free(&msg);
773
774         return(filename);
775 }
776
777 int
778 do_rename(struct sftp_conn *conn, char *oldpath, char *newpath,
779     int force_legacy)
780 {
781         Buffer msg;
782         u_int status, id;
783         int use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
784
785         buffer_init(&msg);
786
787         /* Send rename request */
788         id = conn->msg_id++;
789         if (use_ext) {
790                 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
791                 buffer_put_int(&msg, id);
792                 buffer_put_cstring(&msg, "posix-rename@openssh.com");
793         } else {
794                 buffer_put_char(&msg, SSH2_FXP_RENAME);
795                 buffer_put_int(&msg, id);
796         }
797         buffer_put_cstring(&msg, oldpath);
798         buffer_put_cstring(&msg, newpath);
799         send_msg(conn, &msg);
800         debug3("Sent message %s \"%s\" -> \"%s\"",
801             use_ext ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME",
802             oldpath, newpath);
803         buffer_free(&msg);
804
805         status = get_status(conn, id);
806         if (status != SSH2_FX_OK)
807                 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
808                     newpath, fx2txt(status));
809
810         return(status);
811 }
812
813 int
814 do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
815 {
816         Buffer msg;
817         u_int status, id;
818
819         if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
820                 error("Server does not support hardlink@openssh.com extension");
821                 return -1;
822         }
823
824         buffer_init(&msg);
825
826         /* Send link request */
827         id = conn->msg_id++;
828         buffer_put_char(&msg, SSH2_FXP_EXTENDED);
829         buffer_put_int(&msg, id);
830         buffer_put_cstring(&msg, "hardlink@openssh.com");
831         buffer_put_cstring(&msg, oldpath);
832         buffer_put_cstring(&msg, newpath);
833         send_msg(conn, &msg);
834         debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
835                oldpath, newpath);
836         buffer_free(&msg);
837
838         status = get_status(conn, id);
839         if (status != SSH2_FX_OK)
840                 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
841                     newpath, fx2txt(status));
842
843         return(status);
844 }
845
846 int
847 do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
848 {
849         Buffer msg;
850         u_int status, id;
851
852         if (conn->version < 3) {
853                 error("This server does not support the symlink operation");
854                 return(SSH2_FX_OP_UNSUPPORTED);
855         }
856
857         buffer_init(&msg);
858
859         /* Send symlink request */
860         id = conn->msg_id++;
861         buffer_put_char(&msg, SSH2_FXP_SYMLINK);
862         buffer_put_int(&msg, id);
863         buffer_put_cstring(&msg, oldpath);
864         buffer_put_cstring(&msg, newpath);
865         send_msg(conn, &msg);
866         debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
867             newpath);
868         buffer_free(&msg);
869
870         status = get_status(conn, id);
871         if (status != SSH2_FX_OK)
872                 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
873                     newpath, fx2txt(status));
874
875         return(status);
876 }
877
878 int
879 do_fsync(struct sftp_conn *conn, char *handle, u_int handle_len)
880 {
881         Buffer msg;
882         u_int status, id;
883
884         /* Silently return if the extension is not supported */
885         if ((conn->exts & SFTP_EXT_FSYNC) == 0)
886                 return -1;
887
888         buffer_init(&msg);
889
890         /* Send fsync request */
891         id = conn->msg_id++;
892
893         buffer_put_char(&msg, SSH2_FXP_EXTENDED);
894         buffer_put_int(&msg, id);
895         buffer_put_cstring(&msg, "fsync@openssh.com");
896         buffer_put_string(&msg, handle, handle_len);
897         send_msg(conn, &msg);
898         debug3("Sent message fsync@openssh.com I:%u", id);
899         buffer_free(&msg);
900
901         status = get_status(conn, id);
902         if (status != SSH2_FX_OK)
903                 error("Couldn't sync file: %s", fx2txt(status));
904
905         return status;
906 }
907
908 #ifdef notyet
909 char *
910 do_readlink(struct sftp_conn *conn, char *path)
911 {
912         Buffer msg;
913         u_int type, expected_id, count, id;
914         char *filename, *longname;
915         Attrib *a;
916
917         expected_id = id = conn->msg_id++;
918         send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
919
920         buffer_init(&msg);
921
922         get_msg(conn, &msg);
923         type = buffer_get_char(&msg);
924         id = buffer_get_int(&msg);
925
926         if (id != expected_id)
927                 fatal("ID mismatch (%u != %u)", id, expected_id);
928
929         if (type == SSH2_FXP_STATUS) {
930                 u_int status = buffer_get_int(&msg);
931
932                 error("Couldn't readlink: %s", fx2txt(status));
933                 buffer_free(&msg);
934                 return(NULL);
935         } else if (type != SSH2_FXP_NAME)
936                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
937                     SSH2_FXP_NAME, type);
938
939         count = buffer_get_int(&msg);
940         if (count != 1)
941                 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
942
943         filename = buffer_get_string(&msg, NULL);
944         longname = buffer_get_string(&msg, NULL);
945         a = decode_attrib(&msg);
946
947         debug3("SSH_FXP_READLINK %s -> %s", path, filename);
948
949         free(longname);
950
951         buffer_free(&msg);
952
953         return(filename);
954 }
955 #endif
956
957 int
958 do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
959     int quiet)
960 {
961         Buffer msg;
962         u_int id;
963
964         if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
965                 error("Server does not support statvfs@openssh.com extension");
966                 return -1;
967         }
968
969         id = conn->msg_id++;
970
971         buffer_init(&msg);
972         buffer_clear(&msg);
973         buffer_put_char(&msg, SSH2_FXP_EXTENDED);
974         buffer_put_int(&msg, id);
975         buffer_put_cstring(&msg, "statvfs@openssh.com");
976         buffer_put_cstring(&msg, path);
977         send_msg(conn, &msg);
978         buffer_free(&msg);
979
980         return get_decode_statvfs(conn, st, id, quiet);
981 }
982
983 #ifdef notyet
984 int
985 do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
986     struct sftp_statvfs *st, int quiet)
987 {
988         Buffer msg;
989         u_int id;
990
991         if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
992                 error("Server does not support fstatvfs@openssh.com extension");
993                 return -1;
994         }
995
996         id = conn->msg_id++;
997
998         buffer_init(&msg);
999         buffer_clear(&msg);
1000         buffer_put_char(&msg, SSH2_FXP_EXTENDED);
1001         buffer_put_int(&msg, id);
1002         buffer_put_cstring(&msg, "fstatvfs@openssh.com");
1003         buffer_put_string(&msg, handle, handle_len);
1004         send_msg(conn, &msg);
1005         buffer_free(&msg);
1006
1007         return get_decode_statvfs(conn, st, id, quiet);
1008 }
1009 #endif
1010
1011 static void
1012 send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1013     u_int len, char *handle, u_int handle_len)
1014 {
1015         Buffer msg;
1016
1017         buffer_init(&msg);
1018         buffer_clear(&msg);
1019         buffer_put_char(&msg, SSH2_FXP_READ);
1020         buffer_put_int(&msg, id);
1021         buffer_put_string(&msg, handle, handle_len);
1022         buffer_put_int64(&msg, offset);
1023         buffer_put_int(&msg, len);
1024         send_msg(conn, &msg);
1025         buffer_free(&msg);
1026 }
1027
1028 int
1029 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1030     Attrib *a, int preserve_flag, int resume_flag, int fsync_flag)
1031 {
1032         Attrib junk;
1033         Buffer msg;
1034         char *handle;
1035         int local_fd = -1, status = 0, write_error;
1036         int read_error, write_errno, reordered = 0;
1037         u_int64_t offset = 0, size, highwater;
1038         u_int handle_len, mode, type, id, buflen, num_req, max_req;
1039         off_t progress_counter;
1040         struct stat st;
1041         struct request {
1042                 u_int id;
1043                 u_int len;
1044                 u_int64_t offset;
1045                 TAILQ_ENTRY(request) tq;
1046         };
1047         TAILQ_HEAD(reqhead, request) requests;
1048         struct request *req;
1049
1050         TAILQ_INIT(&requests);
1051
1052         if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1053                 return -1;
1054
1055         /* Do not preserve set[ug]id here, as we do not preserve ownership */
1056         if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1057                 mode = a->perm & 0777;
1058         else
1059                 mode = 0666;
1060
1061         if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1062             (!S_ISREG(a->perm))) {
1063                 error("Cannot download non-regular file: %s", remote_path);
1064                 return(-1);
1065         }
1066
1067         if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1068                 size = a->size;
1069         else
1070                 size = 0;
1071
1072         buflen = conn->transfer_buflen;
1073         buffer_init(&msg);
1074
1075         /* Send open request */
1076         id = conn->msg_id++;
1077         buffer_put_char(&msg, SSH2_FXP_OPEN);
1078         buffer_put_int(&msg, id);
1079         buffer_put_cstring(&msg, remote_path);
1080         buffer_put_int(&msg, SSH2_FXF_READ);
1081         attrib_clear(&junk); /* Send empty attributes */
1082         encode_attrib(&msg, &junk);
1083         send_msg(conn, &msg);
1084         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1085
1086         handle = get_handle(conn, id, &handle_len,
1087             "remote open(\"%s\")", remote_path);
1088         if (handle == NULL) {
1089                 buffer_free(&msg);
1090                 return(-1);
1091         }
1092
1093         local_fd = open(local_path,
1094             O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
1095         if (local_fd == -1) {
1096                 error("Couldn't open local file \"%s\" for writing: %s",
1097                     local_path, strerror(errno));
1098                 goto fail;
1099         }
1100         offset = highwater = 0;
1101         if (resume_flag) {
1102                 if (fstat(local_fd, &st) == -1) {
1103                         error("Unable to stat local file \"%s\": %s",
1104                             local_path, strerror(errno));
1105                         goto fail;
1106                 }
1107                 if (st.st_size < 0) {
1108                         error("\"%s\" has negative size", local_path);
1109                         goto fail;
1110                 }
1111                 if ((u_int64_t)st.st_size > size) {
1112                         error("Unable to resume download of \"%s\": "
1113                             "local file is larger than remote", local_path);
1114  fail:
1115                         do_close(conn, handle, handle_len);
1116                         buffer_free(&msg);
1117                         free(handle);
1118                         if (local_fd != -1)
1119                                 close(local_fd);
1120                         return -1;
1121                 }
1122                 offset = highwater = st.st_size;
1123         }
1124
1125         /* Read from remote and write to local */
1126         write_error = read_error = write_errno = num_req = 0;
1127         max_req = 1;
1128         progress_counter = offset;
1129
1130         if (showprogress && size != 0)
1131                 start_progress_meter(remote_path, size, &progress_counter);
1132
1133         while (num_req > 0 || max_req > 0) {
1134                 char *data;
1135                 u_int len;
1136
1137                 /*
1138                  * Simulate EOF on interrupt: stop sending new requests and
1139                  * allow outstanding requests to drain gracefully
1140                  */
1141                 if (interrupted) {
1142                         if (num_req == 0) /* If we haven't started yet... */
1143                                 break;
1144                         max_req = 0;
1145                 }
1146
1147                 /* Send some more requests */
1148                 while (num_req < max_req) {
1149                         debug3("Request range %llu -> %llu (%d/%d)",
1150                             (unsigned long long)offset,
1151                             (unsigned long long)offset + buflen - 1,
1152                             num_req, max_req);
1153                         req = xcalloc(1, sizeof(*req));
1154                         req->id = conn->msg_id++;
1155                         req->len = buflen;
1156                         req->offset = offset;
1157                         offset += buflen;
1158                         num_req++;
1159                         TAILQ_INSERT_TAIL(&requests, req, tq);
1160                         send_read_request(conn, req->id, req->offset,
1161                             req->len, handle, handle_len);
1162                 }
1163
1164                 buffer_clear(&msg);
1165                 get_msg(conn, &msg);
1166                 type = buffer_get_char(&msg);
1167                 id = buffer_get_int(&msg);
1168                 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1169
1170                 /* Find the request in our queue */
1171                 for (req = TAILQ_FIRST(&requests);
1172                     req != NULL && req->id != id;
1173                     req = TAILQ_NEXT(req, tq))
1174                         ;
1175                 if (req == NULL)
1176                         fatal("Unexpected reply %u", id);
1177
1178                 switch (type) {
1179                 case SSH2_FXP_STATUS:
1180                         status = buffer_get_int(&msg);
1181                         if (status != SSH2_FX_EOF)
1182                                 read_error = 1;
1183                         max_req = 0;
1184                         TAILQ_REMOVE(&requests, req, tq);
1185                         free(req);
1186                         num_req--;
1187                         break;
1188                 case SSH2_FXP_DATA:
1189                         data = buffer_get_string(&msg, &len);
1190                         debug3("Received data %llu -> %llu",
1191                             (unsigned long long)req->offset,
1192                             (unsigned long long)req->offset + len - 1);
1193                         if (len > req->len)
1194                                 fatal("Received more data than asked for "
1195                                     "%u > %u", len, req->len);
1196                         if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1197                             atomicio(vwrite, local_fd, data, len) != len) &&
1198                             !write_error) {
1199                                 write_errno = errno;
1200                                 write_error = 1;
1201                                 max_req = 0;
1202                         }
1203                         else if (!reordered && req->offset <= highwater)
1204                                 highwater = req->offset + len;
1205                         else if (!reordered && req->offset > highwater)
1206                                 reordered = 1;
1207                         progress_counter += len;
1208                         free(data);
1209
1210                         if (len == req->len) {
1211                                 TAILQ_REMOVE(&requests, req, tq);
1212                                 free(req);
1213                                 num_req--;
1214                         } else {
1215                                 /* Resend the request for the missing data */
1216                                 debug3("Short data block, re-requesting "
1217                                     "%llu -> %llu (%2d)",
1218                                     (unsigned long long)req->offset + len,
1219                                     (unsigned long long)req->offset +
1220                                     req->len - 1, num_req);
1221                                 req->id = conn->msg_id++;
1222                                 req->len -= len;
1223                                 req->offset += len;
1224                                 send_read_request(conn, req->id,
1225                                     req->offset, req->len, handle, handle_len);
1226                                 /* Reduce the request size */
1227                                 if (len < buflen)
1228                                         buflen = MAX(MIN_READ_SIZE, len);
1229                         }
1230                         if (max_req > 0) { /* max_req = 0 iff EOF received */
1231                                 if (size > 0 && offset > size) {
1232                                         /* Only one request at a time
1233                                          * after the expected EOF */
1234                                         debug3("Finish at %llu (%2d)",
1235                                             (unsigned long long)offset,
1236                                             num_req);
1237                                         max_req = 1;
1238                                 } else if (max_req <= conn->num_requests) {
1239                                         ++max_req;
1240                                 }
1241                         }
1242                         break;
1243                 default:
1244                         fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1245                             SSH2_FXP_DATA, type);
1246                 }
1247         }
1248
1249         if (showprogress && size)
1250                 stop_progress_meter();
1251
1252         /* Sanity check */
1253         if (TAILQ_FIRST(&requests) != NULL)
1254                 fatal("Transfer complete, but requests still in queue");
1255         /* Truncate at highest contiguous point to avoid holes on interrupt */
1256         if (read_error || write_error || interrupted) {
1257                 if (reordered && resume_flag) {
1258                         error("Unable to resume download of \"%s\": "
1259                             "server reordered requests", local_path);
1260                 }
1261                 debug("truncating at %llu", (unsigned long long)highwater);
1262                 ftruncate(local_fd, highwater);
1263         }
1264         if (read_error) {
1265                 error("Couldn't read from remote file \"%s\" : %s",
1266                     remote_path, fx2txt(status));
1267                 status = -1;
1268                 do_close(conn, handle, handle_len);
1269         } else if (write_error) {
1270                 error("Couldn't write to \"%s\": %s", local_path,
1271                     strerror(write_errno));
1272                 status = -1;
1273                 do_close(conn, handle, handle_len);
1274         } else {
1275                 status = do_close(conn, handle, handle_len);
1276                 if (interrupted || status != SSH2_FX_OK)
1277                         status = -1;
1278                 /* Override umask and utimes if asked */
1279 #ifdef HAVE_FCHMOD
1280                 if (preserve_flag && fchmod(local_fd, mode) == -1)
1281 #else
1282                 if (preserve_flag && chmod(local_path, mode) == -1)
1283 #endif /* HAVE_FCHMOD */
1284                         error("Couldn't set mode on \"%s\": %s", local_path,
1285                             strerror(errno));
1286                 if (preserve_flag &&
1287                     (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1288                         struct timeval tv[2];
1289                         tv[0].tv_sec = a->atime;
1290                         tv[1].tv_sec = a->mtime;
1291                         tv[0].tv_usec = tv[1].tv_usec = 0;
1292                         if (utimes(local_path, tv) == -1)
1293                                 error("Can't set times on \"%s\": %s",
1294                                     local_path, strerror(errno));
1295                 }
1296                 if (fsync_flag) {
1297                         debug("syncing \"%s\"", local_path);
1298                         if (fsync(local_fd) == -1)
1299                                 error("Couldn't sync file \"%s\": %s",
1300                                     local_path, strerror(errno));
1301                 }
1302         }
1303         close(local_fd);
1304         buffer_free(&msg);
1305         free(handle);
1306
1307         return(status);
1308 }
1309
1310 static int
1311 download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1312     Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1313     int fsync_flag)
1314 {
1315         int i, ret = 0;
1316         SFTP_DIRENT **dir_entries;
1317         char *filename, *new_src, *new_dst;
1318         mode_t mode = 0777;
1319
1320         if (depth >= MAX_DIR_DEPTH) {
1321                 error("Maximum directory depth exceeded: %d levels", depth);
1322                 return -1;
1323         }
1324
1325         if (dirattrib == NULL &&
1326             (dirattrib = do_stat(conn, src, 1)) == NULL) {
1327                 error("Unable to stat remote directory \"%s\"", src);
1328                 return -1;
1329         }
1330         if (!S_ISDIR(dirattrib->perm)) {
1331                 error("\"%s\" is not a directory", src);
1332                 return -1;
1333         }
1334         if (print_flag)
1335                 printf("Retrieving %s\n", src);
1336
1337         if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1338                 mode = dirattrib->perm & 01777;
1339         else {
1340                 debug("Server did not send permissions for "
1341                     "directory \"%s\"", dst);
1342         }
1343
1344         if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1345                 error("mkdir %s: %s", dst, strerror(errno));
1346                 return -1;
1347         }
1348
1349         if (do_readdir(conn, src, &dir_entries) == -1) {
1350                 error("%s: Failed to get directory contents", src);
1351                 return -1;
1352         }
1353
1354         for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1355                 filename = dir_entries[i]->filename;
1356
1357                 new_dst = path_append(dst, filename);
1358                 new_src = path_append(src, filename);
1359
1360                 if (S_ISDIR(dir_entries[i]->a.perm)) {
1361                         if (strcmp(filename, ".") == 0 ||
1362                             strcmp(filename, "..") == 0)
1363                                 continue;
1364                         if (download_dir_internal(conn, new_src, new_dst,
1365                             depth + 1, &(dir_entries[i]->a), preserve_flag,
1366                             print_flag, resume_flag, fsync_flag) == -1)
1367                                 ret = -1;
1368                 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1369                         if (do_download(conn, new_src, new_dst,
1370                             &(dir_entries[i]->a), preserve_flag,
1371                             resume_flag, fsync_flag) == -1) {
1372                                 error("Download of file %s to %s failed",
1373                                     new_src, new_dst);
1374                                 ret = -1;
1375                         }
1376                 } else
1377                         logit("%s: not a regular file\n", new_src);
1378
1379                 free(new_dst);
1380                 free(new_src);
1381         }
1382
1383         if (preserve_flag) {
1384                 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1385                         struct timeval tv[2];
1386                         tv[0].tv_sec = dirattrib->atime;
1387                         tv[1].tv_sec = dirattrib->mtime;
1388                         tv[0].tv_usec = tv[1].tv_usec = 0;
1389                         if (utimes(dst, tv) == -1)
1390                                 error("Can't set times on \"%s\": %s",
1391                                     dst, strerror(errno));
1392                 } else
1393                         debug("Server did not send times for directory "
1394                             "\"%s\"", dst);
1395         }
1396
1397         free_sftp_dirents(dir_entries);
1398
1399         return ret;
1400 }
1401
1402 int
1403 download_dir(struct sftp_conn *conn, char *src, char *dst,
1404     Attrib *dirattrib, int preserve_flag, int print_flag,
1405     int resume_flag, int fsync_flag)
1406 {
1407         char *src_canon;
1408         int ret;
1409
1410         if ((src_canon = do_realpath(conn, src)) == NULL) {
1411                 error("Unable to canonicalize path \"%s\"", src);
1412                 return -1;
1413         }
1414
1415         ret = download_dir_internal(conn, src_canon, dst, 0,
1416             dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
1417         free(src_canon);
1418         return ret;
1419 }
1420
1421 int
1422 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1423     int preserve_flag, int resume, int fsync_flag)
1424 {
1425         int local_fd;
1426         int status = SSH2_FX_OK;
1427         u_int handle_len, id, type;
1428         off_t offset, progress_counter;
1429         char *handle, *data;
1430         Buffer msg;
1431         struct stat sb;
1432         Attrib a, *c = NULL;
1433         u_int32_t startid;
1434         u_int32_t ackid;
1435         struct outstanding_ack {
1436                 u_int id;
1437                 u_int len;
1438                 off_t offset;
1439                 TAILQ_ENTRY(outstanding_ack) tq;
1440         };
1441         TAILQ_HEAD(ackhead, outstanding_ack) acks;
1442         struct outstanding_ack *ack = NULL;
1443
1444         TAILQ_INIT(&acks);
1445
1446         if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1447                 error("Couldn't open local file \"%s\" for reading: %s",
1448                     local_path, strerror(errno));
1449                 return(-1);
1450         }
1451         if (fstat(local_fd, &sb) == -1) {
1452                 error("Couldn't fstat local file \"%s\": %s",
1453                     local_path, strerror(errno));
1454                 close(local_fd);
1455                 return(-1);
1456         }
1457         if (!S_ISREG(sb.st_mode)) {
1458                 error("%s is not a regular file", local_path);
1459                 close(local_fd);
1460                 return(-1);
1461         }
1462         stat_to_attrib(&sb, &a);
1463
1464         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1465         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1466         a.perm &= 0777;
1467         if (!preserve_flag)
1468                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1469
1470         if (resume) {
1471                 /* Get remote file size if it exists */
1472                 if ((c = do_stat(conn, remote_path, 0)) == NULL) {
1473                         close(local_fd);                
1474                         return -1;
1475                 }
1476
1477                 if ((off_t)c->size >= sb.st_size) {
1478                         error("destination file bigger or same size as "
1479                               "source file");
1480                         close(local_fd);
1481                         return -1;
1482                 }
1483
1484                 if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) {
1485                         close(local_fd);
1486                         return -1;
1487                 }
1488         }
1489
1490         buffer_init(&msg);
1491
1492         /* Send open request */
1493         id = conn->msg_id++;
1494         buffer_put_char(&msg, SSH2_FXP_OPEN);
1495         buffer_put_int(&msg, id);
1496         buffer_put_cstring(&msg, remote_path);
1497         buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|
1498                       (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC));
1499         encode_attrib(&msg, &a);
1500         send_msg(conn, &msg);
1501         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1502
1503         buffer_clear(&msg);
1504
1505         handle = get_handle(conn, id, &handle_len,
1506             "remote open(\"%s\")", remote_path);
1507         if (handle == NULL) {
1508                 close(local_fd);
1509                 buffer_free(&msg);
1510                 return -1;
1511         }
1512
1513         startid = ackid = id + 1;
1514         data = xmalloc(conn->transfer_buflen);
1515
1516         /* Read from local and write to remote */
1517         offset = progress_counter = (resume ? c->size : 0);
1518         if (showprogress)
1519                 start_progress_meter(local_path, sb.st_size,
1520                     &progress_counter);
1521
1522         for (;;) {
1523                 int len;
1524
1525                 /*
1526                  * Can't use atomicio here because it returns 0 on EOF,
1527                  * thus losing the last block of the file.
1528                  * Simulate an EOF on interrupt, allowing ACKs from the
1529                  * server to drain.
1530                  */
1531                 if (interrupted || status != SSH2_FX_OK)
1532                         len = 0;
1533                 else do
1534                         len = read(local_fd, data, conn->transfer_buflen);
1535                 while ((len == -1) &&
1536                     (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
1537
1538                 if (len == -1)
1539                         fatal("Couldn't read from \"%s\": %s", local_path,
1540                             strerror(errno));
1541
1542                 if (len != 0) {
1543                         ack = xcalloc(1, sizeof(*ack));
1544                         ack->id = ++id;
1545                         ack->offset = offset;
1546                         ack->len = len;
1547                         TAILQ_INSERT_TAIL(&acks, ack, tq);
1548
1549                         buffer_clear(&msg);
1550                         buffer_put_char(&msg, SSH2_FXP_WRITE);
1551                         buffer_put_int(&msg, ack->id);
1552                         buffer_put_string(&msg, handle, handle_len);
1553                         buffer_put_int64(&msg, offset);
1554                         buffer_put_string(&msg, data, len);
1555                         send_msg(conn, &msg);
1556                         debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1557                             id, (unsigned long long)offset, len);
1558                 } else if (TAILQ_FIRST(&acks) == NULL)
1559                         break;
1560
1561                 if (ack == NULL)
1562                         fatal("Unexpected ACK %u", id);
1563
1564                 if (id == startid || len == 0 ||
1565                     id - ackid >= conn->num_requests) {
1566                         u_int r_id;
1567
1568                         buffer_clear(&msg);
1569                         get_msg(conn, &msg);
1570                         type = buffer_get_char(&msg);
1571                         r_id = buffer_get_int(&msg);
1572
1573                         if (type != SSH2_FXP_STATUS)
1574                                 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1575                                     "got %d", SSH2_FXP_STATUS, type);
1576
1577                         status = buffer_get_int(&msg);
1578                         debug3("SSH2_FXP_STATUS %d", status);
1579
1580                         /* Find the request in our queue */
1581                         for (ack = TAILQ_FIRST(&acks);
1582                             ack != NULL && ack->id != r_id;
1583                             ack = TAILQ_NEXT(ack, tq))
1584                                 ;
1585                         if (ack == NULL)
1586                                 fatal("Can't find request for ID %u", r_id);
1587                         TAILQ_REMOVE(&acks, ack, tq);
1588                         debug3("In write loop, ack for %u %u bytes at %lld",
1589                             ack->id, ack->len, (long long)ack->offset);
1590                         ++ackid;
1591                         progress_counter += ack->len;
1592                         free(ack);
1593                 }
1594                 offset += len;
1595                 if (offset < 0)
1596                         fatal("%s: offset < 0", __func__);
1597         }
1598         buffer_free(&msg);
1599
1600         if (showprogress)
1601                 stop_progress_meter();
1602         free(data);
1603
1604         if (status != SSH2_FX_OK) {
1605                 error("Couldn't write to remote file \"%s\": %s",
1606                     remote_path, fx2txt(status));
1607                 status = -1;
1608         }
1609
1610         if (close(local_fd) == -1) {
1611                 error("Couldn't close local file \"%s\": %s", local_path,
1612                     strerror(errno));
1613                 status = -1;
1614         }
1615
1616         /* Override umask and utimes if asked */
1617         if (preserve_flag)
1618                 do_fsetstat(conn, handle, handle_len, &a);
1619
1620         if (fsync_flag)
1621                 (void)do_fsync(conn, handle, handle_len);
1622
1623         if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1624                 status = -1;
1625         free(handle);
1626
1627         return status;
1628 }
1629
1630 static int
1631 upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1632     int preserve_flag, int print_flag, int resume, int fsync_flag)
1633 {
1634         int ret = 0, status;
1635         DIR *dirp;
1636         struct dirent *dp;
1637         char *filename, *new_src, *new_dst;
1638         struct stat sb;
1639         Attrib a;
1640
1641         if (depth >= MAX_DIR_DEPTH) {
1642                 error("Maximum directory depth exceeded: %d levels", depth);
1643                 return -1;
1644         }
1645
1646         if (stat(src, &sb) == -1) {
1647                 error("Couldn't stat directory \"%s\": %s",
1648                     src, strerror(errno));
1649                 return -1;
1650         }
1651         if (!S_ISDIR(sb.st_mode)) {
1652                 error("\"%s\" is not a directory", src);
1653                 return -1;
1654         }
1655         if (print_flag)
1656                 printf("Entering %s\n", src);
1657
1658         attrib_clear(&a);
1659         stat_to_attrib(&sb, &a);
1660         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1661         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1662         a.perm &= 01777;
1663         if (!preserve_flag)
1664                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1665
1666         status = do_mkdir(conn, dst, &a, 0);
1667         /*
1668          * we lack a portable status for errno EEXIST,
1669          * so if we get a SSH2_FX_FAILURE back we must check
1670          * if it was created successfully.
1671          */
1672         if (status != SSH2_FX_OK) {
1673                 if (status != SSH2_FX_FAILURE)
1674                         return -1;
1675                 if (do_stat(conn, dst, 0) == NULL)
1676                         return -1;
1677         }
1678
1679         if ((dirp = opendir(src)) == NULL) {
1680                 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1681                 return -1;
1682         }
1683
1684         while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1685                 if (dp->d_ino == 0)
1686                         continue;
1687                 filename = dp->d_name;
1688                 new_dst = path_append(dst, filename);
1689                 new_src = path_append(src, filename);
1690
1691                 if (lstat(new_src, &sb) == -1) {
1692                         logit("%s: lstat failed: %s", filename,
1693                             strerror(errno));
1694                         ret = -1;
1695                 } else if (S_ISDIR(sb.st_mode)) {
1696                         if (strcmp(filename, ".") == 0 ||
1697                             strcmp(filename, "..") == 0)
1698                                 continue;
1699
1700                         if (upload_dir_internal(conn, new_src, new_dst,
1701                             depth + 1, preserve_flag, print_flag, resume,
1702                             fsync_flag) == -1)
1703                                 ret = -1;
1704                 } else if (S_ISREG(sb.st_mode)) {
1705                         if (do_upload(conn, new_src, new_dst,
1706                             preserve_flag, resume, fsync_flag) == -1) {
1707                                 error("Uploading of file %s to %s failed!",
1708                                     new_src, new_dst);
1709                                 ret = -1;
1710                         }
1711                 } else
1712                         logit("%s: not a regular file\n", filename);
1713                 free(new_dst);
1714                 free(new_src);
1715         }
1716
1717         do_setstat(conn, dst, &a);
1718
1719         (void) closedir(dirp);
1720         return ret;
1721 }
1722
1723 int
1724 upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag,
1725     int print_flag, int resume, int fsync_flag)
1726 {
1727         char *dst_canon;
1728         int ret;
1729
1730         if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1731                 error("Unable to canonicalize path \"%s\"", dst);
1732                 return -1;
1733         }
1734
1735         ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
1736             print_flag, resume, fsync_flag);
1737
1738         free(dst_canon);
1739         return ret;
1740 }
1741
1742 char *
1743 path_append(char *p1, char *p2)
1744 {
1745         char *ret;
1746         size_t len = strlen(p1) + strlen(p2) + 2;
1747
1748         ret = xmalloc(len);
1749         strlcpy(ret, p1, len);
1750         if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1751                 strlcat(ret, "/", len);
1752         strlcat(ret, p2, len);
1753
1754         return(ret);
1755 }
1756