blob: 542af8b18fc101e67fabe259a2aed5e37a34727b [file] [log] [blame]
Damien Miller6c81fee2013-11-08 12:19:55 +11001/* $OpenBSD: sftp-client.c,v 1.108 2013/11/08 00:39:15 djm Exp $ */
Damien Miller33804262001-02-04 23:20:18 +11002/*
Damien Miller4e60ed72004-02-17 17:07:59 +11003 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
Damien Miller33804262001-02-04 23:20:18 +11004 *
Damien Miller4e60ed72004-02-17 17:07:59 +11005 * 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.
Damien Miller33804262001-02-04 23:20:18 +11008 *
Damien Miller4e60ed72004-02-17 17:07:59 +11009 * 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.
Damien Miller33804262001-02-04 23:20:18 +110016 */
17
18/* XXX: memleaks */
19/* XXX: signed vs unsigned */
Damien Miller3db5f532002-02-13 14:10:32 +110020/* XXX: remove all logging, only return status codes */
Damien Miller33804262001-02-04 23:20:18 +110021/* XXX: copy between two remote sites */
22
23#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110024
25#include <sys/types.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100026#include <sys/param.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100027#ifdef HAVE_SYS_STATVFS_H
Damien Millerd671e5a2008-05-19 14:53:33 +100028#include <sys/statvfs.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100029#endif
Damien Millerd7834352006-08-05 12:39:39 +100030#include "openbsd-compat/sys-queue.h"
Damien Millerf17883e2006-03-15 11:45:54 +110031#ifdef HAVE_SYS_STAT_H
32# include <sys/stat.h>
33#endif
Damien Miller9aec9192006-08-05 10:57:45 +100034#ifdef HAVE_SYS_TIME_H
35# include <sys/time.h>
36#endif
Damien Millerd7834352006-08-05 12:39:39 +100037#include <sys/uio.h>
Damien Miller57cf6382006-07-10 21:13:46 +100038
Darren Tucker1b0dd172009-10-07 08:37:48 +110039#include <dirent.h>
Darren Tucker39972492006-07-12 22:22:46 +100040#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100041#include <fcntl.h>
42#include <signal.h>
Damien Millerd7834352006-08-05 12:39:39 +100043#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100044#include <stdio.h>
Damien Millere3476ed2006-07-24 14:13:33 +100045#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100046#include <unistd.h>
Damien Miller16a13332002-02-13 14:03:56 +110047
Damien Miller33804262001-02-04 23:20:18 +110048#include "xmalloc.h"
Damien Millerd7834352006-08-05 12:39:39 +100049#include "buffer.h"
Damien Miller33804262001-02-04 23:20:18 +110050#include "log.h"
51#include "atomicio.h"
Damien Miller62d57f62003-01-10 21:43:24 +110052#include "progressmeter.h"
Damien Miller3f941882006-03-31 23:13:02 +110053#include "misc.h"
Damien Miller33804262001-02-04 23:20:18 +110054
55#include "sftp.h"
56#include "sftp-common.h"
57#include "sftp-client.h"
58
Darren Tuckercdf547a2004-05-24 10:12:19 +100059extern volatile sig_atomic_t interrupted;
Damien Miller62d57f62003-01-10 21:43:24 +110060extern int showprogress;
61
Damien Miller0c8d8f62006-03-15 11:34:25 +110062/* Minimum amount of data to read at a time */
Damien Miller16a13332002-02-13 14:03:56 +110063#define MIN_READ_SIZE 512
64
Darren Tucker1b0dd172009-10-07 08:37:48 +110065/* Maximum depth to descend in directory trees */
66#define MAX_DIR_DEPTH 64
67
Damien Miller3db5f532002-02-13 14:10:32 +110068struct sftp_conn {
69 int fd_in;
70 int fd_out;
71 u_int transfer_buflen;
72 u_int num_requests;
73 u_int version;
74 u_int msg_id;
Damien Millerd671e5a2008-05-19 14:53:33 +100075#define SFTP_EXT_POSIX_RENAME 0x00000001
76#define SFTP_EXT_STATVFS 0x00000002
77#define SFTP_EXT_FSTATVFS 0x00000004
Darren Tuckeraf1f9092010-12-05 09:02:47 +110078#define SFTP_EXT_HARDLINK 0x00000008
Damien Millerf29238e2013-10-17 11:48:52 +110079#define SFTP_EXT_FSYNC 0x00000010
Damien Miller7a3e1d02008-03-27 10:59:57 +110080 u_int exts;
Damien Miller65e42f82010-09-24 22:15:11 +100081 u_int64_t limit_kbps;
82 struct bwlimit bwlimit_in, bwlimit_out;
Damien Miller3db5f532002-02-13 14:10:32 +110083};
Ben Lindstrom288cc392001-02-09 02:58:04 +000084
Darren Tuckerc22f0902009-10-07 08:24:19 +110085static char *
Damien Miller65e42f82010-09-24 22:15:11 +100086get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
87 const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
88
89/* ARGSUSED */
90static int
91sftpio(void *_bwlimit, size_t amount)
92{
93 struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
94
95 bandwidth_limit(bwlimit, amount);
96 return 0;
97}
Darren Tuckerc22f0902009-10-07 08:24:19 +110098
Ben Lindstrombba81212001-06-25 05:01:22 +000099static void
Damien Miller65e42f82010-09-24 22:15:11 +1000100send_msg(struct sftp_conn *conn, Buffer *m)
Damien Miller33804262001-02-04 23:20:18 +1100101{
Damien Millera7f3aaa2003-01-10 21:43:58 +1100102 u_char mlen[4];
Damien Miller58ca98b2006-04-23 12:06:35 +1000103 struct iovec iov[2];
Damien Miller33804262001-02-04 23:20:18 +1100104
Damien Miller54446182006-01-02 23:40:50 +1100105 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
Damien Millera7f3aaa2003-01-10 21:43:58 +1100106 fatal("Outbound message too long %u", buffer_len(m));
Damien Miller33804262001-02-04 23:20:18 +1100107
Damien Millera7f3aaa2003-01-10 21:43:58 +1100108 /* Send length first */
Damien Miller3f941882006-03-31 23:13:02 +1100109 put_u32(mlen, buffer_len(m));
Damien Miller58ca98b2006-04-23 12:06:35 +1000110 iov[0].iov_base = mlen;
111 iov[0].iov_len = sizeof(mlen);
112 iov[1].iov_base = buffer_ptr(m);
113 iov[1].iov_len = buffer_len(m);
Damien Millerd7834352006-08-05 12:39:39 +1000114
Damien Miller65e42f82010-09-24 22:15:11 +1000115 if (atomiciov6(writev, conn->fd_out, iov, 2,
Damien Miller0d032412013-07-25 11:56:52 +1000116 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
Damien Miller65e42f82010-09-24 22:15:11 +1000117 buffer_len(m) + sizeof(mlen))
Damien Millera7f3aaa2003-01-10 21:43:58 +1100118 fatal("Couldn't send packet: %s", strerror(errno));
119
120 buffer_clear(m);
Damien Miller33804262001-02-04 23:20:18 +1100121}
122
Ben Lindstrombba81212001-06-25 05:01:22 +0000123static void
Damien Miller65e42f82010-09-24 22:15:11 +1000124get_msg(struct sftp_conn *conn, Buffer *m)
Damien Miller33804262001-02-04 23:20:18 +1100125{
Damien Millera7f3aaa2003-01-10 21:43:58 +1100126 u_int msg_len;
Damien Miller33804262001-02-04 23:20:18 +1100127
Damien Millera7f3aaa2003-01-10 21:43:58 +1100128 buffer_append_space(m, 4);
Damien Miller65e42f82010-09-24 22:15:11 +1000129 if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
130 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
Damien Millerb253cc42005-05-26 12:23:44 +1000131 if (errno == EPIPE)
132 fatal("Connection closed");
133 else
134 fatal("Couldn't read packet: %s", strerror(errno));
135 }
Damien Miller33804262001-02-04 23:20:18 +1100136
Damien Millera7f3aaa2003-01-10 21:43:58 +1100137 msg_len = buffer_get_int(m);
Damien Miller54446182006-01-02 23:40:50 +1100138 if (msg_len > SFTP_MAX_MSG_LENGTH)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000139 fatal("Received message too long %u", msg_len);
Damien Miller33804262001-02-04 23:20:18 +1100140
Damien Millera7f3aaa2003-01-10 21:43:58 +1100141 buffer_append_space(m, msg_len);
Damien Miller65e42f82010-09-24 22:15:11 +1000142 if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
143 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
144 != msg_len) {
Damien Millerb253cc42005-05-26 12:23:44 +1000145 if (errno == EPIPE)
146 fatal("Connection closed");
147 else
148 fatal("Read packet: %s", strerror(errno));
149 }
Damien Miller33804262001-02-04 23:20:18 +1100150}
151
Ben Lindstrombba81212001-06-25 05:01:22 +0000152static void
Damien Miller65e42f82010-09-24 22:15:11 +1000153send_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
Damien Miller33804262001-02-04 23:20:18 +1100154 u_int len)
155{
156 Buffer msg;
157
158 buffer_init(&msg);
159 buffer_put_char(&msg, code);
160 buffer_put_int(&msg, id);
161 buffer_put_string(&msg, s, len);
Damien Miller65e42f82010-09-24 22:15:11 +1000162 send_msg(conn, &msg);
163 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100164 buffer_free(&msg);
165}
166
Ben Lindstrombba81212001-06-25 05:01:22 +0000167static void
Damien Miller65e42f82010-09-24 22:15:11 +1000168send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
169 char *s, u_int len, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100170{
171 Buffer msg;
172
173 buffer_init(&msg);
174 buffer_put_char(&msg, code);
175 buffer_put_int(&msg, id);
176 buffer_put_string(&msg, s, len);
177 encode_attrib(&msg, a);
Damien Miller65e42f82010-09-24 22:15:11 +1000178 send_msg(conn, &msg);
179 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100180 buffer_free(&msg);
181}
182
Ben Lindstrombba81212001-06-25 05:01:22 +0000183static u_int
Damien Miller65e42f82010-09-24 22:15:11 +1000184get_status(struct sftp_conn *conn, u_int expected_id)
Damien Miller33804262001-02-04 23:20:18 +1100185{
186 Buffer msg;
187 u_int type, id, status;
188
189 buffer_init(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000190 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100191 type = buffer_get_char(&msg);
192 id = buffer_get_int(&msg);
193
194 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000195 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100196 if (type != SSH2_FXP_STATUS)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000197 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100198 SSH2_FXP_STATUS, type);
199
200 status = buffer_get_int(&msg);
201 buffer_free(&msg);
202
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000203 debug3("SSH2_FXP_STATUS %u", status);
Damien Miller33804262001-02-04 23:20:18 +1100204
Damien Miller65e42f82010-09-24 22:15:11 +1000205 return status;
Damien Miller33804262001-02-04 23:20:18 +1100206}
207
Ben Lindstrombba81212001-06-25 05:01:22 +0000208static char *
Damien Miller65e42f82010-09-24 22:15:11 +1000209get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
210 const char *errfmt, ...)
Damien Miller33804262001-02-04 23:20:18 +1100211{
212 Buffer msg;
213 u_int type, id;
Darren Tuckerc22f0902009-10-07 08:24:19 +1100214 char *handle, errmsg[256];
215 va_list args;
216 int status;
217
218 va_start(args, errfmt);
219 if (errfmt != NULL)
220 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
221 va_end(args);
Damien Miller33804262001-02-04 23:20:18 +1100222
223 buffer_init(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000224 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100225 type = buffer_get_char(&msg);
226 id = buffer_get_int(&msg);
227
228 if (id != expected_id)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100229 fatal("%s: ID mismatch (%u != %u)",
230 errfmt == NULL ? __func__ : errmsg, id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100231 if (type == SSH2_FXP_STATUS) {
Darren Tuckerc22f0902009-10-07 08:24:19 +1100232 status = buffer_get_int(&msg);
233 if (errfmt != NULL)
234 error("%s: %s", errmsg, fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100235 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100236 return(NULL);
237 } else if (type != SSH2_FXP_HANDLE)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100238 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
239 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
Damien Miller33804262001-02-04 23:20:18 +1100240
241 handle = buffer_get_string(&msg, len);
242 buffer_free(&msg);
243
244 return(handle);
245}
246
Ben Lindstrombba81212001-06-25 05:01:22 +0000247static Attrib *
Damien Miller65e42f82010-09-24 22:15:11 +1000248get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100249{
250 Buffer msg;
251 u_int type, id;
252 Attrib *a;
253
254 buffer_init(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000255 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100256
257 type = buffer_get_char(&msg);
258 id = buffer_get_int(&msg);
259
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000260 debug3("Received stat reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100261 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000262 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100263 if (type == SSH2_FXP_STATUS) {
264 int status = buffer_get_int(&msg);
265
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000266 if (quiet)
267 debug("Couldn't stat remote file: %s", fx2txt(status));
268 else
269 error("Couldn't stat remote file: %s", fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100270 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100271 return(NULL);
272 } else if (type != SSH2_FXP_ATTRS) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000273 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100274 SSH2_FXP_ATTRS, type);
275 }
276 a = decode_attrib(&msg);
277 buffer_free(&msg);
278
279 return(a);
280}
281
Damien Millerd671e5a2008-05-19 14:53:33 +1000282static int
Damien Miller65e42f82010-09-24 22:15:11 +1000283get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
284 u_int expected_id, int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +1000285{
286 Buffer msg;
287 u_int type, id, flag;
288
289 buffer_init(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000290 get_msg(conn, &msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000291
292 type = buffer_get_char(&msg);
293 id = buffer_get_int(&msg);
294
295 debug3("Received statvfs reply T:%u I:%u", type, id);
296 if (id != expected_id)
297 fatal("ID mismatch (%u != %u)", id, expected_id);
298 if (type == SSH2_FXP_STATUS) {
299 int status = buffer_get_int(&msg);
300
301 if (quiet)
302 debug("Couldn't statvfs: %s", fx2txt(status));
303 else
304 error("Couldn't statvfs: %s", fx2txt(status));
305 buffer_free(&msg);
306 return -1;
307 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
308 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
309 SSH2_FXP_EXTENDED_REPLY, type);
310 }
311
312 bzero(st, sizeof(*st));
Darren Tucker7b598892008-06-09 22:49:36 +1000313 st->f_bsize = buffer_get_int64(&msg);
314 st->f_frsize = buffer_get_int64(&msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000315 st->f_blocks = buffer_get_int64(&msg);
316 st->f_bfree = buffer_get_int64(&msg);
317 st->f_bavail = buffer_get_int64(&msg);
318 st->f_files = buffer_get_int64(&msg);
319 st->f_ffree = buffer_get_int64(&msg);
320 st->f_favail = buffer_get_int64(&msg);
Darren Tucker294b8412008-06-08 12:57:08 +1000321 st->f_fsid = buffer_get_int64(&msg);
Darren Tucker7b598892008-06-09 22:49:36 +1000322 flag = buffer_get_int64(&msg);
323 st->f_namemax = buffer_get_int64(&msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000324
325 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
326 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
327
328 buffer_free(&msg);
329
330 return 0;
331}
332
Damien Miller3db5f532002-02-13 14:10:32 +1100333struct sftp_conn *
Damien Miller65e42f82010-09-24 22:15:11 +1000334do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
335 u_int64_t limit_kbps)
Damien Miller33804262001-02-04 23:20:18 +1100336{
Damien Miller65e42f82010-09-24 22:15:11 +1000337 u_int type;
Damien Miller33804262001-02-04 23:20:18 +1100338 Buffer msg;
Damien Miller3db5f532002-02-13 14:10:32 +1100339 struct sftp_conn *ret;
Damien Miller33804262001-02-04 23:20:18 +1100340
Damien Millerfec029f2013-08-21 02:42:12 +1000341 ret = xcalloc(1, sizeof(*ret));
342 ret->msg_id = 1;
Damien Miller65e42f82010-09-24 22:15:11 +1000343 ret->fd_in = fd_in;
344 ret->fd_out = fd_out;
345 ret->transfer_buflen = transfer_buflen;
346 ret->num_requests = num_requests;
347 ret->exts = 0;
348 ret->limit_kbps = 0;
349
Damien Miller33804262001-02-04 23:20:18 +1100350 buffer_init(&msg);
351 buffer_put_char(&msg, SSH2_FXP_INIT);
352 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller65e42f82010-09-24 22:15:11 +1000353 send_msg(ret, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100354
355 buffer_clear(&msg);
356
Damien Miller65e42f82010-09-24 22:15:11 +1000357 get_msg(ret, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100358
Kevin Stevesef4eea92001-02-05 12:42:17 +0000359 /* Expecting a VERSION reply */
Damien Miller33804262001-02-04 23:20:18 +1100360 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000361 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
Damien Miller33804262001-02-04 23:20:18 +1100362 type);
363 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100364 return(NULL);
Damien Miller33804262001-02-04 23:20:18 +1100365 }
Damien Miller65e42f82010-09-24 22:15:11 +1000366 ret->version = buffer_get_int(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100367
Damien Miller65e42f82010-09-24 22:15:11 +1000368 debug2("Remote version: %u", ret->version);
Damien Miller33804262001-02-04 23:20:18 +1100369
370 /* Check for extensions */
371 while (buffer_len(&msg) > 0) {
372 char *name = buffer_get_string(&msg, NULL);
373 char *value = buffer_get_string(&msg, NULL);
Darren Tuckera64ab332008-06-13 07:01:29 +1000374 int known = 0;
Damien Miller33804262001-02-04 23:20:18 +1100375
Damien Millerd671e5a2008-05-19 14:53:33 +1000376 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
Darren Tuckera64ab332008-06-13 07:01:29 +1000377 strcmp(value, "1") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000378 ret->exts |= SFTP_EXT_POSIX_RENAME;
Darren Tuckera64ab332008-06-13 07:01:29 +1000379 known = 1;
380 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
381 strcmp(value, "2") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000382 ret->exts |= SFTP_EXT_STATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000383 known = 1;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100384 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
Darren Tuckera64ab332008-06-13 07:01:29 +1000385 strcmp(value, "2") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000386 ret->exts |= SFTP_EXT_FSTATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000387 known = 1;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100388 } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
389 strcmp(value, "1") == 0) {
390 ret->exts |= SFTP_EXT_HARDLINK;
391 known = 1;
Damien Millerf29238e2013-10-17 11:48:52 +1100392 } else if (strcmp(name, "fsync@openssh.com") == 0 &&
393 strcmp(value, "1") == 0) {
394 ret->exts |= SFTP_EXT_FSYNC;
395 known = 1;
Darren Tuckera64ab332008-06-13 07:01:29 +1000396 }
397 if (known) {
398 debug2("Server supports extension \"%s\" revision %s",
399 name, value);
400 } else {
401 debug2("Unrecognised server extension \"%s\"", name);
402 }
Darren Tuckera627d422013-06-02 07:31:17 +1000403 free(name);
404 free(value);
Damien Miller33804262001-02-04 23:20:18 +1100405 }
406
407 buffer_free(&msg);
Damien Miller058316f2001-03-08 10:08:49 +1100408
Damien Miller3db5f532002-02-13 14:10:32 +1100409 /* Some filexfer v.0 servers don't support large packets */
Damien Miller65e42f82010-09-24 22:15:11 +1000410 if (ret->version == 0)
Ben Lindstroma1d81142002-04-02 20:58:11 +0000411 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
Damien Miller3db5f532002-02-13 14:10:32 +1100412
Damien Miller65e42f82010-09-24 22:15:11 +1000413 ret->limit_kbps = limit_kbps;
414 if (ret->limit_kbps > 0) {
415 bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
416 ret->transfer_buflen);
417 bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
418 ret->transfer_buflen);
419 }
420
421 return ret;
Damien Miller3db5f532002-02-13 14:10:32 +1100422}
423
424u_int
425sftp_proto_version(struct sftp_conn *conn)
426{
Damien Miller65e42f82010-09-24 22:15:11 +1000427 return conn->version;
Damien Miller33804262001-02-04 23:20:18 +1100428}
429
430int
Damien Miller3db5f532002-02-13 14:10:32 +1100431do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
Damien Miller33804262001-02-04 23:20:18 +1100432{
433 u_int id, status;
434 Buffer msg;
435
436 buffer_init(&msg);
437
Damien Miller3db5f532002-02-13 14:10:32 +1100438 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100439 buffer_put_char(&msg, SSH2_FXP_CLOSE);
440 buffer_put_int(&msg, id);
441 buffer_put_string(&msg, handle, handle_len);
Damien Miller65e42f82010-09-24 22:15:11 +1000442 send_msg(conn, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000443 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100444
Damien Miller65e42f82010-09-24 22:15:11 +1000445 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100446 if (status != SSH2_FX_OK)
447 error("Couldn't close file: %s", fx2txt(status));
448
449 buffer_free(&msg);
450
Damien Miller65e42f82010-09-24 22:15:11 +1000451 return status;
Damien Miller33804262001-02-04 23:20:18 +1100452}
453
Damien Miller4870afd2001-03-14 10:27:09 +1100454
Ben Lindstrombba81212001-06-25 05:01:22 +0000455static int
Damien Millerbda5c842013-10-15 12:05:58 +1100456do_lsreaddir(struct sftp_conn *conn, char *path, int print_flag,
Damien Miller4870afd2001-03-14 10:27:09 +1100457 SFTP_DIRENT ***dir)
Damien Miller33804262001-02-04 23:20:18 +1100458{
459 Buffer msg;
Damien Millereccb9de2005-06-17 12:59:34 +1000460 u_int count, type, id, handle_len, i, expected_id, ents = 0;
Damien Miller33804262001-02-04 23:20:18 +1100461 char *handle;
462
Damien Miller3db5f532002-02-13 14:10:32 +1100463 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100464
465 buffer_init(&msg);
466 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
467 buffer_put_int(&msg, id);
468 buffer_put_cstring(&msg, path);
Damien Miller65e42f82010-09-24 22:15:11 +1000469 send_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100470
Damien Miller65e42f82010-09-24 22:15:11 +1000471 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +1100472 "remote readdir(\"%s\")", path);
Damien Miller57c38ac2011-09-22 21:42:45 +1000473 if (handle == NULL) {
474 buffer_free(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000475 return -1;
Damien Miller57c38ac2011-09-22 21:42:45 +1000476 }
Damien Miller33804262001-02-04 23:20:18 +1100477
Damien Miller4870afd2001-03-14 10:27:09 +1100478 if (dir) {
479 ents = 0;
Damien Miller6c81fee2013-11-08 12:19:55 +1100480 *dir = xcalloc(1, sizeof(**dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100481 (*dir)[0] = NULL;
482 }
Damien Miller4870afd2001-03-14 10:27:09 +1100483
Darren Tuckercdf547a2004-05-24 10:12:19 +1000484 for (; !interrupted;) {
Damien Miller3db5f532002-02-13 14:10:32 +1100485 id = expected_id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100486
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000487 debug3("Sending SSH2_FXP_READDIR I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100488
489 buffer_clear(&msg);
490 buffer_put_char(&msg, SSH2_FXP_READDIR);
491 buffer_put_int(&msg, id);
492 buffer_put_string(&msg, handle, handle_len);
Damien Miller65e42f82010-09-24 22:15:11 +1000493 send_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100494
495 buffer_clear(&msg);
496
Damien Miller65e42f82010-09-24 22:15:11 +1000497 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100498
499 type = buffer_get_char(&msg);
500 id = buffer_get_int(&msg);
501
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000502 debug3("Received reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100503
504 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000505 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100506
507 if (type == SSH2_FXP_STATUS) {
508 int status = buffer_get_int(&msg);
509
510 debug3("Received SSH2_FXP_STATUS %d", status);
511
512 if (status == SSH2_FX_EOF) {
513 break;
514 } else {
515 error("Couldn't read directory: %s",
516 fx2txt(status));
Damien Miller3db5f532002-02-13 14:10:32 +1100517 do_close(conn, handle, handle_len);
Darren Tuckera627d422013-06-02 07:31:17 +1000518 free(handle);
Damien Miller57c38ac2011-09-22 21:42:45 +1000519 buffer_free(&msg);
Ben Lindstrom10ac33f2001-02-10 21:53:40 +0000520 return(status);
Damien Miller33804262001-02-04 23:20:18 +1100521 }
522 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000523 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100524 SSH2_FXP_NAME, type);
525
526 count = buffer_get_int(&msg);
Damien Millerd7686fd2001-02-10 00:40:03 +1100527 if (count == 0)
528 break;
529 debug3("Received %d SSH2_FXP_NAME responses", count);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100530 for (i = 0; i < count; i++) {
Damien Miller33804262001-02-04 23:20:18 +1100531 char *filename, *longname;
532 Attrib *a;
533
534 filename = buffer_get_string(&msg, NULL);
535 longname = buffer_get_string(&msg, NULL);
536 a = decode_attrib(&msg);
537
Damien Millerbda5c842013-10-15 12:05:58 +1100538 if (print_flag)
Damien Miller4870afd2001-03-14 10:27:09 +1100539 printf("%s\n", longname);
540
Darren Tucker1b0dd172009-10-07 08:37:48 +1100541 /*
542 * Directory entries should never contain '/'
543 * These can be used to attack recursive ops
544 * (e.g. send '../../../../etc/passwd')
545 */
546 if (strchr(filename, '/') != NULL) {
547 error("Server sent suspect path \"%s\" "
548 "during readdir of \"%s\"", filename, path);
549 goto next;
550 }
551
Damien Miller4870afd2001-03-14 10:27:09 +1100552 if (dir) {
Damien Miller36812092006-03-26 14:22:47 +1100553 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
Damien Miller6c81fee2013-11-08 12:19:55 +1100554 (*dir)[ents] = xcalloc(1, sizeof(***dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100555 (*dir)[ents]->filename = xstrdup(filename);
556 (*dir)[ents]->longname = xstrdup(longname);
557 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
558 (*dir)[++ents] = NULL;
559 }
Darren Tucker1b0dd172009-10-07 08:37:48 +1100560 next:
Darren Tuckera627d422013-06-02 07:31:17 +1000561 free(filename);
562 free(longname);
Damien Miller33804262001-02-04 23:20:18 +1100563 }
564 }
565
566 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100567 do_close(conn, handle, handle_len);
Darren Tuckera627d422013-06-02 07:31:17 +1000568 free(handle);
Damien Miller33804262001-02-04 23:20:18 +1100569
Darren Tuckercdf547a2004-05-24 10:12:19 +1000570 /* Don't return partial matches on interrupt */
571 if (interrupted && dir != NULL && *dir != NULL) {
572 free_sftp_dirents(*dir);
Damien Miller6c81fee2013-11-08 12:19:55 +1100573 *dir = xcalloc(1, sizeof(**dir));
Darren Tuckercdf547a2004-05-24 10:12:19 +1000574 **dir = NULL;
575 }
576
Damien Miller65e42f82010-09-24 22:15:11 +1000577 return 0;
Damien Miller33804262001-02-04 23:20:18 +1100578}
579
580int
Damien Miller3db5f532002-02-13 14:10:32 +1100581do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
Damien Miller4870afd2001-03-14 10:27:09 +1100582{
Damien Miller3db5f532002-02-13 14:10:32 +1100583 return(do_lsreaddir(conn, path, 0, dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100584}
585
586void free_sftp_dirents(SFTP_DIRENT **s)
587{
588 int i;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100589
590 for (i = 0; s[i]; i++) {
Darren Tuckera627d422013-06-02 07:31:17 +1000591 free(s[i]->filename);
592 free(s[i]->longname);
593 free(s[i]);
Damien Miller4870afd2001-03-14 10:27:09 +1100594 }
Darren Tuckera627d422013-06-02 07:31:17 +1000595 free(s);
Damien Miller4870afd2001-03-14 10:27:09 +1100596}
597
598int
Damien Miller3db5f532002-02-13 14:10:32 +1100599do_rm(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100600{
601 u_int status, id;
602
603 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
604
Damien Miller3db5f532002-02-13 14:10:32 +1100605 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000606 send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
607 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100608 if (status != SSH2_FX_OK)
609 error("Couldn't delete file: %s", fx2txt(status));
610 return(status);
611}
612
613int
Damien Millerbda5c842013-10-15 12:05:58 +1100614do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int print_flag)
Damien Miller33804262001-02-04 23:20:18 +1100615{
616 u_int status, id;
617
Damien Miller3db5f532002-02-13 14:10:32 +1100618 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000619 send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
Damien Miller33804262001-02-04 23:20:18 +1100620 strlen(path), a);
621
Damien Miller65e42f82010-09-24 22:15:11 +1000622 status = get_status(conn, id);
Damien Millerbda5c842013-10-15 12:05:58 +1100623 if (status != SSH2_FX_OK && print_flag)
Damien Miller33804262001-02-04 23:20:18 +1100624 error("Couldn't create directory: %s", fx2txt(status));
625
626 return(status);
627}
628
629int
Damien Miller3db5f532002-02-13 14:10:32 +1100630do_rmdir(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100631{
632 u_int status, id;
633
Damien Miller3db5f532002-02-13 14:10:32 +1100634 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000635 send_string_request(conn, id, SSH2_FXP_RMDIR, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100636 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100637
Damien Miller65e42f82010-09-24 22:15:11 +1000638 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100639 if (status != SSH2_FX_OK)
640 error("Couldn't remove directory: %s", fx2txt(status));
641
642 return(status);
643}
644
645Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100646do_stat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100647{
648 u_int id;
649
Damien Miller3db5f532002-02-13 14:10:32 +1100650 id = conn->msg_id++;
651
Damien Miller65e42f82010-09-24 22:15:11 +1000652 send_string_request(conn, id,
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000653 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
Damien Miller3db5f532002-02-13 14:10:32 +1100654 path, strlen(path));
655
Damien Miller65e42f82010-09-24 22:15:11 +1000656 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100657}
658
659Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100660do_lstat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100661{
662 u_int id;
663
Damien Miller3db5f532002-02-13 14:10:32 +1100664 if (conn->version == 0) {
665 if (quiet)
666 debug("Server version does not support lstat operation");
667 else
Damien Miller996acd22003-04-09 20:59:48 +1000668 logit("Server version does not support lstat operation");
Ben Lindstromf26ff5b2002-04-02 21:00:31 +0000669 return(do_stat(conn, path, quiet));
Damien Miller3db5f532002-02-13 14:10:32 +1100670 }
671
672 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000673 send_string_request(conn, id, SSH2_FXP_LSTAT, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100674 strlen(path));
675
Damien Miller65e42f82010-09-24 22:15:11 +1000676 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100677}
678
Damien Millercfe23d32008-02-10 22:20:44 +1100679#ifdef notyet
Damien Miller33804262001-02-04 23:20:18 +1100680Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100681do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100682{
683 u_int id;
684
Damien Miller3db5f532002-02-13 14:10:32 +1100685 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000686 send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
Damien Miller3db5f532002-02-13 14:10:32 +1100687 handle_len);
688
Damien Miller65e42f82010-09-24 22:15:11 +1000689 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100690}
Damien Millercfe23d32008-02-10 22:20:44 +1100691#endif
Damien Miller33804262001-02-04 23:20:18 +1100692
693int
Damien Miller3db5f532002-02-13 14:10:32 +1100694do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100695{
696 u_int status, id;
697
Damien Miller3db5f532002-02-13 14:10:32 +1100698 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000699 send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
Damien Miller33804262001-02-04 23:20:18 +1100700 strlen(path), a);
701
Damien Miller65e42f82010-09-24 22:15:11 +1000702 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100703 if (status != SSH2_FX_OK)
704 error("Couldn't setstat on \"%s\": %s", path,
705 fx2txt(status));
706
707 return(status);
708}
709
710int
Damien Miller3db5f532002-02-13 14:10:32 +1100711do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
Damien Miller33804262001-02-04 23:20:18 +1100712 Attrib *a)
713{
714 u_int status, id;
715
Damien Miller3db5f532002-02-13 14:10:32 +1100716 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000717 send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
Damien Miller33804262001-02-04 23:20:18 +1100718 handle_len, a);
719
Damien Miller65e42f82010-09-24 22:15:11 +1000720 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100721 if (status != SSH2_FX_OK)
722 error("Couldn't fsetstat: %s", fx2txt(status));
723
724 return(status);
725}
726
727char *
Damien Miller3db5f532002-02-13 14:10:32 +1100728do_realpath(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100729{
730 Buffer msg;
731 u_int type, expected_id, count, id;
732 char *filename, *longname;
733 Attrib *a;
734
Damien Miller3db5f532002-02-13 14:10:32 +1100735 expected_id = id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000736 send_string_request(conn, id, SSH2_FXP_REALPATH, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100737 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100738
739 buffer_init(&msg);
740
Damien Miller65e42f82010-09-24 22:15:11 +1000741 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100742 type = buffer_get_char(&msg);
743 id = buffer_get_int(&msg);
744
745 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000746 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100747
748 if (type == SSH2_FXP_STATUS) {
749 u_int status = buffer_get_int(&msg);
750
Damien Millerf29238e2013-10-17 11:48:52 +1100751 error("Couldn't canonicalize: %s", fx2txt(status));
Damien Miller49566312010-06-26 09:38:23 +1000752 buffer_free(&msg);
753 return NULL;
Damien Miller33804262001-02-04 23:20:18 +1100754 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000755 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100756 SSH2_FXP_NAME, type);
757
758 count = buffer_get_int(&msg);
759 if (count != 1)
760 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
761
762 filename = buffer_get_string(&msg, NULL);
763 longname = buffer_get_string(&msg, NULL);
764 a = decode_attrib(&msg);
765
Darren Tucker4908d442012-07-02 22:15:38 +1000766 debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
767 (unsigned long)a->size);
Damien Miller33804262001-02-04 23:20:18 +1100768
Darren Tuckera627d422013-06-02 07:31:17 +1000769 free(longname);
Damien Miller33804262001-02-04 23:20:18 +1100770
771 buffer_free(&msg);
772
773 return(filename);
774}
775
776int
Damien Millerc7dba122013-08-21 02:41:15 +1000777do_rename(struct sftp_conn *conn, char *oldpath, char *newpath,
778 int force_legacy)
Damien Miller33804262001-02-04 23:20:18 +1100779{
780 Buffer msg;
781 u_int status, id;
Damien Millerc7dba122013-08-21 02:41:15 +1000782 int use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
Damien Miller33804262001-02-04 23:20:18 +1100783
784 buffer_init(&msg);
785
786 /* Send rename request */
Damien Miller3db5f532002-02-13 14:10:32 +1100787 id = conn->msg_id++;
Damien Millerc7dba122013-08-21 02:41:15 +1000788 if (use_ext) {
Damien Miller7a3e1d02008-03-27 10:59:57 +1100789 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
790 buffer_put_int(&msg, id);
791 buffer_put_cstring(&msg, "posix-rename@openssh.com");
792 } else {
793 buffer_put_char(&msg, SSH2_FXP_RENAME);
794 buffer_put_int(&msg, id);
795 }
Damien Miller33804262001-02-04 23:20:18 +1100796 buffer_put_cstring(&msg, oldpath);
797 buffer_put_cstring(&msg, newpath);
Damien Miller65e42f82010-09-24 22:15:11 +1000798 send_msg(conn, &msg);
Damien Miller7a3e1d02008-03-27 10:59:57 +1100799 debug3("Sent message %s \"%s\" -> \"%s\"",
Damien Millerc7dba122013-08-21 02:41:15 +1000800 use_ext ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME",
801 oldpath, newpath);
Damien Miller33804262001-02-04 23:20:18 +1100802 buffer_free(&msg);
803
Damien Miller65e42f82010-09-24 22:15:11 +1000804 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100805 if (status != SSH2_FX_OK)
Damien Miller3db5f532002-02-13 14:10:32 +1100806 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
807 newpath, fx2txt(status));
Damien Miller33804262001-02-04 23:20:18 +1100808
809 return(status);
810}
811
812int
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100813do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
814{
815 Buffer msg;
816 u_int status, id;
817
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100818 if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
819 error("Server does not support hardlink@openssh.com extension");
820 return -1;
821 }
822
Damien Miller3decdba2011-09-22 21:41:05 +1000823 buffer_init(&msg);
824
825 /* Send link request */
826 id = conn->msg_id++;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100827 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
828 buffer_put_int(&msg, id);
829 buffer_put_cstring(&msg, "hardlink@openssh.com");
830 buffer_put_cstring(&msg, oldpath);
831 buffer_put_cstring(&msg, newpath);
832 send_msg(conn, &msg);
833 debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
834 oldpath, newpath);
835 buffer_free(&msg);
836
837 status = get_status(conn, id);
838 if (status != SSH2_FX_OK)
839 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
840 newpath, fx2txt(status));
841
842 return(status);
843}
844
845int
Damien Miller3db5f532002-02-13 14:10:32 +1100846do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
Damien Miller058316f2001-03-08 10:08:49 +1100847{
848 Buffer msg;
849 u_int status, id;
850
Damien Miller3db5f532002-02-13 14:10:32 +1100851 if (conn->version < 3) {
852 error("This server does not support the symlink operation");
853 return(SSH2_FX_OP_UNSUPPORTED);
854 }
855
Damien Miller058316f2001-03-08 10:08:49 +1100856 buffer_init(&msg);
857
Darren Tuckerdca6a4d2004-04-19 22:10:52 +1000858 /* Send symlink request */
Damien Miller3db5f532002-02-13 14:10:32 +1100859 id = conn->msg_id++;
Damien Miller058316f2001-03-08 10:08:49 +1100860 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
861 buffer_put_int(&msg, id);
862 buffer_put_cstring(&msg, oldpath);
863 buffer_put_cstring(&msg, newpath);
Damien Miller65e42f82010-09-24 22:15:11 +1000864 send_msg(conn, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100865 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
866 newpath);
867 buffer_free(&msg);
868
Damien Miller65e42f82010-09-24 22:15:11 +1000869 status = get_status(conn, id);
Damien Miller058316f2001-03-08 10:08:49 +1100870 if (status != SSH2_FX_OK)
Ben Lindstrom8e879cf2002-11-09 15:48:49 +0000871 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
Damien Miller3db5f532002-02-13 14:10:32 +1100872 newpath, fx2txt(status));
Damien Miller058316f2001-03-08 10:08:49 +1100873
874 return(status);
875}
876
Damien Millerf29238e2013-10-17 11:48:52 +1100877int
878do_fsync(struct sftp_conn *conn, char *handle, u_int handle_len)
879{
880 Buffer msg;
881 u_int status, id;
882
883 /* Silently return if the extension is not supported */
884 if ((conn->exts & SFTP_EXT_FSYNC) == 0)
885 return -1;
886
887 buffer_init(&msg);
888
889 /* Send fsync request */
890 id = conn->msg_id++;
891
892 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
893 buffer_put_int(&msg, id);
894 buffer_put_cstring(&msg, "fsync@openssh.com");
895 buffer_put_string(&msg, handle, handle_len);
896 send_msg(conn, &msg);
897 debug3("Sent message fsync@openssh.com I:%u", id);
898 buffer_free(&msg);
899
900 status = get_status(conn, id);
901 if (status != SSH2_FX_OK)
902 error("Couldn't sync file: %s", fx2txt(status));
903
904 return status;
905}
906
Damien Millercfe23d32008-02-10 22:20:44 +1100907#ifdef notyet
Damien Miller058316f2001-03-08 10:08:49 +1100908char *
Damien Miller3db5f532002-02-13 14:10:32 +1100909do_readlink(struct sftp_conn *conn, char *path)
Damien Miller058316f2001-03-08 10:08:49 +1100910{
911 Buffer msg;
912 u_int type, expected_id, count, id;
913 char *filename, *longname;
914 Attrib *a;
915
Damien Miller3db5f532002-02-13 14:10:32 +1100916 expected_id = id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000917 send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
Damien Miller058316f2001-03-08 10:08:49 +1100918
919 buffer_init(&msg);
920
Damien Miller65e42f82010-09-24 22:15:11 +1000921 get_msg(conn, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100922 type = buffer_get_char(&msg);
923 id = buffer_get_int(&msg);
924
925 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000926 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller058316f2001-03-08 10:08:49 +1100927
928 if (type == SSH2_FXP_STATUS) {
929 u_int status = buffer_get_int(&msg);
930
931 error("Couldn't readlink: %s", fx2txt(status));
Damien Miller3decdba2011-09-22 21:41:05 +1000932 buffer_free(&msg);
Damien Miller058316f2001-03-08 10:08:49 +1100933 return(NULL);
934 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000935 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller058316f2001-03-08 10:08:49 +1100936 SSH2_FXP_NAME, type);
937
938 count = buffer_get_int(&msg);
939 if (count != 1)
940 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
941
942 filename = buffer_get_string(&msg, NULL);
943 longname = buffer_get_string(&msg, NULL);
944 a = decode_attrib(&msg);
945
946 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
947
Darren Tuckera627d422013-06-02 07:31:17 +1000948 free(longname);
Damien Miller058316f2001-03-08 10:08:49 +1100949
950 buffer_free(&msg);
951
952 return(filename);
953}
Damien Millercfe23d32008-02-10 22:20:44 +1100954#endif
Damien Miller058316f2001-03-08 10:08:49 +1100955
Damien Millerd671e5a2008-05-19 14:53:33 +1000956int
Darren Tucker7b598892008-06-09 22:49:36 +1000957do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
Damien Millerd671e5a2008-05-19 14:53:33 +1000958 int quiet)
959{
960 Buffer msg;
961 u_int id;
962
963 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
964 error("Server does not support statvfs@openssh.com extension");
965 return -1;
966 }
967
968 id = conn->msg_id++;
969
970 buffer_init(&msg);
971 buffer_clear(&msg);
972 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
973 buffer_put_int(&msg, id);
974 buffer_put_cstring(&msg, "statvfs@openssh.com");
975 buffer_put_cstring(&msg, path);
Damien Miller65e42f82010-09-24 22:15:11 +1000976 send_msg(conn, &msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000977 buffer_free(&msg);
978
Damien Miller65e42f82010-09-24 22:15:11 +1000979 return get_decode_statvfs(conn, st, id, quiet);
Damien Millerd671e5a2008-05-19 14:53:33 +1000980}
981
982#ifdef notyet
983int
984do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
Darren Tucker7b598892008-06-09 22:49:36 +1000985 struct sftp_statvfs *st, int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +1000986{
987 Buffer msg;
988 u_int id;
989
990 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
991 error("Server does not support fstatvfs@openssh.com extension");
992 return -1;
993 }
994
995 id = conn->msg_id++;
996
997 buffer_init(&msg);
998 buffer_clear(&msg);
999 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
1000 buffer_put_int(&msg, id);
1001 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
1002 buffer_put_string(&msg, handle, handle_len);
Damien Miller65e42f82010-09-24 22:15:11 +10001003 send_msg(conn, &msg);
Damien Millerd671e5a2008-05-19 14:53:33 +10001004 buffer_free(&msg);
1005
Damien Miller65e42f82010-09-24 22:15:11 +10001006 return get_decode_statvfs(conn, st, id, quiet);
Damien Millerd671e5a2008-05-19 14:53:33 +10001007}
1008#endif
1009
Damien Miller16a13332002-02-13 14:03:56 +11001010static void
Damien Miller65e42f82010-09-24 22:15:11 +10001011send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1012 u_int len, char *handle, u_int handle_len)
Damien Miller16a13332002-02-13 14:03:56 +11001013{
1014 Buffer msg;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001015
Damien Miller16a13332002-02-13 14:03:56 +11001016 buffer_init(&msg);
1017 buffer_clear(&msg);
1018 buffer_put_char(&msg, SSH2_FXP_READ);
1019 buffer_put_int(&msg, id);
1020 buffer_put_string(&msg, handle, handle_len);
1021 buffer_put_int64(&msg, offset);
1022 buffer_put_int(&msg, len);
Damien Miller65e42f82010-09-24 22:15:11 +10001023 send_msg(conn, &msg);
Damien Miller16a13332002-02-13 14:03:56 +11001024 buffer_free(&msg);
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001025}
Damien Miller16a13332002-02-13 14:03:56 +11001026
Damien Miller058316f2001-03-08 10:08:49 +11001027int
Damien Miller3db5f532002-02-13 14:10:32 +11001028do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
Damien Millerf29238e2013-10-17 11:48:52 +11001029 Attrib *a, int preserve_flag, int resume_flag, int fsync_flag)
Damien Miller33804262001-02-04 23:20:18 +11001030{
Darren Tucker1b0dd172009-10-07 08:37:48 +11001031 Attrib junk;
Damien Miller16a13332002-02-13 14:03:56 +11001032 Buffer msg;
1033 char *handle;
Damien Miller0d032412013-07-25 11:56:52 +10001034 int local_fd = -1, status = 0, write_error;
1035 int read_error, write_errno, reordered = 0;
1036 u_int64_t offset = 0, size, highwater;
Damien Millereccb9de2005-06-17 12:59:34 +10001037 u_int handle_len, mode, type, id, buflen, num_req, max_req;
Damien Miller62d57f62003-01-10 21:43:24 +11001038 off_t progress_counter;
Damien Miller0d032412013-07-25 11:56:52 +10001039 struct stat st;
Damien Miller16a13332002-02-13 14:03:56 +11001040 struct request {
1041 u_int id;
1042 u_int len;
1043 u_int64_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001044 TAILQ_ENTRY(request) tq;
Damien Miller16a13332002-02-13 14:03:56 +11001045 };
1046 TAILQ_HEAD(reqhead, request) requests;
1047 struct request *req;
1048
1049 TAILQ_INIT(&requests);
Damien Miller33804262001-02-04 23:20:18 +11001050
Darren Tucker1b0dd172009-10-07 08:37:48 +11001051 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1052 return -1;
Damien Miller33804262001-02-04 23:20:18 +11001053
Damien Miller9e720282008-06-29 22:46:35 +10001054 /* Do not preserve set[ug]id here, as we do not preserve ownership */
Damien Miller33804262001-02-04 23:20:18 +11001055 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
Damien Miller770b3742003-01-08 14:04:53 +11001056 mode = a->perm & 0777;
Damien Miller33804262001-02-04 23:20:18 +11001057 else
1058 mode = 0666;
1059
Ben Lindstromc8d1c302001-03-17 00:34:46 +00001060 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
Damien Miller5fa01fd2003-01-14 22:24:47 +11001061 (!S_ISREG(a->perm))) {
1062 error("Cannot download non-regular file: %s", remote_path);
Ben Lindstromc8d1c302001-03-17 00:34:46 +00001063 return(-1);
1064 }
1065
Damien Miller16a13332002-02-13 14:03:56 +11001066 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1067 size = a->size;
1068 else
1069 size = 0;
1070
Damien Miller3db5f532002-02-13 14:10:32 +11001071 buflen = conn->transfer_buflen;
Damien Miller33804262001-02-04 23:20:18 +11001072 buffer_init(&msg);
1073
1074 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001075 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +11001076 buffer_put_char(&msg, SSH2_FXP_OPEN);
1077 buffer_put_int(&msg, id);
1078 buffer_put_cstring(&msg, remote_path);
1079 buffer_put_int(&msg, SSH2_FXF_READ);
1080 attrib_clear(&junk); /* Send empty attributes */
1081 encode_attrib(&msg, &junk);
Damien Miller65e42f82010-09-24 22:15:11 +10001082 send_msg(conn, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001083 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001084
Damien Miller65e42f82010-09-24 22:15:11 +10001085 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +11001086 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001087 if (handle == NULL) {
1088 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +11001089 return(-1);
1090 }
1091
Damien Millerbda5c842013-10-15 12:05:58 +11001092 local_fd = open(local_path,
1093 O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
Damien Miller3db5f532002-02-13 14:10:32 +11001094 if (local_fd == -1) {
1095 error("Couldn't open local file \"%s\" for writing: %s",
1096 local_path, strerror(errno));
Damien Miller0d032412013-07-25 11:56:52 +10001097 goto fail;
1098 }
1099 offset = highwater = 0;
Damien Millerbda5c842013-10-15 12:05:58 +11001100 if (resume_flag) {
Damien Miller0d032412013-07-25 11:56:52 +10001101 if (fstat(local_fd, &st) == -1) {
1102 error("Unable to stat local file \"%s\": %s",
1103 local_path, strerror(errno));
1104 goto fail;
1105 }
1106 if ((size_t)st.st_size > size) {
1107 error("Unable to resume download of \"%s\": "
1108 "local file is larger than remote", local_path);
1109 fail:
1110 do_close(conn, handle, handle_len);
1111 buffer_free(&msg);
1112 free(handle);
1113 return -1;
1114 }
1115 offset = highwater = st.st_size;
Damien Miller3db5f532002-02-13 14:10:32 +11001116 }
1117
Damien Miller33804262001-02-04 23:20:18 +11001118 /* Read from remote and write to local */
Damien Miller0d032412013-07-25 11:56:52 +10001119 write_error = read_error = write_errno = num_req = 0;
Damien Miller16a13332002-02-13 14:03:56 +11001120 max_req = 1;
Damien Miller0d032412013-07-25 11:56:52 +10001121 progress_counter = offset;
Damien Miller62d57f62003-01-10 21:43:24 +11001122
Damien Miller9ba30692004-03-08 23:12:02 +11001123 if (showprogress && size != 0)
1124 start_progress_meter(remote_path, size, &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +11001125
Damien Miller16a13332002-02-13 14:03:56 +11001126 while (num_req > 0 || max_req > 0) {
Damien Miller33804262001-02-04 23:20:18 +11001127 char *data;
Damien Miller16a13332002-02-13 14:03:56 +11001128 u_int len;
Damien Miller33804262001-02-04 23:20:18 +11001129
Darren Tuckercdf547a2004-05-24 10:12:19 +10001130 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001131 * Simulate EOF on interrupt: stop sending new requests and
Darren Tuckercdf547a2004-05-24 10:12:19 +10001132 * allow outstanding requests to drain gracefully
1133 */
1134 if (interrupted) {
1135 if (num_req == 0) /* If we haven't started yet... */
1136 break;
1137 max_req = 0;
1138 }
1139
Damien Miller16a13332002-02-13 14:03:56 +11001140 /* Send some more requests */
1141 while (num_req < max_req) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001142 debug3("Request range %llu -> %llu (%d/%d)",
Ben Lindstromd45f28c2002-03-22 01:00:57 +00001143 (unsigned long long)offset,
1144 (unsigned long long)offset + buflen - 1,
1145 num_req, max_req);
Damien Miller6c81fee2013-11-08 12:19:55 +11001146 req = xcalloc(1, sizeof(*req));
Damien Miller3db5f532002-02-13 14:10:32 +11001147 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001148 req->len = buflen;
1149 req->offset = offset;
1150 offset += buflen;
1151 num_req++;
1152 TAILQ_INSERT_TAIL(&requests, req, tq);
Damien Miller65e42f82010-09-24 22:15:11 +10001153 send_read_request(conn, req->id, req->offset,
Damien Miller16a13332002-02-13 14:03:56 +11001154 req->len, handle, handle_len);
1155 }
Damien Miller33804262001-02-04 23:20:18 +11001156
1157 buffer_clear(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +10001158 get_msg(conn, &msg);
Damien Miller33804262001-02-04 23:20:18 +11001159 type = buffer_get_char(&msg);
1160 id = buffer_get_int(&msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001161 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
Damien Miller33804262001-02-04 23:20:18 +11001162
Damien Miller16a13332002-02-13 14:03:56 +11001163 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001164 for (req = TAILQ_FIRST(&requests);
Damien Miller16a13332002-02-13 14:03:56 +11001165 req != NULL && req->id != id;
1166 req = TAILQ_NEXT(req, tq))
1167 ;
1168 if (req == NULL)
1169 fatal("Unexpected reply %u", id);
1170
1171 switch (type) {
1172 case SSH2_FXP_STATUS:
1173 status = buffer_get_int(&msg);
1174 if (status != SSH2_FX_EOF)
1175 read_error = 1;
1176 max_req = 0;
1177 TAILQ_REMOVE(&requests, req, tq);
Darren Tuckera627d422013-06-02 07:31:17 +10001178 free(req);
Damien Miller16a13332002-02-13 14:03:56 +11001179 num_req--;
1180 break;
1181 case SSH2_FXP_DATA:
1182 data = buffer_get_string(&msg, &len);
Ben Lindstromeb505452002-03-22 01:03:15 +00001183 debug3("Received data %llu -> %llu",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001184 (unsigned long long)req->offset,
Ben Lindstromeb505452002-03-22 01:03:15 +00001185 (unsigned long long)req->offset + len - 1);
Damien Miller16a13332002-02-13 14:03:56 +11001186 if (len > req->len)
1187 fatal("Received more data than asked for "
Ben Lindstrom93576d92002-12-23 02:06:19 +00001188 "%u > %u", len, req->len);
Damien Miller16a13332002-02-13 14:03:56 +11001189 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
Darren Tucker9f63f222003-07-03 13:46:56 +10001190 atomicio(vwrite, local_fd, data, len) != len) &&
Damien Miller16a13332002-02-13 14:03:56 +11001191 !write_error) {
1192 write_errno = errno;
1193 write_error = 1;
1194 max_req = 0;
Damien Miller33804262001-02-04 23:20:18 +11001195 }
Damien Miller0d032412013-07-25 11:56:52 +10001196 else if (!reordered && req->offset <= highwater)
1197 highwater = req->offset + len;
1198 else if (!reordered && req->offset > highwater)
1199 reordered = 1;
Damien Miller62d57f62003-01-10 21:43:24 +11001200 progress_counter += len;
Darren Tuckera627d422013-06-02 07:31:17 +10001201 free(data);
Damien Miller16a13332002-02-13 14:03:56 +11001202
1203 if (len == req->len) {
1204 TAILQ_REMOVE(&requests, req, tq);
Darren Tuckera627d422013-06-02 07:31:17 +10001205 free(req);
Damien Miller16a13332002-02-13 14:03:56 +11001206 num_req--;
1207 } else {
1208 /* Resend the request for the missing data */
1209 debug3("Short data block, re-requesting "
Ben Lindstromeb505452002-03-22 01:03:15 +00001210 "%llu -> %llu (%2d)",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001211 (unsigned long long)req->offset + len,
Ben Lindstrom83b79e42002-03-22 01:05:27 +00001212 (unsigned long long)req->offset +
1213 req->len - 1, num_req);
Damien Miller3db5f532002-02-13 14:10:32 +11001214 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001215 req->len -= len;
1216 req->offset += len;
Damien Miller65e42f82010-09-24 22:15:11 +10001217 send_read_request(conn, req->id,
Damien Miller3db5f532002-02-13 14:10:32 +11001218 req->offset, req->len, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001219 /* Reduce the request size */
1220 if (len < buflen)
1221 buflen = MAX(MIN_READ_SIZE, len);
1222 }
1223 if (max_req > 0) { /* max_req = 0 iff EOF received */
1224 if (size > 0 && offset > size) {
1225 /* Only one request at a time
1226 * after the expected EOF */
1227 debug3("Finish at %llu (%2d)",
Ben Lindstromeb505452002-03-22 01:03:15 +00001228 (unsigned long long)offset,
1229 num_req);
Damien Miller16a13332002-02-13 14:03:56 +11001230 max_req = 1;
Darren Tuckercdf547a2004-05-24 10:12:19 +10001231 } else if (max_req <= conn->num_requests) {
Damien Miller16a13332002-02-13 14:03:56 +11001232 ++max_req;
1233 }
1234 }
1235 break;
1236 default:
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001237 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +11001238 SSH2_FXP_DATA, type);
1239 }
Damien Miller33804262001-02-04 23:20:18 +11001240 }
Damien Miller33804262001-02-04 23:20:18 +11001241
Damien Miller62d57f62003-01-10 21:43:24 +11001242 if (showprogress && size)
1243 stop_progress_meter();
1244
Damien Miller16a13332002-02-13 14:03:56 +11001245 /* Sanity check */
1246 if (TAILQ_FIRST(&requests) != NULL)
1247 fatal("Transfer complete, but requests still in queue");
Damien Miller0d032412013-07-25 11:56:52 +10001248 /* Truncate at highest contiguous point to avoid holes on interrupt */
1249 if (read_error || write_error || interrupted) {
Damien Millerbda5c842013-10-15 12:05:58 +11001250 if (reordered && resume_flag) {
Damien Miller0d032412013-07-25 11:56:52 +10001251 error("Unable to resume download of \"%s\": "
1252 "server reordered requests", local_path);
1253 }
1254 debug("truncating at %llu", (unsigned long long)highwater);
1255 ftruncate(local_fd, highwater);
1256 }
Damien Miller16a13332002-02-13 14:03:56 +11001257 if (read_error) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001258 error("Couldn't read from remote file \"%s\" : %s",
Damien Miller3db5f532002-02-13 14:10:32 +11001259 remote_path, fx2txt(status));
Damien Millerfec029f2013-08-21 02:42:12 +10001260 status = -1;
Damien Miller3db5f532002-02-13 14:10:32 +11001261 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001262 } else if (write_error) {
Damien Miller3db5f532002-02-13 14:10:32 +11001263 error("Couldn't write to \"%s\": %s", local_path,
1264 strerror(write_errno));
1265 status = -1;
1266 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001267 } else {
Damien Miller3db5f532002-02-13 14:10:32 +11001268 status = do_close(conn, handle, handle_len);
Damien Millerfec029f2013-08-21 02:42:12 +10001269 if (interrupted || status != SSH2_FX_OK)
Damien Miller0d032412013-07-25 11:56:52 +10001270 status = -1;
Damien Miller16a13332002-02-13 14:03:56 +11001271 /* Override umask and utimes if asked */
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001272#ifdef HAVE_FCHMOD
Damien Millerbda5c842013-10-15 12:05:58 +11001273 if (preserve_flag && fchmod(local_fd, mode) == -1)
Damien Millera8e06ce2003-11-21 23:48:55 +11001274#else
Damien Millerbda5c842013-10-15 12:05:58 +11001275 if (preserve_flag && chmod(local_path, mode) == -1)
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001276#endif /* HAVE_FCHMOD */
Damien Miller16a13332002-02-13 14:03:56 +11001277 error("Couldn't set mode on \"%s\": %s", local_path,
Ben Lindstrom93576d92002-12-23 02:06:19 +00001278 strerror(errno));
Damien Millerbda5c842013-10-15 12:05:58 +11001279 if (preserve_flag &&
1280 (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
Damien Miller16a13332002-02-13 14:03:56 +11001281 struct timeval tv[2];
1282 tv[0].tv_sec = a->atime;
1283 tv[1].tv_sec = a->mtime;
1284 tv[0].tv_usec = tv[1].tv_usec = 0;
1285 if (utimes(local_path, tv) == -1)
1286 error("Can't set times on \"%s\": %s",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001287 local_path, strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001288 }
Damien Millerf29238e2013-10-17 11:48:52 +11001289 if (fsync_flag) {
1290 debug("syncing \"%s\"", local_path);
1291 if (fsync(local_fd) == -1)
1292 error("Couldn't sync file \"%s\": %s",
1293 local_path, strerror(errno));
1294 }
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001295 }
Damien Millerd7686fd2001-02-10 00:40:03 +11001296 close(local_fd);
1297 buffer_free(&msg);
Darren Tuckera627d422013-06-02 07:31:17 +10001298 free(handle);
Damien Miller3db5f532002-02-13 14:10:32 +11001299
1300 return(status);
Damien Miller33804262001-02-04 23:20:18 +11001301}
1302
Darren Tucker1b0dd172009-10-07 08:37:48 +11001303static int
Damien Millerbda5c842013-10-15 12:05:58 +11001304download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
Damien Millerf29238e2013-10-17 11:48:52 +11001305 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1306 int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001307{
1308 int i, ret = 0;
1309 SFTP_DIRENT **dir_entries;
1310 char *filename, *new_src, *new_dst;
1311 mode_t mode = 0777;
1312
1313 if (depth >= MAX_DIR_DEPTH) {
1314 error("Maximum directory depth exceeded: %d levels", depth);
1315 return -1;
1316 }
1317
1318 if (dirattrib == NULL &&
1319 (dirattrib = do_stat(conn, src, 1)) == NULL) {
1320 error("Unable to stat remote directory \"%s\"", src);
1321 return -1;
1322 }
1323 if (!S_ISDIR(dirattrib->perm)) {
1324 error("\"%s\" is not a directory", src);
1325 return -1;
1326 }
Damien Millerbda5c842013-10-15 12:05:58 +11001327 if (print_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001328 printf("Retrieving %s\n", src);
1329
1330 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1331 mode = dirattrib->perm & 01777;
1332 else {
1333 debug("Server did not send permissions for "
1334 "directory \"%s\"", dst);
1335 }
1336
1337 if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1338 error("mkdir %s: %s", dst, strerror(errno));
1339 return -1;
1340 }
1341
1342 if (do_readdir(conn, src, &dir_entries) == -1) {
1343 error("%s: Failed to get directory contents", src);
1344 return -1;
1345 }
1346
1347 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1348 filename = dir_entries[i]->filename;
1349
1350 new_dst = path_append(dst, filename);
1351 new_src = path_append(src, filename);
1352
1353 if (S_ISDIR(dir_entries[i]->a.perm)) {
1354 if (strcmp(filename, ".") == 0 ||
1355 strcmp(filename, "..") == 0)
1356 continue;
1357 if (download_dir_internal(conn, new_src, new_dst,
Damien Millerbda5c842013-10-15 12:05:58 +11001358 depth + 1, &(dir_entries[i]->a), preserve_flag,
Damien Millerf29238e2013-10-17 11:48:52 +11001359 print_flag, resume_flag, fsync_flag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001360 ret = -1;
1361 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1362 if (do_download(conn, new_src, new_dst,
Damien Millerf29238e2013-10-17 11:48:52 +11001363 &(dir_entries[i]->a), preserve_flag,
1364 resume_flag, fsync_flag) == -1) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001365 error("Download of file %s to %s failed",
1366 new_src, new_dst);
1367 ret = -1;
1368 }
1369 } else
1370 logit("%s: not a regular file\n", new_src);
1371
Darren Tuckera627d422013-06-02 07:31:17 +10001372 free(new_dst);
1373 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001374 }
1375
Damien Millerbda5c842013-10-15 12:05:58 +11001376 if (preserve_flag) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001377 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1378 struct timeval tv[2];
1379 tv[0].tv_sec = dirattrib->atime;
1380 tv[1].tv_sec = dirattrib->mtime;
1381 tv[0].tv_usec = tv[1].tv_usec = 0;
1382 if (utimes(dst, tv) == -1)
1383 error("Can't set times on \"%s\": %s",
1384 dst, strerror(errno));
1385 } else
1386 debug("Server did not send times for directory "
1387 "\"%s\"", dst);
1388 }
1389
1390 free_sftp_dirents(dir_entries);
1391
1392 return ret;
1393}
1394
1395int
1396download_dir(struct sftp_conn *conn, char *src, char *dst,
Damien Millerf29238e2013-10-17 11:48:52 +11001397 Attrib *dirattrib, int preserve_flag, int print_flag,
1398 int resume_flag, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001399{
1400 char *src_canon;
1401 int ret;
1402
1403 if ((src_canon = do_realpath(conn, src)) == NULL) {
Damien Millerf29238e2013-10-17 11:48:52 +11001404 error("Unable to canonicalize path \"%s\"", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001405 return -1;
1406 }
1407
Damien Millerbda5c842013-10-15 12:05:58 +11001408 ret = download_dir_internal(conn, src_canon, dst, 0,
Damien Millerf29238e2013-10-17 11:48:52 +11001409 dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
Darren Tuckera627d422013-06-02 07:31:17 +10001410 free(src_canon);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001411 return ret;
1412}
1413
Damien Miller33804262001-02-04 23:20:18 +11001414int
Damien Miller3db5f532002-02-13 14:10:32 +11001415do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
Damien Millerf29238e2013-10-17 11:48:52 +11001416 int preserve_flag, int fsync_flag)
Damien Miller33804262001-02-04 23:20:18 +11001417{
Damien Milleracdf25b2008-02-10 22:27:24 +11001418 int local_fd;
1419 int status = SSH2_FX_OK;
Damien Miller5873dfd2002-02-13 14:04:37 +11001420 u_int handle_len, id, type;
Darren Tuckerc9a19912013-06-02 08:37:05 +10001421 off_t offset, progress_counter;
Damien Miller8829d362002-02-08 22:04:05 +11001422 char *handle, *data;
Damien Miller33804262001-02-04 23:20:18 +11001423 Buffer msg;
1424 struct stat sb;
1425 Attrib a;
Damien Miller16a13332002-02-13 14:03:56 +11001426 u_int32_t startid;
1427 u_int32_t ackid;
Damien Miller5873dfd2002-02-13 14:04:37 +11001428 struct outstanding_ack {
1429 u_int id;
1430 u_int len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001431 off_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001432 TAILQ_ENTRY(outstanding_ack) tq;
Damien Miller5873dfd2002-02-13 14:04:37 +11001433 };
1434 TAILQ_HEAD(ackhead, outstanding_ack) acks;
Damien Miller7cf17eb2004-06-15 10:28:56 +10001435 struct outstanding_ack *ack = NULL;
Damien Miller5873dfd2002-02-13 14:04:37 +11001436
1437 TAILQ_INIT(&acks);
Damien Miller33804262001-02-04 23:20:18 +11001438
1439 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1440 error("Couldn't open local file \"%s\" for reading: %s",
1441 local_path, strerror(errno));
1442 return(-1);
1443 }
1444 if (fstat(local_fd, &sb) == -1) {
1445 error("Couldn't fstat local file \"%s\": %s",
1446 local_path, strerror(errno));
1447 close(local_fd);
1448 return(-1);
1449 }
Damien Miller5fa01fd2003-01-14 22:24:47 +11001450 if (!S_ISREG(sb.st_mode)) {
1451 error("%s is not a regular file", local_path);
1452 close(local_fd);
1453 return(-1);
1454 }
Damien Miller33804262001-02-04 23:20:18 +11001455 stat_to_attrib(&sb, &a);
1456
1457 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1458 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1459 a.perm &= 0777;
Damien Millerbda5c842013-10-15 12:05:58 +11001460 if (!preserve_flag)
Damien Miller33804262001-02-04 23:20:18 +11001461 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1462
1463 buffer_init(&msg);
1464
1465 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001466 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +11001467 buffer_put_char(&msg, SSH2_FXP_OPEN);
1468 buffer_put_int(&msg, id);
1469 buffer_put_cstring(&msg, remote_path);
1470 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1471 encode_attrib(&msg, &a);
Damien Miller65e42f82010-09-24 22:15:11 +10001472 send_msg(conn, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001473 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001474
1475 buffer_clear(&msg);
1476
Damien Miller65e42f82010-09-24 22:15:11 +10001477 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +11001478 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001479 if (handle == NULL) {
1480 close(local_fd);
1481 buffer_free(&msg);
Damien Milleracdf25b2008-02-10 22:27:24 +11001482 return -1;
Damien Miller33804262001-02-04 23:20:18 +11001483 }
1484
Damien Miller16a13332002-02-13 14:03:56 +11001485 startid = ackid = id + 1;
Damien Miller3db5f532002-02-13 14:10:32 +11001486 data = xmalloc(conn->transfer_buflen);
Damien Miller8829d362002-02-08 22:04:05 +11001487
Damien Miller33804262001-02-04 23:20:18 +11001488 /* Read from local and write to remote */
Darren Tuckerc9a19912013-06-02 08:37:05 +10001489 offset = progress_counter = 0;
Damien Miller62d57f62003-01-10 21:43:24 +11001490 if (showprogress)
Darren Tuckerc9a19912013-06-02 08:37:05 +10001491 start_progress_meter(local_path, sb.st_size,
1492 &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +11001493
Damien Miller9f0f5c62001-12-21 14:45:46 +11001494 for (;;) {
Damien Miller33804262001-02-04 23:20:18 +11001495 int len;
Damien Miller33804262001-02-04 23:20:18 +11001496
1497 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001498 * Can't use atomicio here because it returns 0 on EOF,
Darren Tuckercdf547a2004-05-24 10:12:19 +10001499 * thus losing the last block of the file.
Darren Tuckerfc959702004-07-17 16:12:08 +10001500 * Simulate an EOF on interrupt, allowing ACKs from the
Darren Tuckercdf547a2004-05-24 10:12:19 +10001501 * server to drain.
Damien Miller33804262001-02-04 23:20:18 +11001502 */
Damien Milleracdf25b2008-02-10 22:27:24 +11001503 if (interrupted || status != SSH2_FX_OK)
Darren Tuckercdf547a2004-05-24 10:12:19 +10001504 len = 0;
1505 else do
Damien Miller3db5f532002-02-13 14:10:32 +11001506 len = read(local_fd, data, conn->transfer_buflen);
Damien Millerd8968ad2008-07-04 23:10:49 +10001507 while ((len == -1) &&
1508 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
Damien Miller33804262001-02-04 23:20:18 +11001509
1510 if (len == -1)
1511 fatal("Couldn't read from \"%s\": %s", local_path,
1512 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001513
1514 if (len != 0) {
Damien Miller6c81fee2013-11-08 12:19:55 +11001515 ack = xcalloc(1, sizeof(*ack));
Damien Miller5873dfd2002-02-13 14:04:37 +11001516 ack->id = ++id;
1517 ack->offset = offset;
1518 ack->len = len;
1519 TAILQ_INSERT_TAIL(&acks, ack, tq);
1520
Damien Miller16a13332002-02-13 14:03:56 +11001521 buffer_clear(&msg);
1522 buffer_put_char(&msg, SSH2_FXP_WRITE);
Damien Miller5873dfd2002-02-13 14:04:37 +11001523 buffer_put_int(&msg, ack->id);
Damien Miller16a13332002-02-13 14:03:56 +11001524 buffer_put_string(&msg, handle, handle_len);
1525 buffer_put_int64(&msg, offset);
1526 buffer_put_string(&msg, data, len);
Damien Miller65e42f82010-09-24 22:15:11 +10001527 send_msg(conn, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001528 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001529 id, (unsigned long long)offset, len);
Damien Miller5873dfd2002-02-13 14:04:37 +11001530 } else if (TAILQ_FIRST(&acks) == NULL)
Damien Miller33804262001-02-04 23:20:18 +11001531 break;
1532
Damien Miller5873dfd2002-02-13 14:04:37 +11001533 if (ack == NULL)
1534 fatal("Unexpected ACK %u", id);
1535
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001536 if (id == startid || len == 0 ||
Damien Miller3db5f532002-02-13 14:10:32 +11001537 id - ackid >= conn->num_requests) {
Ben Lindstrom5a6abda2002-06-09 19:41:48 +00001538 u_int r_id;
Ben Lindstrom06e95152002-04-06 04:16:45 +00001539
Damien Miller5873dfd2002-02-13 14:04:37 +11001540 buffer_clear(&msg);
Damien Miller65e42f82010-09-24 22:15:11 +10001541 get_msg(conn, &msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001542 type = buffer_get_char(&msg);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001543 r_id = buffer_get_int(&msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001544
1545 if (type != SSH2_FXP_STATUS)
1546 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1547 "got %d", SSH2_FXP_STATUS, type);
1548
1549 status = buffer_get_int(&msg);
1550 debug3("SSH2_FXP_STATUS %d", status);
1551
1552 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001553 for (ack = TAILQ_FIRST(&acks);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001554 ack != NULL && ack->id != r_id;
Damien Miller5873dfd2002-02-13 14:04:37 +11001555 ack = TAILQ_NEXT(ack, tq))
1556 ;
1557 if (ack == NULL)
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001558 fatal("Can't find request for ID %u", r_id);
Damien Miller5873dfd2002-02-13 14:04:37 +11001559 TAILQ_REMOVE(&acks, ack, tq);
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001560 debug3("In write loop, ack for %u %u bytes at %lld",
1561 ack->id, ack->len, (long long)ack->offset);
Damien Miller16a13332002-02-13 14:03:56 +11001562 ++ackid;
Darren Tuckerc9a19912013-06-02 08:37:05 +10001563 progress_counter += ack->len;
Darren Tuckera627d422013-06-02 07:31:17 +10001564 free(ack);
Damien Miller33804262001-02-04 23:20:18 +11001565 }
Damien Miller33804262001-02-04 23:20:18 +11001566 offset += len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001567 if (offset < 0)
1568 fatal("%s: offset < 0", __func__);
Damien Miller33804262001-02-04 23:20:18 +11001569 }
Damien Milleracdf25b2008-02-10 22:27:24 +11001570 buffer_free(&msg);
1571
Damien Miller62d57f62003-01-10 21:43:24 +11001572 if (showprogress)
1573 stop_progress_meter();
Darren Tuckera627d422013-06-02 07:31:17 +10001574 free(data);
Damien Miller33804262001-02-04 23:20:18 +11001575
Damien Milleracdf25b2008-02-10 22:27:24 +11001576 if (status != SSH2_FX_OK) {
1577 error("Couldn't write to remote file \"%s\": %s",
1578 remote_path, fx2txt(status));
1579 status = -1;
1580 }
1581
Damien Miller33804262001-02-04 23:20:18 +11001582 if (close(local_fd) == -1) {
1583 error("Couldn't close local file \"%s\": %s", local_path,
1584 strerror(errno));
Damien Millerd7686fd2001-02-10 00:40:03 +11001585 status = -1;
Damien Miller33804262001-02-04 23:20:18 +11001586 }
1587
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001588 /* Override umask and utimes if asked */
Damien Millerbda5c842013-10-15 12:05:58 +11001589 if (preserve_flag)
Damien Miller3db5f532002-02-13 14:10:32 +11001590 do_fsetstat(conn, handle, handle_len, &a);
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001591
Damien Millerf29238e2013-10-17 11:48:52 +11001592 if (fsync_flag)
1593 (void)do_fsync(conn, handle, handle_len);
1594
Damien Milleracdf25b2008-02-10 22:27:24 +11001595 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1596 status = -1;
Darren Tuckera627d422013-06-02 07:31:17 +10001597 free(handle);
Damien Milleracdf25b2008-02-10 22:27:24 +11001598
1599 return status;
Damien Miller33804262001-02-04 23:20:18 +11001600}
Darren Tucker1b0dd172009-10-07 08:37:48 +11001601
1602static int
Damien Millerbda5c842013-10-15 12:05:58 +11001603upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
Damien Millerf29238e2013-10-17 11:48:52 +11001604 int preserve_flag, int print_flag, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001605{
1606 int ret = 0, status;
1607 DIR *dirp;
1608 struct dirent *dp;
1609 char *filename, *new_src, *new_dst;
1610 struct stat sb;
1611 Attrib a;
1612
1613 if (depth >= MAX_DIR_DEPTH) {
1614 error("Maximum directory depth exceeded: %d levels", depth);
1615 return -1;
1616 }
1617
1618 if (stat(src, &sb) == -1) {
1619 error("Couldn't stat directory \"%s\": %s",
1620 src, strerror(errno));
1621 return -1;
1622 }
1623 if (!S_ISDIR(sb.st_mode)) {
1624 error("\"%s\" is not a directory", src);
1625 return -1;
1626 }
Damien Millerbda5c842013-10-15 12:05:58 +11001627 if (print_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001628 printf("Entering %s\n", src);
1629
1630 attrib_clear(&a);
1631 stat_to_attrib(&sb, &a);
1632 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1633 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1634 a.perm &= 01777;
Damien Millerbda5c842013-10-15 12:05:58 +11001635 if (!preserve_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001636 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
Damien Miller0d032412013-07-25 11:56:52 +10001637
Darren Tucker1b0dd172009-10-07 08:37:48 +11001638 status = do_mkdir(conn, dst, &a, 0);
1639 /*
1640 * we lack a portable status for errno EEXIST,
1641 * so if we get a SSH2_FX_FAILURE back we must check
1642 * if it was created successfully.
1643 */
1644 if (status != SSH2_FX_OK) {
1645 if (status != SSH2_FX_FAILURE)
1646 return -1;
Damien Miller0d032412013-07-25 11:56:52 +10001647 if (do_stat(conn, dst, 0) == NULL)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001648 return -1;
1649 }
1650
1651 if ((dirp = opendir(src)) == NULL) {
1652 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1653 return -1;
1654 }
Damien Miller0d032412013-07-25 11:56:52 +10001655
Darren Tucker1b0dd172009-10-07 08:37:48 +11001656 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1657 if (dp->d_ino == 0)
1658 continue;
1659 filename = dp->d_name;
1660 new_dst = path_append(dst, filename);
1661 new_src = path_append(src, filename);
1662
Darren Tucker438b4732009-10-11 21:52:10 +11001663 if (lstat(new_src, &sb) == -1) {
1664 logit("%s: lstat failed: %s", filename,
1665 strerror(errno));
1666 ret = -1;
1667 } else if (S_ISDIR(sb.st_mode)) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001668 if (strcmp(filename, ".") == 0 ||
1669 strcmp(filename, "..") == 0)
1670 continue;
1671
1672 if (upload_dir_internal(conn, new_src, new_dst,
Damien Millerf29238e2013-10-17 11:48:52 +11001673 depth + 1, preserve_flag, print_flag,
1674 fsync_flag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001675 ret = -1;
Darren Tucker438b4732009-10-11 21:52:10 +11001676 } else if (S_ISREG(sb.st_mode)) {
Damien Millerbda5c842013-10-15 12:05:58 +11001677 if (do_upload(conn, new_src, new_dst,
Damien Millerf29238e2013-10-17 11:48:52 +11001678 preserve_flag, fsync_flag) == -1) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001679 error("Uploading of file %s to %s failed!",
1680 new_src, new_dst);
1681 ret = -1;
1682 }
1683 } else
1684 logit("%s: not a regular file\n", filename);
Darren Tuckera627d422013-06-02 07:31:17 +10001685 free(new_dst);
1686 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001687 }
1688
1689 do_setstat(conn, dst, &a);
1690
1691 (void) closedir(dirp);
1692 return ret;
1693}
1694
1695int
Damien Millerbda5c842013-10-15 12:05:58 +11001696upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag,
Damien Millerf29238e2013-10-17 11:48:52 +11001697 int print_flag, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001698{
1699 char *dst_canon;
1700 int ret;
1701
1702 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
Damien Millerf29238e2013-10-17 11:48:52 +11001703 error("Unable to canonicalize path \"%s\"", dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001704 return -1;
1705 }
1706
Damien Miller2f93d052013-10-15 12:06:27 +11001707 ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
Damien Millerf29238e2013-10-17 11:48:52 +11001708 print_flag, fsync_flag);
1709
Darren Tuckera627d422013-06-02 07:31:17 +10001710 free(dst_canon);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001711 return ret;
1712}
1713
1714char *
1715path_append(char *p1, char *p2)
1716{
1717 char *ret;
1718 size_t len = strlen(p1) + strlen(p2) + 2;
1719
1720 ret = xmalloc(len);
1721 strlcpy(ret, p1, len);
1722 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1723 strlcat(ret, "/", len);
1724 strlcat(ret, p2, len);
1725
1726 return(ret);
1727}
1728