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