blob: d3f80e5a0c104ef5275bc7812595af7c70c024cd [file] [log] [blame]
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001/* $OpenBSD: sftp-client.c,v 1.131 2019/01/16 23:23:45 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>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100026#ifdef HAVE_SYS_STATVFS_H
Damien Millerd671e5a2008-05-19 14:53:33 +100027#include <sys/statvfs.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100028#endif
Damien Millerd7834352006-08-05 12:39:39 +100029#include "openbsd-compat/sys-queue.h"
Damien Millerf17883e2006-03-15 11:45:54 +110030#ifdef HAVE_SYS_STAT_H
31# include <sys/stat.h>
32#endif
Damien Miller9aec9192006-08-05 10:57:45 +100033#ifdef HAVE_SYS_TIME_H
34# include <sys/time.h>
35#endif
Damien Millerd7834352006-08-05 12:39:39 +100036#include <sys/uio.h>
Damien Miller57cf6382006-07-10 21:13:46 +100037
Darren Tucker1b0dd172009-10-07 08:37:48 +110038#include <dirent.h>
Darren Tucker39972492006-07-12 22:22:46 +100039#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100040#include <fcntl.h>
41#include <signal.h>
Damien Millerd7834352006-08-05 12:39:39 +100042#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100043#include <stdio.h>
Damien Miller0600c702013-11-21 13:55:43 +110044#include <stdlib.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"
djm@openbsd.org7d845f42015-01-14 13:54:13 +000049#include "ssherr.h"
50#include "sshbuf.h"
Damien Miller33804262001-02-04 23:20:18 +110051#include "log.h"
52#include "atomicio.h"
Damien Miller62d57f62003-01-10 21:43:24 +110053#include "progressmeter.h"
Damien Miller3f941882006-03-31 23:13:02 +110054#include "misc.h"
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +000055#include "utf8.h"
Damien Miller33804262001-02-04 23:20:18 +110056
57#include "sftp.h"
58#include "sftp-common.h"
59#include "sftp-client.h"
60
Darren Tuckercdf547a2004-05-24 10:12:19 +100061extern volatile sig_atomic_t interrupted;
Damien Miller62d57f62003-01-10 21:43:24 +110062extern int showprogress;
63
Damien Miller0c8d8f62006-03-15 11:34:25 +110064/* Minimum amount of data to read at a time */
Damien Miller16a13332002-02-13 14:03:56 +110065#define MIN_READ_SIZE 512
66
Darren Tucker1b0dd172009-10-07 08:37:48 +110067/* Maximum depth to descend in directory trees */
68#define MAX_DIR_DEPTH 64
69
Damien Miller89f04852017-03-20 11:53:34 +110070/* Directory separator characters */
71#ifdef HAVE_CYGWIN
72# define SFTP_DIRECTORY_CHARS "/\\"
73#else /* HAVE_CYGWIN */
74# define SFTP_DIRECTORY_CHARS "/"
75#endif /* HAVE_CYGWIN */
76
Damien Miller3db5f532002-02-13 14:10:32 +110077struct sftp_conn {
78 int fd_in;
79 int fd_out;
80 u_int transfer_buflen;
81 u_int num_requests;
82 u_int version;
83 u_int msg_id;
Damien Millerd671e5a2008-05-19 14:53:33 +100084#define SFTP_EXT_POSIX_RENAME 0x00000001
85#define SFTP_EXT_STATVFS 0x00000002
86#define SFTP_EXT_FSTATVFS 0x00000004
Darren Tuckeraf1f9092010-12-05 09:02:47 +110087#define SFTP_EXT_HARDLINK 0x00000008
Damien Millerf29238e2013-10-17 11:48:52 +110088#define SFTP_EXT_FSYNC 0x00000010
djm@openbsd.org60d8c842019-01-16 23:23:45 +000089#define SFTP_EXT_LSETSTAT 0x00000020
Damien Miller7a3e1d02008-03-27 10:59:57 +110090 u_int exts;
Damien Miller65e42f82010-09-24 22:15:11 +100091 u_int64_t limit_kbps;
92 struct bwlimit bwlimit_in, bwlimit_out;
Damien Miller3db5f532002-02-13 14:10:32 +110093};
Ben Lindstrom288cc392001-02-09 02:58:04 +000094
djm@openbsd.org7d845f42015-01-14 13:54:13 +000095static u_char *
96get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
Damien Miller65e42f82010-09-24 22:15:11 +100097 const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
98
99/* ARGSUSED */
100static int
101sftpio(void *_bwlimit, size_t amount)
102{
103 struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
104
105 bandwidth_limit(bwlimit, amount);
106 return 0;
107}
Darren Tuckerc22f0902009-10-07 08:24:19 +1100108
Ben Lindstrombba81212001-06-25 05:01:22 +0000109static void
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000110send_msg(struct sftp_conn *conn, struct sshbuf *m)
Damien Miller33804262001-02-04 23:20:18 +1100111{
Damien Millera7f3aaa2003-01-10 21:43:58 +1100112 u_char mlen[4];
Damien Miller58ca98b2006-04-23 12:06:35 +1000113 struct iovec iov[2];
Damien Miller33804262001-02-04 23:20:18 +1100114
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000115 if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH)
116 fatal("Outbound message too long %zu", sshbuf_len(m));
Damien Miller33804262001-02-04 23:20:18 +1100117
Damien Millera7f3aaa2003-01-10 21:43:58 +1100118 /* Send length first */
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000119 put_u32(mlen, sshbuf_len(m));
Damien Miller58ca98b2006-04-23 12:06:35 +1000120 iov[0].iov_base = mlen;
121 iov[0].iov_len = sizeof(mlen);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000122 iov[1].iov_base = (u_char *)sshbuf_ptr(m);
123 iov[1].iov_len = sshbuf_len(m);
Damien Millerd7834352006-08-05 12:39:39 +1000124
Damien Miller65e42f82010-09-24 22:15:11 +1000125 if (atomiciov6(writev, conn->fd_out, iov, 2,
Damien Miller0d032412013-07-25 11:56:52 +1000126 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000127 sshbuf_len(m) + sizeof(mlen))
Damien Millera7f3aaa2003-01-10 21:43:58 +1100128 fatal("Couldn't send packet: %s", strerror(errno));
129
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000130 sshbuf_reset(m);
Damien Miller33804262001-02-04 23:20:18 +1100131}
132
Ben Lindstrombba81212001-06-25 05:01:22 +0000133static void
dtucker@openbsd.org3dde09a2017-11-28 21:10:22 +0000134get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial)
Damien Miller33804262001-02-04 23:20:18 +1100135{
Damien Millera7f3aaa2003-01-10 21:43:58 +1100136 u_int msg_len;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000137 u_char *p;
138 int r;
Damien Miller33804262001-02-04 23:20:18 +1100139
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000140 if ((r = sshbuf_reserve(m, 4, &p)) != 0)
141 fatal("%s: buffer error: %s", __func__, ssh_err(r));
142 if (atomicio6(read, conn->fd_in, p, 4,
Damien Miller65e42f82010-09-24 22:15:11 +1000143 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
djm@openbsd.org4b3ecbb2017-08-11 04:41:08 +0000144 if (errno == EPIPE || errno == ECONNRESET)
Damien Millerb253cc42005-05-26 12:23:44 +1000145 fatal("Connection closed");
146 else
147 fatal("Couldn't read packet: %s", strerror(errno));
148 }
Damien Miller33804262001-02-04 23:20:18 +1100149
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000150 if ((r = sshbuf_get_u32(m, &msg_len)) != 0)
151 fatal("%s: buffer error: %s", __func__, ssh_err(r));
dtucker@openbsd.org3dde09a2017-11-28 21:10:22 +0000152 if (msg_len > SFTP_MAX_MSG_LENGTH) {
153 do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL,
154 "Received message too long %u", msg_len);
155 fatal("Ensure the remote shell produces no output "
156 "for non-interactive sessions.");
157 }
Damien Miller33804262001-02-04 23:20:18 +1100158
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000159 if ((r = sshbuf_reserve(m, msg_len, &p)) != 0)
160 fatal("%s: buffer error: %s", __func__, ssh_err(r));
161 if (atomicio6(read, conn->fd_in, p, msg_len,
Damien Miller65e42f82010-09-24 22:15:11 +1000162 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
163 != msg_len) {
Damien Millerb253cc42005-05-26 12:23:44 +1000164 if (errno == EPIPE)
165 fatal("Connection closed");
166 else
167 fatal("Read packet: %s", strerror(errno));
168 }
Damien Miller33804262001-02-04 23:20:18 +1100169}
170
Ben Lindstrombba81212001-06-25 05:01:22 +0000171static void
dtucker@openbsd.org3dde09a2017-11-28 21:10:22 +0000172get_msg(struct sftp_conn *conn, struct sshbuf *m)
173{
174 get_msg_extended(conn, m, 0);
175}
176
177static void
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000178send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s,
Damien Miller33804262001-02-04 23:20:18 +1100179 u_int len)
180{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000181 struct sshbuf *msg;
182 int r;
Damien Miller33804262001-02-04 23:20:18 +1100183
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000184 if ((msg = sshbuf_new()) == NULL)
185 fatal("%s: sshbuf_new failed", __func__);
186 if ((r = sshbuf_put_u8(msg, code)) != 0 ||
187 (r = sshbuf_put_u32(msg, id)) != 0 ||
188 (r = sshbuf_put_string(msg, s, len)) != 0)
189 fatal("%s: buffer error: %s", __func__, ssh_err(r));
190 send_msg(conn, msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000191 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000192 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100193}
194
Ben Lindstrombba81212001-06-25 05:01:22 +0000195static void
Damien Miller65e42f82010-09-24 22:15:11 +1000196send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000197 const void *s, u_int len, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100198{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000199 struct sshbuf *msg;
200 int r;
Damien Miller33804262001-02-04 23:20:18 +1100201
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000202 if ((msg = sshbuf_new()) == NULL)
203 fatal("%s: sshbuf_new failed", __func__);
204 if ((r = sshbuf_put_u8(msg, code)) != 0 ||
205 (r = sshbuf_put_u32(msg, id)) != 0 ||
206 (r = sshbuf_put_string(msg, s, len)) != 0 ||
207 (r = encode_attrib(msg, a)) != 0)
208 fatal("%s: buffer error: %s", __func__, ssh_err(r));
209 send_msg(conn, msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000210 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000211 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100212}
213
Ben Lindstrombba81212001-06-25 05:01:22 +0000214static u_int
Damien Miller65e42f82010-09-24 22:15:11 +1000215get_status(struct sftp_conn *conn, u_int expected_id)
Damien Miller33804262001-02-04 23:20:18 +1100216{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000217 struct sshbuf *msg;
218 u_char type;
219 u_int id, status;
220 int r;
Damien Miller33804262001-02-04 23:20:18 +1100221
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000222 if ((msg = sshbuf_new()) == NULL)
223 fatal("%s: sshbuf_new failed", __func__);
224 get_msg(conn, msg);
225 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
226 (r = sshbuf_get_u32(msg, &id)) != 0)
227 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100228
229 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000230 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100231 if (type != SSH2_FXP_STATUS)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000232 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100233 SSH2_FXP_STATUS, type);
234
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000235 if ((r = sshbuf_get_u32(msg, &status)) != 0)
236 fatal("%s: buffer error: %s", __func__, ssh_err(r));
237 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100238
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000239 debug3("SSH2_FXP_STATUS %u", status);
Damien Miller33804262001-02-04 23:20:18 +1100240
Damien Miller65e42f82010-09-24 22:15:11 +1000241 return status;
Damien Miller33804262001-02-04 23:20:18 +1100242}
243
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000244static u_char *
245get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
Damien Miller65e42f82010-09-24 22:15:11 +1000246 const char *errfmt, ...)
Damien Miller33804262001-02-04 23:20:18 +1100247{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000248 struct sshbuf *msg;
249 u_int id, status;
250 u_char type;
251 u_char *handle;
252 char errmsg[256];
Darren Tuckerc22f0902009-10-07 08:24:19 +1100253 va_list args;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000254 int r;
Darren Tuckerc22f0902009-10-07 08:24:19 +1100255
256 va_start(args, errfmt);
257 if (errfmt != NULL)
258 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
259 va_end(args);
Damien Miller33804262001-02-04 23:20:18 +1100260
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000261 if ((msg = sshbuf_new()) == NULL)
262 fatal("%s: sshbuf_new failed", __func__);
263 get_msg(conn, msg);
264 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
265 (r = sshbuf_get_u32(msg, &id)) != 0)
266 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100267
268 if (id != expected_id)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100269 fatal("%s: ID mismatch (%u != %u)",
270 errfmt == NULL ? __func__ : errmsg, id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100271 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000272 if ((r = sshbuf_get_u32(msg, &status)) != 0)
273 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Darren Tuckerc22f0902009-10-07 08:24:19 +1100274 if (errfmt != NULL)
275 error("%s: %s", errmsg, fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000276 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100277 return(NULL);
278 } else if (type != SSH2_FXP_HANDLE)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100279 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
280 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
Damien Miller33804262001-02-04 23:20:18 +1100281
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000282 if ((r = sshbuf_get_string(msg, &handle, len)) != 0)
283 fatal("%s: buffer error: %s", __func__, ssh_err(r));
284 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100285
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000286 return handle;
Damien Miller33804262001-02-04 23:20:18 +1100287}
288
Ben Lindstrombba81212001-06-25 05:01:22 +0000289static Attrib *
Damien Miller65e42f82010-09-24 22:15:11 +1000290get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100291{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000292 struct sshbuf *msg;
293 u_int id;
294 u_char type;
295 int r;
296 static Attrib a;
Damien Miller33804262001-02-04 23:20:18 +1100297
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000298 if ((msg = sshbuf_new()) == NULL)
299 fatal("%s: sshbuf_new failed", __func__);
300 get_msg(conn, msg);
Damien Miller33804262001-02-04 23:20:18 +1100301
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000302 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
303 (r = sshbuf_get_u32(msg, &id)) != 0)
304 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100305
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000306 debug3("Received stat reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100307 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000308 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100309 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000310 u_int status;
Damien Miller33804262001-02-04 23:20:18 +1100311
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000312 if ((r = sshbuf_get_u32(msg, &status)) != 0)
313 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000314 if (quiet)
315 debug("Couldn't stat remote file: %s", fx2txt(status));
316 else
317 error("Couldn't stat remote file: %s", fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000318 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100319 return(NULL);
320 } else if (type != SSH2_FXP_ATTRS) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000321 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100322 SSH2_FXP_ATTRS, type);
323 }
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000324 if ((r = decode_attrib(msg, &a)) != 0) {
325 error("%s: couldn't decode attrib: %s", __func__, ssh_err(r));
326 sshbuf_free(msg);
327 return NULL;
328 }
329 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100330
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000331 return &a;
Damien Miller33804262001-02-04 23:20:18 +1100332}
333
Damien Millerd671e5a2008-05-19 14:53:33 +1000334static int
Damien Miller65e42f82010-09-24 22:15:11 +1000335get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
336 u_int expected_id, int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +1000337{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000338 struct sshbuf *msg;
339 u_char type;
340 u_int id;
341 u_int64_t flag;
342 int r;
Damien Millerd671e5a2008-05-19 14:53:33 +1000343
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000344 if ((msg = sshbuf_new()) == NULL)
345 fatal("%s: sshbuf_new failed", __func__);
346 get_msg(conn, msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000347
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000348 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
349 (r = sshbuf_get_u32(msg, &id)) != 0)
350 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Millerd671e5a2008-05-19 14:53:33 +1000351
352 debug3("Received statvfs reply T:%u I:%u", type, id);
353 if (id != expected_id)
354 fatal("ID mismatch (%u != %u)", id, expected_id);
355 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000356 u_int status;
Damien Millerd671e5a2008-05-19 14:53:33 +1000357
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000358 if ((r = sshbuf_get_u32(msg, &status)) != 0)
359 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Millerd671e5a2008-05-19 14:53:33 +1000360 if (quiet)
361 debug("Couldn't statvfs: %s", fx2txt(status));
362 else
363 error("Couldn't statvfs: %s", fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000364 sshbuf_free(msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000365 return -1;
366 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
367 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
368 SSH2_FXP_EXTENDED_REPLY, type);
369 }
370
Damien Miller1d2c4562014-02-04 11:18:20 +1100371 memset(st, 0, sizeof(*st));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000372 if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 ||
373 (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 ||
374 (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 ||
375 (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 ||
376 (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 ||
377 (r = sshbuf_get_u64(msg, &st->f_files)) != 0 ||
378 (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 ||
379 (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 ||
380 (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 ||
381 (r = sshbuf_get_u64(msg, &flag)) != 0 ||
382 (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0)
383 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Millerd671e5a2008-05-19 14:53:33 +1000384
385 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
386 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
387
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000388 sshbuf_free(msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000389
390 return 0;
391}
392
Damien Miller3db5f532002-02-13 14:10:32 +1100393struct sftp_conn *
Damien Miller65e42f82010-09-24 22:15:11 +1000394do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
395 u_int64_t limit_kbps)
Damien Miller33804262001-02-04 23:20:18 +1100396{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000397 u_char type;
398 struct sshbuf *msg;
Damien Miller3db5f532002-02-13 14:10:32 +1100399 struct sftp_conn *ret;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000400 int r;
Damien Miller33804262001-02-04 23:20:18 +1100401
Damien Millerfec029f2013-08-21 02:42:12 +1000402 ret = xcalloc(1, sizeof(*ret));
403 ret->msg_id = 1;
Damien Miller65e42f82010-09-24 22:15:11 +1000404 ret->fd_in = fd_in;
405 ret->fd_out = fd_out;
406 ret->transfer_buflen = transfer_buflen;
407 ret->num_requests = num_requests;
408 ret->exts = 0;
409 ret->limit_kbps = 0;
410
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000411 if ((msg = sshbuf_new()) == NULL)
412 fatal("%s: sshbuf_new failed", __func__);
413 if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 ||
414 (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0)
415 fatal("%s: buffer error: %s", __func__, ssh_err(r));
416 send_msg(ret, msg);
Damien Miller33804262001-02-04 23:20:18 +1100417
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000418 sshbuf_reset(msg);
Damien Miller33804262001-02-04 23:20:18 +1100419
dtucker@openbsd.org3dde09a2017-11-28 21:10:22 +0000420 get_msg_extended(ret, msg, 1);
Damien Miller33804262001-02-04 23:20:18 +1100421
Kevin Stevesef4eea92001-02-05 12:42:17 +0000422 /* Expecting a VERSION reply */
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000423 if ((r = sshbuf_get_u8(msg, &type)) != 0)
424 fatal("%s: buffer error: %s", __func__, ssh_err(r));
425 if (type != SSH2_FXP_VERSION) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000426 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
Damien Miller33804262001-02-04 23:20:18 +1100427 type);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000428 sshbuf_free(msg);
jsg@openbsd.org64a89ec2015-05-23 14:28:37 +0000429 free(ret);
Damien Miller3db5f532002-02-13 14:10:32 +1100430 return(NULL);
Damien Miller33804262001-02-04 23:20:18 +1100431 }
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000432 if ((r = sshbuf_get_u32(msg, &ret->version)) != 0)
433 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100434
Damien Miller65e42f82010-09-24 22:15:11 +1000435 debug2("Remote version: %u", ret->version);
Damien Miller33804262001-02-04 23:20:18 +1100436
437 /* Check for extensions */
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000438 while (sshbuf_len(msg) > 0) {
439 char *name;
440 u_char *value;
441 size_t vlen;
Darren Tuckera64ab332008-06-13 07:01:29 +1000442 int known = 0;
Damien Miller33804262001-02-04 23:20:18 +1100443
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000444 if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 ||
445 (r = sshbuf_get_string(msg, &value, &vlen)) != 0)
446 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Millerd671e5a2008-05-19 14:53:33 +1000447 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000448 strcmp((char *)value, "1") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000449 ret->exts |= SFTP_EXT_POSIX_RENAME;
Darren Tuckera64ab332008-06-13 07:01:29 +1000450 known = 1;
451 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000452 strcmp((char *)value, "2") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000453 ret->exts |= SFTP_EXT_STATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000454 known = 1;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100455 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000456 strcmp((char *)value, "2") == 0) {
Damien Miller65e42f82010-09-24 22:15:11 +1000457 ret->exts |= SFTP_EXT_FSTATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000458 known = 1;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100459 } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000460 strcmp((char *)value, "1") == 0) {
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100461 ret->exts |= SFTP_EXT_HARDLINK;
462 known = 1;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000463 } else if (strcmp(name, "fsync@openssh.com") == 0 &&
464 strcmp((char *)value, "1") == 0) {
465 ret->exts |= SFTP_EXT_FSYNC;
466 known = 1;
djm@openbsd.org60d8c842019-01-16 23:23:45 +0000467 } else if (strcmp(name, "lsetstat@openssh.com") == 0 &&
468 strcmp((char *)value, "1") == 0) {
469 ret->exts |= SFTP_EXT_LSETSTAT;
470 known = 1;
Darren Tuckera64ab332008-06-13 07:01:29 +1000471 }
472 if (known) {
473 debug2("Server supports extension \"%s\" revision %s",
474 name, value);
475 } else {
476 debug2("Unrecognised server extension \"%s\"", name);
477 }
Darren Tuckera627d422013-06-02 07:31:17 +1000478 free(name);
479 free(value);
Damien Miller33804262001-02-04 23:20:18 +1100480 }
481
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000482 sshbuf_free(msg);
Damien Miller058316f2001-03-08 10:08:49 +1100483
Damien Miller3db5f532002-02-13 14:10:32 +1100484 /* Some filexfer v.0 servers don't support large packets */
Damien Miller65e42f82010-09-24 22:15:11 +1000485 if (ret->version == 0)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +0000486 ret->transfer_buflen = MINIMUM(ret->transfer_buflen, 20480);
Damien Miller3db5f532002-02-13 14:10:32 +1100487
Damien Miller65e42f82010-09-24 22:15:11 +1000488 ret->limit_kbps = limit_kbps;
489 if (ret->limit_kbps > 0) {
490 bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
491 ret->transfer_buflen);
492 bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
493 ret->transfer_buflen);
494 }
495
496 return ret;
Damien Miller3db5f532002-02-13 14:10:32 +1100497}
498
499u_int
500sftp_proto_version(struct sftp_conn *conn)
501{
Damien Miller65e42f82010-09-24 22:15:11 +1000502 return conn->version;
Damien Miller33804262001-02-04 23:20:18 +1100503}
504
505int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000506do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
Damien Miller33804262001-02-04 23:20:18 +1100507{
508 u_int id, status;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000509 struct sshbuf *msg;
510 int r;
Damien Miller33804262001-02-04 23:20:18 +1100511
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000512 if ((msg = sshbuf_new()) == NULL)
513 fatal("%s: sshbuf_new failed", __func__);
Damien Miller33804262001-02-04 23:20:18 +1100514
Damien Miller3db5f532002-02-13 14:10:32 +1100515 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000516 if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 ||
517 (r = sshbuf_put_u32(msg, id)) != 0 ||
518 (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
519 fatal("%s: buffer error: %s", __func__, ssh_err(r));
520 send_msg(conn, msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000521 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100522
Damien Miller65e42f82010-09-24 22:15:11 +1000523 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100524 if (status != SSH2_FX_OK)
525 error("Couldn't close file: %s", fx2txt(status));
526
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000527 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100528
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000529 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100530}
531
Damien Miller4870afd2001-03-14 10:27:09 +1100532
Ben Lindstrombba81212001-06-25 05:01:22 +0000533static int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000534do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
Damien Miller4870afd2001-03-14 10:27:09 +1100535 SFTP_DIRENT ***dir)
Damien Miller33804262001-02-04 23:20:18 +1100536{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000537 struct sshbuf *msg;
538 u_int count, id, i, expected_id, ents = 0;
539 size_t handle_len;
djm@openbsd.org1a31d022016-05-02 08:49:03 +0000540 u_char type, *handle;
Damien Miller39392072013-12-07 10:31:08 +1100541 int status = SSH2_FX_FAILURE;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000542 int r;
Damien Miller39392072013-12-07 10:31:08 +1100543
544 if (dir)
545 *dir = NULL;
Damien Miller33804262001-02-04 23:20:18 +1100546
Damien Miller3db5f532002-02-13 14:10:32 +1100547 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100548
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000549 if ((msg = sshbuf_new()) == NULL)
550 fatal("%s: sshbuf_new failed", __func__);
551 if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 ||
552 (r = sshbuf_put_u32(msg, id)) != 0 ||
553 (r = sshbuf_put_cstring(msg, path)) != 0)
554 fatal("%s: buffer error: %s", __func__, ssh_err(r));
555 send_msg(conn, msg);
Damien Miller33804262001-02-04 23:20:18 +1100556
Damien Miller65e42f82010-09-24 22:15:11 +1000557 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +1100558 "remote readdir(\"%s\")", path);
Damien Miller57c38ac2011-09-22 21:42:45 +1000559 if (handle == NULL) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000560 sshbuf_free(msg);
Damien Miller65e42f82010-09-24 22:15:11 +1000561 return -1;
Damien Miller57c38ac2011-09-22 21:42:45 +1000562 }
Damien Miller33804262001-02-04 23:20:18 +1100563
Damien Miller4870afd2001-03-14 10:27:09 +1100564 if (dir) {
565 ents = 0;
Damien Miller6c81fee2013-11-08 12:19:55 +1100566 *dir = xcalloc(1, sizeof(**dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100567 (*dir)[0] = NULL;
568 }
Damien Miller4870afd2001-03-14 10:27:09 +1100569
Darren Tuckercdf547a2004-05-24 10:12:19 +1000570 for (; !interrupted;) {
Damien Miller3db5f532002-02-13 14:10:32 +1100571 id = expected_id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100572
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000573 debug3("Sending SSH2_FXP_READDIR I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100574
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000575 sshbuf_reset(msg);
576 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 ||
577 (r = sshbuf_put_u32(msg, id)) != 0 ||
578 (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
579 fatal("%s: buffer error: %s", __func__, ssh_err(r));
580 send_msg(conn, msg);
Damien Miller33804262001-02-04 23:20:18 +1100581
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000582 sshbuf_reset(msg);
Damien Miller33804262001-02-04 23:20:18 +1100583
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000584 get_msg(conn, msg);
Damien Miller33804262001-02-04 23:20:18 +1100585
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000586 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
587 (r = sshbuf_get_u32(msg, &id)) != 0)
588 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100589
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000590 debug3("Received reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100591
592 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000593 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100594
595 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000596 u_int rstatus;
597
598 if ((r = sshbuf_get_u32(msg, &rstatus)) != 0)
599 fatal("%s: buffer error: %s",
600 __func__, ssh_err(r));
601 debug3("Received SSH2_FXP_STATUS %d", rstatus);
602 if (rstatus == SSH2_FX_EOF)
Damien Miller33804262001-02-04 23:20:18 +1100603 break;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000604 error("Couldn't read directory: %s", fx2txt(rstatus));
Damien Miller39392072013-12-07 10:31:08 +1100605 goto out;
Damien Miller33804262001-02-04 23:20:18 +1100606 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000607 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100608 SSH2_FXP_NAME, type);
609
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000610 if ((r = sshbuf_get_u32(msg, &count)) != 0)
611 fatal("%s: buffer error: %s", __func__, ssh_err(r));
djm@openbsd.orgc7995f22017-01-03 05:46:51 +0000612 if (count > SSHBUF_SIZE_MAX)
613 fatal("%s: nonsensical number of entries", __func__);
Damien Millerd7686fd2001-02-10 00:40:03 +1100614 if (count == 0)
615 break;
616 debug3("Received %d SSH2_FXP_NAME responses", count);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100617 for (i = 0; i < count; i++) {
Damien Miller33804262001-02-04 23:20:18 +1100618 char *filename, *longname;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000619 Attrib a;
Damien Miller33804262001-02-04 23:20:18 +1100620
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000621 if ((r = sshbuf_get_cstring(msg, &filename,
622 NULL)) != 0 ||
623 (r = sshbuf_get_cstring(msg, &longname,
624 NULL)) != 0)
625 fatal("%s: buffer error: %s",
626 __func__, ssh_err(r));
627 if ((r = decode_attrib(msg, &a)) != 0) {
628 error("%s: couldn't decode attrib: %s",
629 __func__, ssh_err(r));
630 free(filename);
631 free(longname);
632 sshbuf_free(msg);
633 return -1;
634 }
Damien Miller33804262001-02-04 23:20:18 +1100635
Damien Millerbda5c842013-10-15 12:05:58 +1100636 if (print_flag)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +0000637 mprintf("%s\n", longname);
Damien Miller4870afd2001-03-14 10:27:09 +1100638
Darren Tucker1b0dd172009-10-07 08:37:48 +1100639 /*
640 * Directory entries should never contain '/'
641 * These can be used to attack recursive ops
642 * (e.g. send '../../../../etc/passwd')
643 */
Damien Miller89f04852017-03-20 11:53:34 +1100644 if (strpbrk(filename, SFTP_DIRECTORY_CHARS) != NULL) {
Darren Tucker1b0dd172009-10-07 08:37:48 +1100645 error("Server sent suspect path \"%s\" "
646 "during readdir of \"%s\"", filename, path);
Damien Miller39392072013-12-07 10:31:08 +1100647 } else if (dir) {
deraadt@openbsd.org657a5fb2015-04-24 01:36:00 +0000648 *dir = xreallocarray(*dir, ents + 2, sizeof(**dir));
Damien Miller6c81fee2013-11-08 12:19:55 +1100649 (*dir)[ents] = xcalloc(1, sizeof(***dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100650 (*dir)[ents]->filename = xstrdup(filename);
651 (*dir)[ents]->longname = xstrdup(longname);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000652 memcpy(&(*dir)[ents]->a, &a, sizeof(a));
Damien Miller4870afd2001-03-14 10:27:09 +1100653 (*dir)[++ents] = NULL;
654 }
Darren Tuckera627d422013-06-02 07:31:17 +1000655 free(filename);
656 free(longname);
Damien Miller33804262001-02-04 23:20:18 +1100657 }
658 }
Damien Miller39392072013-12-07 10:31:08 +1100659 status = 0;
Damien Miller33804262001-02-04 23:20:18 +1100660
Damien Miller39392072013-12-07 10:31:08 +1100661 out:
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000662 sshbuf_free(msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100663 do_close(conn, handle, handle_len);
Darren Tuckera627d422013-06-02 07:31:17 +1000664 free(handle);
Damien Miller33804262001-02-04 23:20:18 +1100665
Damien Miller39392072013-12-07 10:31:08 +1100666 if (status != 0 && dir != NULL) {
667 /* Don't return results on error */
668 free_sftp_dirents(*dir);
669 *dir = NULL;
670 } else if (interrupted && dir != NULL && *dir != NULL) {
671 /* Don't return partial matches on interrupt */
Darren Tuckercdf547a2004-05-24 10:12:19 +1000672 free_sftp_dirents(*dir);
Damien Miller6c81fee2013-11-08 12:19:55 +1100673 *dir = xcalloc(1, sizeof(**dir));
Darren Tuckercdf547a2004-05-24 10:12:19 +1000674 **dir = NULL;
675 }
676
djm@openbsd.orgf6a59a22018-05-25 04:25:46 +0000677 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100678}
679
680int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000681do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir)
Damien Miller4870afd2001-03-14 10:27:09 +1100682{
Damien Miller3db5f532002-02-13 14:10:32 +1100683 return(do_lsreaddir(conn, path, 0, dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100684}
685
686void free_sftp_dirents(SFTP_DIRENT **s)
687{
688 int i;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100689
Damien Miller39392072013-12-07 10:31:08 +1100690 if (s == NULL)
691 return;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100692 for (i = 0; s[i]; i++) {
Darren Tuckera627d422013-06-02 07:31:17 +1000693 free(s[i]->filename);
694 free(s[i]->longname);
695 free(s[i]);
Damien Miller4870afd2001-03-14 10:27:09 +1100696 }
Darren Tuckera627d422013-06-02 07:31:17 +1000697 free(s);
Damien Miller4870afd2001-03-14 10:27:09 +1100698}
699
700int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000701do_rm(struct sftp_conn *conn, const char *path)
Damien Miller33804262001-02-04 23:20:18 +1100702{
703 u_int status, id;
704
705 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
706
Damien Miller3db5f532002-02-13 14:10:32 +1100707 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000708 send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
709 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100710 if (status != SSH2_FX_OK)
711 error("Couldn't delete file: %s", fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000712 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100713}
714
715int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000716do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
Damien Miller33804262001-02-04 23:20:18 +1100717{
718 u_int status, id;
719
Damien Miller3db5f532002-02-13 14:10:32 +1100720 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000721 send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
Damien Miller33804262001-02-04 23:20:18 +1100722 strlen(path), a);
723
Damien Miller65e42f82010-09-24 22:15:11 +1000724 status = get_status(conn, id);
Damien Millerbda5c842013-10-15 12:05:58 +1100725 if (status != SSH2_FX_OK && print_flag)
Damien Miller33804262001-02-04 23:20:18 +1100726 error("Couldn't create directory: %s", fx2txt(status));
727
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000728 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100729}
730
731int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000732do_rmdir(struct sftp_conn *conn, const char *path)
Damien Miller33804262001-02-04 23:20:18 +1100733{
734 u_int status, id;
735
Damien Miller3db5f532002-02-13 14:10:32 +1100736 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000737 send_string_request(conn, id, SSH2_FXP_RMDIR, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100738 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100739
Damien Miller65e42f82010-09-24 22:15:11 +1000740 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100741 if (status != SSH2_FX_OK)
742 error("Couldn't remove directory: %s", fx2txt(status));
743
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000744 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100745}
746
747Attrib *
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000748do_stat(struct sftp_conn *conn, const char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100749{
750 u_int id;
751
Damien Miller3db5f532002-02-13 14:10:32 +1100752 id = conn->msg_id++;
753
Damien Miller65e42f82010-09-24 22:15:11 +1000754 send_string_request(conn, id,
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000755 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
Damien Miller3db5f532002-02-13 14:10:32 +1100756 path, strlen(path));
757
Damien Miller65e42f82010-09-24 22:15:11 +1000758 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100759}
760
761Attrib *
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000762do_lstat(struct sftp_conn *conn, const char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100763{
764 u_int id;
765
Damien Miller3db5f532002-02-13 14:10:32 +1100766 if (conn->version == 0) {
767 if (quiet)
768 debug("Server version does not support lstat operation");
769 else
Damien Miller996acd22003-04-09 20:59:48 +1000770 logit("Server version does not support lstat operation");
Ben Lindstromf26ff5b2002-04-02 21:00:31 +0000771 return(do_stat(conn, path, quiet));
Damien Miller3db5f532002-02-13 14:10:32 +1100772 }
773
774 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000775 send_string_request(conn, id, SSH2_FXP_LSTAT, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100776 strlen(path));
777
Damien Miller65e42f82010-09-24 22:15:11 +1000778 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100779}
780
Damien Millercfe23d32008-02-10 22:20:44 +1100781#ifdef notyet
Damien Miller33804262001-02-04 23:20:18 +1100782Attrib *
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000783do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
784 int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100785{
786 u_int id;
787
Damien Miller3db5f532002-02-13 14:10:32 +1100788 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000789 send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
Damien Miller3db5f532002-02-13 14:10:32 +1100790 handle_len);
791
Damien Miller65e42f82010-09-24 22:15:11 +1000792 return(get_decode_stat(conn, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100793}
Damien Millercfe23d32008-02-10 22:20:44 +1100794#endif
Damien Miller33804262001-02-04 23:20:18 +1100795
796int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000797do_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100798{
799 u_int status, id;
800
Damien Miller3db5f532002-02-13 14:10:32 +1100801 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000802 send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
Damien Miller33804262001-02-04 23:20:18 +1100803 strlen(path), a);
804
Damien Miller65e42f82010-09-24 22:15:11 +1000805 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100806 if (status != SSH2_FX_OK)
807 error("Couldn't setstat on \"%s\": %s", path,
808 fx2txt(status));
809
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000810 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100811}
812
813int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000814do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
Damien Miller33804262001-02-04 23:20:18 +1100815 Attrib *a)
816{
817 u_int status, id;
818
Damien Miller3db5f532002-02-13 14:10:32 +1100819 id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000820 send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
Damien Miller33804262001-02-04 23:20:18 +1100821 handle_len, a);
822
Damien Miller65e42f82010-09-24 22:15:11 +1000823 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100824 if (status != SSH2_FX_OK)
825 error("Couldn't fsetstat: %s", fx2txt(status));
826
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000827 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100828}
829
830char *
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000831do_realpath(struct sftp_conn *conn, const char *path)
Damien Miller33804262001-02-04 23:20:18 +1100832{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000833 struct sshbuf *msg;
834 u_int expected_id, count, id;
Damien Miller33804262001-02-04 23:20:18 +1100835 char *filename, *longname;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000836 Attrib a;
837 u_char type;
838 int r;
Damien Miller33804262001-02-04 23:20:18 +1100839
Damien Miller3db5f532002-02-13 14:10:32 +1100840 expected_id = id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +1000841 send_string_request(conn, id, SSH2_FXP_REALPATH, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100842 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100843
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000844 if ((msg = sshbuf_new()) == NULL)
845 fatal("%s: sshbuf_new failed", __func__);
Damien Miller33804262001-02-04 23:20:18 +1100846
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000847 get_msg(conn, msg);
848 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
849 (r = sshbuf_get_u32(msg, &id)) != 0)
850 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100851
852 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000853 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100854
855 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000856 u_int status;
Damien Miller33804262001-02-04 23:20:18 +1100857
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000858 if ((r = sshbuf_get_u32(msg, &status)) != 0)
859 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Millerf29238e2013-10-17 11:48:52 +1100860 error("Couldn't canonicalize: %s", fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000861 sshbuf_free(msg);
Damien Miller49566312010-06-26 09:38:23 +1000862 return NULL;
Damien Miller33804262001-02-04 23:20:18 +1100863 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000864 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100865 SSH2_FXP_NAME, type);
866
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000867 if ((r = sshbuf_get_u32(msg, &count)) != 0)
868 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100869 if (count != 1)
870 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
871
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000872 if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
873 (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
874 (r = decode_attrib(msg, &a)) != 0)
875 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller33804262001-02-04 23:20:18 +1100876
Darren Tucker4908d442012-07-02 22:15:38 +1000877 debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000878 (unsigned long)a.size);
Damien Miller33804262001-02-04 23:20:18 +1100879
Darren Tuckera627d422013-06-02 07:31:17 +1000880 free(longname);
Damien Miller33804262001-02-04 23:20:18 +1100881
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000882 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100883
884 return(filename);
885}
886
887int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000888do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
Damien Millerc7dba122013-08-21 02:41:15 +1000889 int force_legacy)
Damien Miller33804262001-02-04 23:20:18 +1100890{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000891 struct sshbuf *msg;
Damien Miller33804262001-02-04 23:20:18 +1100892 u_int status, id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000893 int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
Damien Miller33804262001-02-04 23:20:18 +1100894
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000895 if ((msg = sshbuf_new()) == NULL)
896 fatal("%s: sshbuf_new failed", __func__);
Damien Miller33804262001-02-04 23:20:18 +1100897
898 /* Send rename request */
Damien Miller3db5f532002-02-13 14:10:32 +1100899 id = conn->msg_id++;
Damien Millerc7dba122013-08-21 02:41:15 +1000900 if (use_ext) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000901 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
902 (r = sshbuf_put_u32(msg, id)) != 0 ||
903 (r = sshbuf_put_cstring(msg,
904 "posix-rename@openssh.com")) != 0)
905 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7a3e1d02008-03-27 10:59:57 +1100906 } else {
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000907 if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 ||
908 (r = sshbuf_put_u32(msg, id)) != 0)
909 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller7a3e1d02008-03-27 10:59:57 +1100910 }
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000911 if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
912 (r = sshbuf_put_cstring(msg, newpath)) != 0)
913 fatal("%s: buffer error: %s", __func__, ssh_err(r));
914 send_msg(conn, msg);
Damien Miller7a3e1d02008-03-27 10:59:57 +1100915 debug3("Sent message %s \"%s\" -> \"%s\"",
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000916 use_ext ? "posix-rename@openssh.com" :
917 "SSH2_FXP_RENAME", oldpath, newpath);
918 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +1100919
Damien Miller65e42f82010-09-24 22:15:11 +1000920 status = get_status(conn, id);
Damien Miller33804262001-02-04 23:20:18 +1100921 if (status != SSH2_FX_OK)
Damien Miller3db5f532002-02-13 14:10:32 +1100922 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
923 newpath, fx2txt(status));
Damien Miller33804262001-02-04 23:20:18 +1100924
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000925 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +1100926}
927
928int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000929do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100930{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000931 struct sshbuf *msg;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100932 u_int status, id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000933 int r;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100934
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100935 if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
936 error("Server does not support hardlink@openssh.com extension");
937 return -1;
938 }
939
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000940 if ((msg = sshbuf_new()) == NULL)
941 fatal("%s: sshbuf_new failed", __func__);
Damien Miller3decdba2011-09-22 21:41:05 +1000942
943 /* Send link request */
944 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000945 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
946 (r = sshbuf_put_u32(msg, id)) != 0 ||
947 (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 ||
948 (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
949 (r = sshbuf_put_cstring(msg, newpath)) != 0)
950 fatal("%s: buffer error: %s", __func__, ssh_err(r));
951 send_msg(conn, msg);
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100952 debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
953 oldpath, newpath);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000954 sshbuf_free(msg);
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100955
956 status = get_status(conn, id);
957 if (status != SSH2_FX_OK)
958 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
959 newpath, fx2txt(status));
960
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000961 return status == SSH2_FX_OK ? 0 : -1;
Darren Tuckeraf1f9092010-12-05 09:02:47 +1100962}
963
964int
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000965do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
Damien Miller058316f2001-03-08 10:08:49 +1100966{
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000967 struct sshbuf *msg;
Damien Miller058316f2001-03-08 10:08:49 +1100968 u_int status, id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000969 int r;
Damien Miller058316f2001-03-08 10:08:49 +1100970
Damien Miller3db5f532002-02-13 14:10:32 +1100971 if (conn->version < 3) {
972 error("This server does not support the symlink operation");
973 return(SSH2_FX_OP_UNSUPPORTED);
974 }
975
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000976 if ((msg = sshbuf_new()) == NULL)
977 fatal("%s: sshbuf_new failed", __func__);
Damien Miller058316f2001-03-08 10:08:49 +1100978
Darren Tuckerdca6a4d2004-04-19 22:10:52 +1000979 /* Send symlink request */
Damien Miller3db5f532002-02-13 14:10:32 +1100980 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000981 if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 ||
982 (r = sshbuf_put_u32(msg, id)) != 0 ||
983 (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
984 (r = sshbuf_put_cstring(msg, newpath)) != 0)
985 fatal("%s: buffer error: %s", __func__, ssh_err(r));
986 send_msg(conn, msg);
Damien Miller058316f2001-03-08 10:08:49 +1100987 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
988 newpath);
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000989 sshbuf_free(msg);
Damien Miller058316f2001-03-08 10:08:49 +1100990
Damien Miller65e42f82010-09-24 22:15:11 +1000991 status = get_status(conn, id);
Damien Miller058316f2001-03-08 10:08:49 +1100992 if (status != SSH2_FX_OK)
Ben Lindstrom8e879cf2002-11-09 15:48:49 +0000993 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
Damien Miller3db5f532002-02-13 14:10:32 +1100994 newpath, fx2txt(status));
Damien Miller058316f2001-03-08 10:08:49 +1100995
djm@openbsd.org7d845f42015-01-14 13:54:13 +0000996 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller058316f2001-03-08 10:08:49 +1100997}
998
Damien Millerf29238e2013-10-17 11:48:52 +1100999int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001000do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
Damien Millerf29238e2013-10-17 11:48:52 +11001001{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001002 struct sshbuf *msg;
Damien Millerf29238e2013-10-17 11:48:52 +11001003 u_int status, id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001004 int r;
Damien Millerf29238e2013-10-17 11:48:52 +11001005
1006 /* Silently return if the extension is not supported */
1007 if ((conn->exts & SFTP_EXT_FSYNC) == 0)
1008 return -1;
1009
Damien Millerf29238e2013-10-17 11:48:52 +11001010 /* Send fsync request */
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001011 if ((msg = sshbuf_new()) == NULL)
1012 fatal("%s: sshbuf_new failed", __func__);
Damien Millerf29238e2013-10-17 11:48:52 +11001013 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001014 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1015 (r = sshbuf_put_u32(msg, id)) != 0 ||
1016 (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
1017 (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1018 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1019 send_msg(conn, msg);
Damien Millerf29238e2013-10-17 11:48:52 +11001020 debug3("Sent message fsync@openssh.com I:%u", id);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001021 sshbuf_free(msg);
Damien Millerf29238e2013-10-17 11:48:52 +11001022
1023 status = get_status(conn, id);
1024 if (status != SSH2_FX_OK)
1025 error("Couldn't sync file: %s", fx2txt(status));
1026
djm@openbsd.orgf6a59a22018-05-25 04:25:46 +00001027 return status == SSH2_FX_OK ? 0 : -1;
Damien Millerf29238e2013-10-17 11:48:52 +11001028}
1029
Damien Millercfe23d32008-02-10 22:20:44 +11001030#ifdef notyet
Damien Miller058316f2001-03-08 10:08:49 +11001031char *
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001032do_readlink(struct sftp_conn *conn, const char *path)
Damien Miller058316f2001-03-08 10:08:49 +11001033{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001034 struct sshbuf *msg;
1035 u_int expected_id, count, id;
Damien Miller058316f2001-03-08 10:08:49 +11001036 char *filename, *longname;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001037 Attrib a;
1038 u_char type;
1039 int r;
Damien Miller058316f2001-03-08 10:08:49 +11001040
Damien Miller3db5f532002-02-13 14:10:32 +11001041 expected_id = id = conn->msg_id++;
Damien Miller65e42f82010-09-24 22:15:11 +10001042 send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
Damien Miller058316f2001-03-08 10:08:49 +11001043
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001044 if ((msg = sshbuf_new()) == NULL)
1045 fatal("%s: sshbuf_new failed", __func__);
Damien Miller058316f2001-03-08 10:08:49 +11001046
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001047 get_msg(conn, msg);
1048 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1049 (r = sshbuf_get_u32(msg, &id)) != 0)
1050 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller058316f2001-03-08 10:08:49 +11001051
1052 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001053 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller058316f2001-03-08 10:08:49 +11001054
1055 if (type == SSH2_FXP_STATUS) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001056 u_int status;
Damien Miller058316f2001-03-08 10:08:49 +11001057
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001058 if ((r = sshbuf_get_u32(msg, &status)) != 0)
1059 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller058316f2001-03-08 10:08:49 +11001060 error("Couldn't readlink: %s", fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001061 sshbuf_free(msg);
Damien Miller058316f2001-03-08 10:08:49 +11001062 return(NULL);
1063 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001064 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller058316f2001-03-08 10:08:49 +11001065 SSH2_FXP_NAME, type);
1066
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001067 if ((r = sshbuf_get_u32(msg, &count)) != 0)
1068 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller058316f2001-03-08 10:08:49 +11001069 if (count != 1)
1070 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
1071
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001072 if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
1073 (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
1074 (r = decode_attrib(msg, &a)) != 0)
1075 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Damien Miller058316f2001-03-08 10:08:49 +11001076
1077 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
1078
Darren Tuckera627d422013-06-02 07:31:17 +10001079 free(longname);
Damien Miller058316f2001-03-08 10:08:49 +11001080
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001081 sshbuf_free(msg);
Damien Miller058316f2001-03-08 10:08:49 +11001082
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001083 return filename;
Damien Miller058316f2001-03-08 10:08:49 +11001084}
Damien Millercfe23d32008-02-10 22:20:44 +11001085#endif
Damien Miller058316f2001-03-08 10:08:49 +11001086
Damien Millerd671e5a2008-05-19 14:53:33 +10001087int
Darren Tucker7b598892008-06-09 22:49:36 +10001088do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
Damien Millerd671e5a2008-05-19 14:53:33 +10001089 int quiet)
1090{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001091 struct sshbuf *msg;
Damien Millerd671e5a2008-05-19 14:53:33 +10001092 u_int id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001093 int r;
Damien Millerd671e5a2008-05-19 14:53:33 +10001094
1095 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
1096 error("Server does not support statvfs@openssh.com extension");
1097 return -1;
1098 }
1099
1100 id = conn->msg_id++;
1101
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001102 if ((msg = sshbuf_new()) == NULL)
1103 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001104 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1105 (r = sshbuf_put_u32(msg, id)) != 0 ||
1106 (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
1107 (r = sshbuf_put_cstring(msg, path)) != 0)
1108 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1109 send_msg(conn, msg);
1110 sshbuf_free(msg);
Damien Millerd671e5a2008-05-19 14:53:33 +10001111
Damien Miller65e42f82010-09-24 22:15:11 +10001112 return get_decode_statvfs(conn, st, id, quiet);
Damien Millerd671e5a2008-05-19 14:53:33 +10001113}
1114
1115#ifdef notyet
1116int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001117do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
Darren Tucker7b598892008-06-09 22:49:36 +10001118 struct sftp_statvfs *st, int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +10001119{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001120 struct sshbuf *msg;
Damien Millerd671e5a2008-05-19 14:53:33 +10001121 u_int id;
1122
1123 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
1124 error("Server does not support fstatvfs@openssh.com extension");
1125 return -1;
1126 }
1127
1128 id = conn->msg_id++;
1129
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001130 if ((msg = sshbuf_new()) == NULL)
1131 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001132 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1133 (r = sshbuf_put_u32(msg, id)) != 0 ||
1134 (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
1135 (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1136 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1137 send_msg(conn, msg);
1138 sshbuf_free(msg);
Damien Millerd671e5a2008-05-19 14:53:33 +10001139
Damien Miller65e42f82010-09-24 22:15:11 +10001140 return get_decode_statvfs(conn, st, id, quiet);
Damien Millerd671e5a2008-05-19 14:53:33 +10001141}
1142#endif
1143
djm@openbsd.org60d8c842019-01-16 23:23:45 +00001144int
1145do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a)
1146{
1147 struct sshbuf *msg;
1148 u_int status, id;
1149 int r;
1150
1151 if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) {
1152 error("Server does not support lsetstat@openssh.com extension");
1153 return -1;
1154 }
1155
1156 id = conn->msg_id++;
1157 if ((msg = sshbuf_new()) == NULL)
1158 fatal("%s: sshbuf_new failed", __func__);
1159 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1160 (r = sshbuf_put_u32(msg, id)) != 0 ||
1161 (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 ||
1162 (r = sshbuf_put_cstring(msg, path)) != 0 ||
1163 (r = encode_attrib(msg, a)) != 0)
1164 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1165 send_msg(conn, msg);
1166 sshbuf_free(msg);
1167
1168 status = get_status(conn, id);
1169 if (status != SSH2_FX_OK)
1170 error("Couldn't setstat on \"%s\": %s", path,
1171 fx2txt(status));
1172
1173 return status == SSH2_FX_OK ? 0 : -1;
1174}
1175
Damien Miller16a13332002-02-13 14:03:56 +11001176static void
Damien Miller65e42f82010-09-24 22:15:11 +10001177send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001178 u_int len, const u_char *handle, u_int handle_len)
Damien Miller16a13332002-02-13 14:03:56 +11001179{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001180 struct sshbuf *msg;
1181 int r;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001182
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001183 if ((msg = sshbuf_new()) == NULL)
1184 fatal("%s: sshbuf_new failed", __func__);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001185 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 ||
1186 (r = sshbuf_put_u32(msg, id)) != 0 ||
1187 (r = sshbuf_put_string(msg, handle, handle_len)) != 0 ||
1188 (r = sshbuf_put_u64(msg, offset)) != 0 ||
1189 (r = sshbuf_put_u32(msg, len)) != 0)
1190 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1191 send_msg(conn, msg);
1192 sshbuf_free(msg);
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001193}
Damien Miller16a13332002-02-13 14:03:56 +11001194
Damien Miller058316f2001-03-08 10:08:49 +11001195int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001196do_download(struct sftp_conn *conn, const char *remote_path,
1197 const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
1198 int fsync_flag)
Damien Miller33804262001-02-04 23:20:18 +11001199{
Darren Tucker1b0dd172009-10-07 08:37:48 +11001200 Attrib junk;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001201 struct sshbuf *msg;
1202 u_char *handle;
1203 int local_fd = -1, write_error;
1204 int read_error, write_errno, reordered = 0, r;
Damien Miller0d032412013-07-25 11:56:52 +10001205 u_int64_t offset = 0, size, highwater;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001206 u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK;
Damien Miller62d57f62003-01-10 21:43:24 +11001207 off_t progress_counter;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001208 size_t handle_len;
Damien Miller0d032412013-07-25 11:56:52 +10001209 struct stat st;
Damien Miller16a13332002-02-13 14:03:56 +11001210 struct request {
1211 u_int id;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001212 size_t len;
Damien Miller16a13332002-02-13 14:03:56 +11001213 u_int64_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001214 TAILQ_ENTRY(request) tq;
Damien Miller16a13332002-02-13 14:03:56 +11001215 };
1216 TAILQ_HEAD(reqhead, request) requests;
1217 struct request *req;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001218 u_char type;
Damien Miller16a13332002-02-13 14:03:56 +11001219
1220 TAILQ_INIT(&requests);
Damien Miller33804262001-02-04 23:20:18 +11001221
Darren Tucker1b0dd172009-10-07 08:37:48 +11001222 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1223 return -1;
Damien Miller33804262001-02-04 23:20:18 +11001224
Damien Miller9e720282008-06-29 22:46:35 +10001225 /* Do not preserve set[ug]id here, as we do not preserve ownership */
Damien Miller33804262001-02-04 23:20:18 +11001226 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
Damien Miller770b3742003-01-08 14:04:53 +11001227 mode = a->perm & 0777;
Damien Miller33804262001-02-04 23:20:18 +11001228 else
1229 mode = 0666;
1230
Ben Lindstromc8d1c302001-03-17 00:34:46 +00001231 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
Damien Miller5fa01fd2003-01-14 22:24:47 +11001232 (!S_ISREG(a->perm))) {
1233 error("Cannot download non-regular file: %s", remote_path);
Ben Lindstromc8d1c302001-03-17 00:34:46 +00001234 return(-1);
1235 }
1236
Damien Miller16a13332002-02-13 14:03:56 +11001237 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1238 size = a->size;
1239 else
1240 size = 0;
1241
Damien Miller3db5f532002-02-13 14:10:32 +11001242 buflen = conn->transfer_buflen;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001243 if ((msg = sshbuf_new()) == NULL)
1244 fatal("%s: sshbuf_new failed", __func__);
1245
1246 attrib_clear(&junk); /* Send empty attributes */
Damien Miller33804262001-02-04 23:20:18 +11001247
1248 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001249 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001250 if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
1251 (r = sshbuf_put_u32(msg, id)) != 0 ||
1252 (r = sshbuf_put_cstring(msg, remote_path)) != 0 ||
1253 (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 ||
1254 (r = encode_attrib(msg, &junk)) != 0)
1255 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1256 send_msg(conn, msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001257 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001258
Damien Miller65e42f82010-09-24 22:15:11 +10001259 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +11001260 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001261 if (handle == NULL) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001262 sshbuf_free(msg);
Damien Miller33804262001-02-04 23:20:18 +11001263 return(-1);
1264 }
1265
Damien Millerbda5c842013-10-15 12:05:58 +11001266 local_fd = open(local_path,
1267 O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
Damien Miller3db5f532002-02-13 14:10:32 +11001268 if (local_fd == -1) {
1269 error("Couldn't open local file \"%s\" for writing: %s",
1270 local_path, strerror(errno));
Damien Miller0d032412013-07-25 11:56:52 +10001271 goto fail;
1272 }
1273 offset = highwater = 0;
Damien Millerbda5c842013-10-15 12:05:58 +11001274 if (resume_flag) {
Damien Miller0d032412013-07-25 11:56:52 +10001275 if (fstat(local_fd, &st) == -1) {
1276 error("Unable to stat local file \"%s\": %s",
1277 local_path, strerror(errno));
1278 goto fail;
1279 }
Damien Millerc3d483f2014-01-17 11:20:26 +11001280 if (st.st_size < 0) {
1281 error("\"%s\" has negative size", local_path);
1282 goto fail;
1283 }
1284 if ((u_int64_t)st.st_size > size) {
Damien Miller0d032412013-07-25 11:56:52 +10001285 error("Unable to resume download of \"%s\": "
1286 "local file is larger than remote", local_path);
1287 fail:
1288 do_close(conn, handle, handle_len);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001289 sshbuf_free(msg);
Damien Miller0d032412013-07-25 11:56:52 +10001290 free(handle);
Damien Miller9275df32013-12-05 10:26:32 +11001291 if (local_fd != -1)
1292 close(local_fd);
Damien Miller0d032412013-07-25 11:56:52 +10001293 return -1;
1294 }
1295 offset = highwater = st.st_size;
Damien Miller3db5f532002-02-13 14:10:32 +11001296 }
1297
Damien Miller33804262001-02-04 23:20:18 +11001298 /* Read from remote and write to local */
Damien Miller0d032412013-07-25 11:56:52 +10001299 write_error = read_error = write_errno = num_req = 0;
Damien Miller16a13332002-02-13 14:03:56 +11001300 max_req = 1;
Damien Miller0d032412013-07-25 11:56:52 +10001301 progress_counter = offset;
Damien Miller62d57f62003-01-10 21:43:24 +11001302
Damien Miller9ba30692004-03-08 23:12:02 +11001303 if (showprogress && size != 0)
1304 start_progress_meter(remote_path, size, &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +11001305
Damien Miller16a13332002-02-13 14:03:56 +11001306 while (num_req > 0 || max_req > 0) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001307 u_char *data;
1308 size_t len;
Damien Miller33804262001-02-04 23:20:18 +11001309
Darren Tuckercdf547a2004-05-24 10:12:19 +10001310 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001311 * Simulate EOF on interrupt: stop sending new requests and
Darren Tuckercdf547a2004-05-24 10:12:19 +10001312 * allow outstanding requests to drain gracefully
1313 */
1314 if (interrupted) {
1315 if (num_req == 0) /* If we haven't started yet... */
1316 break;
1317 max_req = 0;
1318 }
1319
Damien Miller16a13332002-02-13 14:03:56 +11001320 /* Send some more requests */
1321 while (num_req < max_req) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001322 debug3("Request range %llu -> %llu (%d/%d)",
Ben Lindstromd45f28c2002-03-22 01:00:57 +00001323 (unsigned long long)offset,
1324 (unsigned long long)offset + buflen - 1,
1325 num_req, max_req);
Damien Miller6c81fee2013-11-08 12:19:55 +11001326 req = xcalloc(1, sizeof(*req));
Damien Miller3db5f532002-02-13 14:10:32 +11001327 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001328 req->len = buflen;
1329 req->offset = offset;
1330 offset += buflen;
1331 num_req++;
1332 TAILQ_INSERT_TAIL(&requests, req, tq);
Damien Miller65e42f82010-09-24 22:15:11 +10001333 send_read_request(conn, req->id, req->offset,
Damien Miller16a13332002-02-13 14:03:56 +11001334 req->len, handle, handle_len);
1335 }
Damien Miller33804262001-02-04 23:20:18 +11001336
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001337 sshbuf_reset(msg);
1338 get_msg(conn, msg);
1339 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1340 (r = sshbuf_get_u32(msg, &id)) != 0)
1341 fatal("%s: buffer error: %s", __func__, ssh_err(r));
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001342 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
Damien Miller33804262001-02-04 23:20:18 +11001343
Damien Miller16a13332002-02-13 14:03:56 +11001344 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001345 for (req = TAILQ_FIRST(&requests);
Damien Miller16a13332002-02-13 14:03:56 +11001346 req != NULL && req->id != id;
1347 req = TAILQ_NEXT(req, tq))
1348 ;
1349 if (req == NULL)
1350 fatal("Unexpected reply %u", id);
1351
1352 switch (type) {
1353 case SSH2_FXP_STATUS:
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001354 if ((r = sshbuf_get_u32(msg, &status)) != 0)
1355 fatal("%s: buffer error: %s",
1356 __func__, ssh_err(r));
Damien Miller16a13332002-02-13 14:03:56 +11001357 if (status != SSH2_FX_EOF)
1358 read_error = 1;
1359 max_req = 0;
1360 TAILQ_REMOVE(&requests, req, tq);
Darren Tuckera627d422013-06-02 07:31:17 +10001361 free(req);
Damien Miller16a13332002-02-13 14:03:56 +11001362 num_req--;
1363 break;
1364 case SSH2_FXP_DATA:
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001365 if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
1366 fatal("%s: buffer error: %s",
1367 __func__, ssh_err(r));
Ben Lindstromeb505452002-03-22 01:03:15 +00001368 debug3("Received data %llu -> %llu",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001369 (unsigned long long)req->offset,
Ben Lindstromeb505452002-03-22 01:03:15 +00001370 (unsigned long long)req->offset + len - 1);
Damien Miller16a13332002-02-13 14:03:56 +11001371 if (len > req->len)
1372 fatal("Received more data than asked for "
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001373 "%zu > %zu", len, req->len);
Damien Miller16a13332002-02-13 14:03:56 +11001374 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
Darren Tucker9f63f222003-07-03 13:46:56 +10001375 atomicio(vwrite, local_fd, data, len) != len) &&
Damien Miller16a13332002-02-13 14:03:56 +11001376 !write_error) {
1377 write_errno = errno;
1378 write_error = 1;
1379 max_req = 0;
Damien Miller33804262001-02-04 23:20:18 +11001380 }
Damien Miller0d032412013-07-25 11:56:52 +10001381 else if (!reordered && req->offset <= highwater)
1382 highwater = req->offset + len;
1383 else if (!reordered && req->offset > highwater)
1384 reordered = 1;
Damien Miller62d57f62003-01-10 21:43:24 +11001385 progress_counter += len;
Darren Tuckera627d422013-06-02 07:31:17 +10001386 free(data);
Damien Miller16a13332002-02-13 14:03:56 +11001387
1388 if (len == req->len) {
1389 TAILQ_REMOVE(&requests, req, tq);
Darren Tuckera627d422013-06-02 07:31:17 +10001390 free(req);
Damien Miller16a13332002-02-13 14:03:56 +11001391 num_req--;
1392 } else {
1393 /* Resend the request for the missing data */
1394 debug3("Short data block, re-requesting "
Ben Lindstromeb505452002-03-22 01:03:15 +00001395 "%llu -> %llu (%2d)",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001396 (unsigned long long)req->offset + len,
Ben Lindstrom83b79e42002-03-22 01:05:27 +00001397 (unsigned long long)req->offset +
1398 req->len - 1, num_req);
Damien Miller3db5f532002-02-13 14:10:32 +11001399 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001400 req->len -= len;
1401 req->offset += len;
Damien Miller65e42f82010-09-24 22:15:11 +10001402 send_read_request(conn, req->id,
Damien Miller3db5f532002-02-13 14:10:32 +11001403 req->offset, req->len, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001404 /* Reduce the request size */
1405 if (len < buflen)
deraadt@openbsd.org9136ec12016-09-12 01:22:38 +00001406 buflen = MAXIMUM(MIN_READ_SIZE, len);
Damien Miller16a13332002-02-13 14:03:56 +11001407 }
1408 if (max_req > 0) { /* max_req = 0 iff EOF received */
1409 if (size > 0 && offset > size) {
1410 /* Only one request at a time
1411 * after the expected EOF */
1412 debug3("Finish at %llu (%2d)",
Ben Lindstromeb505452002-03-22 01:03:15 +00001413 (unsigned long long)offset,
1414 num_req);
Damien Miller16a13332002-02-13 14:03:56 +11001415 max_req = 1;
Darren Tuckercdf547a2004-05-24 10:12:19 +10001416 } else if (max_req <= conn->num_requests) {
Damien Miller16a13332002-02-13 14:03:56 +11001417 ++max_req;
1418 }
1419 }
1420 break;
1421 default:
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001422 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +11001423 SSH2_FXP_DATA, type);
1424 }
Damien Miller33804262001-02-04 23:20:18 +11001425 }
Damien Miller33804262001-02-04 23:20:18 +11001426
Damien Miller62d57f62003-01-10 21:43:24 +11001427 if (showprogress && size)
1428 stop_progress_meter();
1429
Damien Miller16a13332002-02-13 14:03:56 +11001430 /* Sanity check */
1431 if (TAILQ_FIRST(&requests) != NULL)
1432 fatal("Transfer complete, but requests still in queue");
Damien Miller0d032412013-07-25 11:56:52 +10001433 /* Truncate at highest contiguous point to avoid holes on interrupt */
1434 if (read_error || write_error || interrupted) {
Damien Millerbda5c842013-10-15 12:05:58 +11001435 if (reordered && resume_flag) {
Damien Miller0d032412013-07-25 11:56:52 +10001436 error("Unable to resume download of \"%s\": "
1437 "server reordered requests", local_path);
1438 }
1439 debug("truncating at %llu", (unsigned long long)highwater);
djm@openbsd.org9cc68422015-05-28 04:50:53 +00001440 if (ftruncate(local_fd, highwater) == -1)
1441 error("ftruncate \"%s\": %s", local_path,
1442 strerror(errno));
Damien Miller0d032412013-07-25 11:56:52 +10001443 }
Damien Miller16a13332002-02-13 14:03:56 +11001444 if (read_error) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001445 error("Couldn't read from remote file \"%s\" : %s",
Damien Miller3db5f532002-02-13 14:10:32 +11001446 remote_path, fx2txt(status));
Damien Millerfec029f2013-08-21 02:42:12 +10001447 status = -1;
Damien Miller3db5f532002-02-13 14:10:32 +11001448 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001449 } else if (write_error) {
Damien Miller3db5f532002-02-13 14:10:32 +11001450 error("Couldn't write to \"%s\": %s", local_path,
1451 strerror(write_errno));
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001452 status = SSH2_FX_FAILURE;
Damien Miller3db5f532002-02-13 14:10:32 +11001453 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001454 } else {
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001455 if (do_close(conn, handle, handle_len) != 0 || interrupted)
1456 status = SSH2_FX_FAILURE;
1457 else
1458 status = SSH2_FX_OK;
Damien Miller16a13332002-02-13 14:03:56 +11001459 /* Override umask and utimes if asked */
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001460#ifdef HAVE_FCHMOD
Damien Millerbda5c842013-10-15 12:05:58 +11001461 if (preserve_flag && fchmod(local_fd, mode) == -1)
Damien Millera8e06ce2003-11-21 23:48:55 +11001462#else
Damien Millerbda5c842013-10-15 12:05:58 +11001463 if (preserve_flag && chmod(local_path, mode) == -1)
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001464#endif /* HAVE_FCHMOD */
Damien Miller16a13332002-02-13 14:03:56 +11001465 error("Couldn't set mode on \"%s\": %s", local_path,
Ben Lindstrom93576d92002-12-23 02:06:19 +00001466 strerror(errno));
Damien Millerbda5c842013-10-15 12:05:58 +11001467 if (preserve_flag &&
1468 (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
Damien Miller16a13332002-02-13 14:03:56 +11001469 struct timeval tv[2];
1470 tv[0].tv_sec = a->atime;
1471 tv[1].tv_sec = a->mtime;
1472 tv[0].tv_usec = tv[1].tv_usec = 0;
1473 if (utimes(local_path, tv) == -1)
1474 error("Can't set times on \"%s\": %s",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001475 local_path, strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001476 }
Damien Millerf29238e2013-10-17 11:48:52 +11001477 if (fsync_flag) {
1478 debug("syncing \"%s\"", local_path);
1479 if (fsync(local_fd) == -1)
1480 error("Couldn't sync file \"%s\": %s",
1481 local_path, strerror(errno));
1482 }
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001483 }
Damien Millerd7686fd2001-02-10 00:40:03 +11001484 close(local_fd);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001485 sshbuf_free(msg);
Darren Tuckera627d422013-06-02 07:31:17 +10001486 free(handle);
Damien Miller3db5f532002-02-13 14:10:32 +11001487
djm@openbsd.orgf6a59a22018-05-25 04:25:46 +00001488 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +11001489}
1490
Darren Tucker1b0dd172009-10-07 08:37:48 +11001491static int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001492download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1493 int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
1494 int resume_flag, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001495{
1496 int i, ret = 0;
1497 SFTP_DIRENT **dir_entries;
djm@openbsd.org1a660792018-07-31 03:07:24 +00001498 char *filename, *new_src = NULL, *new_dst = NULL;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001499 mode_t mode = 0777;
1500
1501 if (depth >= MAX_DIR_DEPTH) {
1502 error("Maximum directory depth exceeded: %d levels", depth);
1503 return -1;
1504 }
1505
1506 if (dirattrib == NULL &&
1507 (dirattrib = do_stat(conn, src, 1)) == NULL) {
1508 error("Unable to stat remote directory \"%s\"", src);
1509 return -1;
1510 }
1511 if (!S_ISDIR(dirattrib->perm)) {
1512 error("\"%s\" is not a directory", src);
1513 return -1;
1514 }
Damien Millerbda5c842013-10-15 12:05:58 +11001515 if (print_flag)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001516 mprintf("Retrieving %s\n", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001517
1518 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1519 mode = dirattrib->perm & 01777;
1520 else {
1521 debug("Server did not send permissions for "
1522 "directory \"%s\"", dst);
1523 }
1524
1525 if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1526 error("mkdir %s: %s", dst, strerror(errno));
1527 return -1;
1528 }
1529
1530 if (do_readdir(conn, src, &dir_entries) == -1) {
1531 error("%s: Failed to get directory contents", src);
1532 return -1;
1533 }
1534
1535 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
djm@openbsd.org1a660792018-07-31 03:07:24 +00001536 free(new_dst);
1537 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001538
djm@openbsd.org1a660792018-07-31 03:07:24 +00001539 filename = dir_entries[i]->filename;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001540 new_dst = path_append(dst, filename);
1541 new_src = path_append(src, filename);
1542
1543 if (S_ISDIR(dir_entries[i]->a.perm)) {
1544 if (strcmp(filename, ".") == 0 ||
1545 strcmp(filename, "..") == 0)
1546 continue;
1547 if (download_dir_internal(conn, new_src, new_dst,
Damien Millerbda5c842013-10-15 12:05:58 +11001548 depth + 1, &(dir_entries[i]->a), preserve_flag,
Damien Millerf29238e2013-10-17 11:48:52 +11001549 print_flag, resume_flag, fsync_flag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001550 ret = -1;
1551 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1552 if (do_download(conn, new_src, new_dst,
Damien Millerf29238e2013-10-17 11:48:52 +11001553 &(dir_entries[i]->a), preserve_flag,
1554 resume_flag, fsync_flag) == -1) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001555 error("Download of file %s to %s failed",
1556 new_src, new_dst);
1557 ret = -1;
1558 }
1559 } else
1560 logit("%s: not a regular file\n", new_src);
1561
Darren Tucker1b0dd172009-10-07 08:37:48 +11001562 }
djm@openbsd.org1a660792018-07-31 03:07:24 +00001563 free(new_dst);
1564 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001565
Damien Millerbda5c842013-10-15 12:05:58 +11001566 if (preserve_flag) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001567 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1568 struct timeval tv[2];
1569 tv[0].tv_sec = dirattrib->atime;
1570 tv[1].tv_sec = dirattrib->mtime;
1571 tv[0].tv_usec = tv[1].tv_usec = 0;
1572 if (utimes(dst, tv) == -1)
1573 error("Can't set times on \"%s\": %s",
1574 dst, strerror(errno));
1575 } else
1576 debug("Server did not send times for directory "
1577 "\"%s\"", dst);
1578 }
1579
1580 free_sftp_dirents(dir_entries);
1581
1582 return ret;
1583}
1584
1585int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001586download_dir(struct sftp_conn *conn, const char *src, const char *dst,
1587 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1588 int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001589{
1590 char *src_canon;
1591 int ret;
1592
1593 if ((src_canon = do_realpath(conn, src)) == NULL) {
Damien Millerf29238e2013-10-17 11:48:52 +11001594 error("Unable to canonicalize path \"%s\"", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001595 return -1;
1596 }
1597
Damien Millerbda5c842013-10-15 12:05:58 +11001598 ret = download_dir_internal(conn, src_canon, dst, 0,
Damien Millerf29238e2013-10-17 11:48:52 +11001599 dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
Darren Tuckera627d422013-06-02 07:31:17 +10001600 free(src_canon);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001601 return ret;
1602}
1603
Damien Miller33804262001-02-04 23:20:18 +11001604int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001605do_upload(struct sftp_conn *conn, const char *local_path,
1606 const char *remote_path, int preserve_flag, int resume, int fsync_flag)
Damien Miller33804262001-02-04 23:20:18 +11001607{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001608 int r, local_fd;
1609 u_int status = SSH2_FX_OK;
1610 u_int id;
1611 u_char type;
Darren Tuckerc9a19912013-06-02 08:37:05 +10001612 off_t offset, progress_counter;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001613 u_char *handle, *data;
1614 struct sshbuf *msg;
Damien Miller33804262001-02-04 23:20:18 +11001615 struct stat sb;
Damien Millerd8accc02014-05-15 13:46:25 +10001616 Attrib a, *c = NULL;
Damien Miller16a13332002-02-13 14:03:56 +11001617 u_int32_t startid;
1618 u_int32_t ackid;
Damien Miller5873dfd2002-02-13 14:04:37 +11001619 struct outstanding_ack {
1620 u_int id;
1621 u_int len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001622 off_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001623 TAILQ_ENTRY(outstanding_ack) tq;
Damien Miller5873dfd2002-02-13 14:04:37 +11001624 };
1625 TAILQ_HEAD(ackhead, outstanding_ack) acks;
Damien Miller7cf17eb2004-06-15 10:28:56 +10001626 struct outstanding_ack *ack = NULL;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001627 size_t handle_len;
Damien Miller5873dfd2002-02-13 14:04:37 +11001628
1629 TAILQ_INIT(&acks);
Damien Miller33804262001-02-04 23:20:18 +11001630
1631 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1632 error("Couldn't open local file \"%s\" for reading: %s",
1633 local_path, strerror(errno));
1634 return(-1);
1635 }
1636 if (fstat(local_fd, &sb) == -1) {
1637 error("Couldn't fstat local file \"%s\": %s",
1638 local_path, strerror(errno));
1639 close(local_fd);
1640 return(-1);
1641 }
Damien Miller5fa01fd2003-01-14 22:24:47 +11001642 if (!S_ISREG(sb.st_mode)) {
1643 error("%s is not a regular file", local_path);
1644 close(local_fd);
1645 return(-1);
1646 }
Damien Miller33804262001-02-04 23:20:18 +11001647 stat_to_attrib(&sb, &a);
1648
1649 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1650 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1651 a.perm &= 0777;
Damien Millerbda5c842013-10-15 12:05:58 +11001652 if (!preserve_flag)
Damien Miller33804262001-02-04 23:20:18 +11001653 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1654
Damien Millerd8accc02014-05-15 13:46:25 +10001655 if (resume) {
1656 /* Get remote file size if it exists */
1657 if ((c = do_stat(conn, remote_path, 0)) == NULL) {
djm@openbsd.org34a01b22016-04-08 08:19:17 +00001658 close(local_fd);
Damien Millerd8accc02014-05-15 13:46:25 +10001659 return -1;
1660 }
1661
1662 if ((off_t)c->size >= sb.st_size) {
1663 error("destination file bigger or same size as "
1664 "source file");
1665 close(local_fd);
1666 return -1;
1667 }
1668
1669 if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) {
1670 close(local_fd);
1671 return -1;
1672 }
1673 }
1674
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001675 if ((msg = sshbuf_new()) == NULL)
1676 fatal("%s: sshbuf_new failed", __func__);
Damien Miller33804262001-02-04 23:20:18 +11001677
1678 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001679 id = conn->msg_id++;
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001680 if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
1681 (r = sshbuf_put_u32(msg, id)) != 0 ||
1682 (r = sshbuf_put_cstring(msg, remote_path)) != 0 ||
1683 (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|
1684 (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC))) != 0 ||
1685 (r = encode_attrib(msg, &a)) != 0)
1686 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1687 send_msg(conn, msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001688 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001689
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001690 sshbuf_reset(msg);
Damien Miller33804262001-02-04 23:20:18 +11001691
Damien Miller65e42f82010-09-24 22:15:11 +10001692 handle = get_handle(conn, id, &handle_len,
Darren Tuckerc22f0902009-10-07 08:24:19 +11001693 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001694 if (handle == NULL) {
1695 close(local_fd);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001696 sshbuf_free(msg);
Damien Milleracdf25b2008-02-10 22:27:24 +11001697 return -1;
Damien Miller33804262001-02-04 23:20:18 +11001698 }
1699
Damien Miller16a13332002-02-13 14:03:56 +11001700 startid = ackid = id + 1;
Damien Miller3db5f532002-02-13 14:10:32 +11001701 data = xmalloc(conn->transfer_buflen);
Damien Miller8829d362002-02-08 22:04:05 +11001702
Damien Miller33804262001-02-04 23:20:18 +11001703 /* Read from local and write to remote */
Damien Millerd8accc02014-05-15 13:46:25 +10001704 offset = progress_counter = (resume ? c->size : 0);
Damien Miller62d57f62003-01-10 21:43:24 +11001705 if (showprogress)
Darren Tuckerc9a19912013-06-02 08:37:05 +10001706 start_progress_meter(local_path, sb.st_size,
1707 &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +11001708
Damien Miller9f0f5c62001-12-21 14:45:46 +11001709 for (;;) {
Damien Miller33804262001-02-04 23:20:18 +11001710 int len;
Damien Miller33804262001-02-04 23:20:18 +11001711
1712 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001713 * Can't use atomicio here because it returns 0 on EOF,
Darren Tuckercdf547a2004-05-24 10:12:19 +10001714 * thus losing the last block of the file.
Darren Tuckerfc959702004-07-17 16:12:08 +10001715 * Simulate an EOF on interrupt, allowing ACKs from the
Darren Tuckercdf547a2004-05-24 10:12:19 +10001716 * server to drain.
Damien Miller33804262001-02-04 23:20:18 +11001717 */
Damien Milleracdf25b2008-02-10 22:27:24 +11001718 if (interrupted || status != SSH2_FX_OK)
Darren Tuckercdf547a2004-05-24 10:12:19 +10001719 len = 0;
1720 else do
Damien Miller3db5f532002-02-13 14:10:32 +11001721 len = read(local_fd, data, conn->transfer_buflen);
Damien Millerd8968ad2008-07-04 23:10:49 +10001722 while ((len == -1) &&
1723 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
Damien Miller33804262001-02-04 23:20:18 +11001724
1725 if (len == -1)
1726 fatal("Couldn't read from \"%s\": %s", local_path,
1727 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001728
1729 if (len != 0) {
Damien Miller6c81fee2013-11-08 12:19:55 +11001730 ack = xcalloc(1, sizeof(*ack));
Damien Miller5873dfd2002-02-13 14:04:37 +11001731 ack->id = ++id;
1732 ack->offset = offset;
1733 ack->len = len;
1734 TAILQ_INSERT_TAIL(&acks, ack, tq);
1735
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001736 sshbuf_reset(msg);
1737 if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
1738 (r = sshbuf_put_u32(msg, ack->id)) != 0 ||
1739 (r = sshbuf_put_string(msg, handle,
1740 handle_len)) != 0 ||
1741 (r = sshbuf_put_u64(msg, offset)) != 0 ||
1742 (r = sshbuf_put_string(msg, data, len)) != 0)
1743 fatal("%s: buffer error: %s",
1744 __func__, ssh_err(r));
1745 send_msg(conn, msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001746 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001747 id, (unsigned long long)offset, len);
Damien Miller5873dfd2002-02-13 14:04:37 +11001748 } else if (TAILQ_FIRST(&acks) == NULL)
Damien Miller33804262001-02-04 23:20:18 +11001749 break;
1750
Damien Miller5873dfd2002-02-13 14:04:37 +11001751 if (ack == NULL)
1752 fatal("Unexpected ACK %u", id);
1753
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001754 if (id == startid || len == 0 ||
Damien Miller3db5f532002-02-13 14:10:32 +11001755 id - ackid >= conn->num_requests) {
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001756 u_int rid;
Ben Lindstrom06e95152002-04-06 04:16:45 +00001757
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001758 sshbuf_reset(msg);
1759 get_msg(conn, msg);
1760 if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1761 (r = sshbuf_get_u32(msg, &rid)) != 0)
1762 fatal("%s: buffer error: %s",
1763 __func__, ssh_err(r));
Damien Miller5873dfd2002-02-13 14:04:37 +11001764
1765 if (type != SSH2_FXP_STATUS)
1766 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1767 "got %d", SSH2_FXP_STATUS, type);
1768
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001769 if ((r = sshbuf_get_u32(msg, &status)) != 0)
1770 fatal("%s: buffer error: %s",
1771 __func__, ssh_err(r));
1772 debug3("SSH2_FXP_STATUS %u", status);
Damien Miller5873dfd2002-02-13 14:04:37 +11001773
1774 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001775 for (ack = TAILQ_FIRST(&acks);
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001776 ack != NULL && ack->id != rid;
Damien Miller5873dfd2002-02-13 14:04:37 +11001777 ack = TAILQ_NEXT(ack, tq))
1778 ;
1779 if (ack == NULL)
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001780 fatal("Can't find request for ID %u", rid);
Damien Miller5873dfd2002-02-13 14:04:37 +11001781 TAILQ_REMOVE(&acks, ack, tq);
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001782 debug3("In write loop, ack for %u %u bytes at %lld",
1783 ack->id, ack->len, (long long)ack->offset);
Damien Miller16a13332002-02-13 14:03:56 +11001784 ++ackid;
Darren Tuckerc9a19912013-06-02 08:37:05 +10001785 progress_counter += ack->len;
Darren Tuckera627d422013-06-02 07:31:17 +10001786 free(ack);
Damien Miller33804262001-02-04 23:20:18 +11001787 }
Damien Miller33804262001-02-04 23:20:18 +11001788 offset += len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001789 if (offset < 0)
1790 fatal("%s: offset < 0", __func__);
Damien Miller33804262001-02-04 23:20:18 +11001791 }
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001792 sshbuf_free(msg);
Damien Milleracdf25b2008-02-10 22:27:24 +11001793
Damien Miller62d57f62003-01-10 21:43:24 +11001794 if (showprogress)
1795 stop_progress_meter();
Darren Tuckera627d422013-06-02 07:31:17 +10001796 free(data);
Damien Miller33804262001-02-04 23:20:18 +11001797
Damien Milleracdf25b2008-02-10 22:27:24 +11001798 if (status != SSH2_FX_OK) {
1799 error("Couldn't write to remote file \"%s\": %s",
1800 remote_path, fx2txt(status));
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001801 status = SSH2_FX_FAILURE;
Damien Milleracdf25b2008-02-10 22:27:24 +11001802 }
1803
Damien Miller33804262001-02-04 23:20:18 +11001804 if (close(local_fd) == -1) {
1805 error("Couldn't close local file \"%s\": %s", local_path,
1806 strerror(errno));
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001807 status = SSH2_FX_FAILURE;
Damien Miller33804262001-02-04 23:20:18 +11001808 }
1809
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001810 /* Override umask and utimes if asked */
Damien Millerbda5c842013-10-15 12:05:58 +11001811 if (preserve_flag)
Damien Miller3db5f532002-02-13 14:10:32 +11001812 do_fsetstat(conn, handle, handle_len, &a);
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001813
Damien Millerf29238e2013-10-17 11:48:52 +11001814 if (fsync_flag)
1815 (void)do_fsync(conn, handle, handle_len);
1816
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001817 if (do_close(conn, handle, handle_len) != 0)
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001818 status = SSH2_FX_FAILURE;
1819
Darren Tuckera627d422013-06-02 07:31:17 +10001820 free(handle);
Damien Milleracdf25b2008-02-10 22:27:24 +11001821
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001822 return status == SSH2_FX_OK ? 0 : -1;
Damien Miller33804262001-02-04 23:20:18 +11001823}
Darren Tucker1b0dd172009-10-07 08:37:48 +11001824
1825static int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001826upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1827 int depth, int preserve_flag, int print_flag, int resume, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001828{
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001829 int ret = 0;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001830 DIR *dirp;
1831 struct dirent *dp;
djm@openbsd.org1a660792018-07-31 03:07:24 +00001832 char *filename, *new_src = NULL, *new_dst = NULL;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001833 struct stat sb;
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001834 Attrib a, *dirattrib;
Darren Tucker1b0dd172009-10-07 08:37:48 +11001835
1836 if (depth >= MAX_DIR_DEPTH) {
1837 error("Maximum directory depth exceeded: %d levels", depth);
1838 return -1;
1839 }
1840
1841 if (stat(src, &sb) == -1) {
1842 error("Couldn't stat directory \"%s\": %s",
1843 src, strerror(errno));
1844 return -1;
1845 }
1846 if (!S_ISDIR(sb.st_mode)) {
1847 error("\"%s\" is not a directory", src);
1848 return -1;
1849 }
Damien Millerbda5c842013-10-15 12:05:58 +11001850 if (print_flag)
schwarze@openbsd.org0e059cd2016-05-25 23:48:45 +00001851 mprintf("Entering %s\n", src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001852
1853 attrib_clear(&a);
1854 stat_to_attrib(&sb, &a);
1855 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1856 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1857 a.perm &= 01777;
Damien Millerbda5c842013-10-15 12:05:58 +11001858 if (!preserve_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001859 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
Damien Miller0d032412013-07-25 11:56:52 +10001860
Darren Tucker1b0dd172009-10-07 08:37:48 +11001861 /*
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001862 * sftp lacks a portable status value to match errno EEXIST,
1863 * so if we get a failure back then we must check whether
1864 * the path already existed and is a directory.
Darren Tucker1b0dd172009-10-07 08:37:48 +11001865 */
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001866 if (do_mkdir(conn, dst, &a, 0) != 0) {
1867 if ((dirattrib = do_stat(conn, dst, 0)) == NULL)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001868 return -1;
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001869 if (!S_ISDIR(dirattrib->perm)) {
1870 error("\"%s\" exists but is not a directory", dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001871 return -1;
djm@openbsd.orge30cabf2016-02-11 02:21:34 +00001872 }
Darren Tucker1b0dd172009-10-07 08:37:48 +11001873 }
1874
1875 if ((dirp = opendir(src)) == NULL) {
1876 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1877 return -1;
1878 }
Damien Miller0d032412013-07-25 11:56:52 +10001879
Darren Tucker1b0dd172009-10-07 08:37:48 +11001880 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1881 if (dp->d_ino == 0)
1882 continue;
djm@openbsd.org1a660792018-07-31 03:07:24 +00001883 free(new_dst);
1884 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001885 filename = dp->d_name;
1886 new_dst = path_append(dst, filename);
1887 new_src = path_append(src, filename);
1888
Darren Tucker438b4732009-10-11 21:52:10 +11001889 if (lstat(new_src, &sb) == -1) {
1890 logit("%s: lstat failed: %s", filename,
1891 strerror(errno));
1892 ret = -1;
1893 } else if (S_ISDIR(sb.st_mode)) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001894 if (strcmp(filename, ".") == 0 ||
1895 strcmp(filename, "..") == 0)
1896 continue;
1897
1898 if (upload_dir_internal(conn, new_src, new_dst,
Damien Millerd8accc02014-05-15 13:46:25 +10001899 depth + 1, preserve_flag, print_flag, resume,
Damien Millerf29238e2013-10-17 11:48:52 +11001900 fsync_flag) == -1)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001901 ret = -1;
Darren Tucker438b4732009-10-11 21:52:10 +11001902 } else if (S_ISREG(sb.st_mode)) {
Damien Millerbda5c842013-10-15 12:05:58 +11001903 if (do_upload(conn, new_src, new_dst,
Damien Millerd8accc02014-05-15 13:46:25 +10001904 preserve_flag, resume, fsync_flag) == -1) {
Darren Tucker1b0dd172009-10-07 08:37:48 +11001905 error("Uploading of file %s to %s failed!",
1906 new_src, new_dst);
1907 ret = -1;
1908 }
1909 } else
1910 logit("%s: not a regular file\n", filename);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001911 }
djm@openbsd.org1a660792018-07-31 03:07:24 +00001912 free(new_dst);
1913 free(new_src);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001914
1915 do_setstat(conn, dst, &a);
1916
1917 (void) closedir(dirp);
1918 return ret;
1919}
1920
1921int
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001922upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
1923 int preserve_flag, int print_flag, int resume, int fsync_flag)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001924{
1925 char *dst_canon;
1926 int ret;
1927
1928 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
Damien Millerf29238e2013-10-17 11:48:52 +11001929 error("Unable to canonicalize path \"%s\"", dst);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001930 return -1;
1931 }
1932
Damien Miller2f93d052013-10-15 12:06:27 +11001933 ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
Damien Millerd8accc02014-05-15 13:46:25 +10001934 print_flag, resume, fsync_flag);
Damien Millerf29238e2013-10-17 11:48:52 +11001935
Darren Tuckera627d422013-06-02 07:31:17 +10001936 free(dst_canon);
Darren Tucker1b0dd172009-10-07 08:37:48 +11001937 return ret;
1938}
1939
1940char *
djm@openbsd.org7d845f42015-01-14 13:54:13 +00001941path_append(const char *p1, const char *p2)
Darren Tucker1b0dd172009-10-07 08:37:48 +11001942{
1943 char *ret;
1944 size_t len = strlen(p1) + strlen(p2) + 2;
1945
1946 ret = xmalloc(len);
1947 strlcpy(ret, p1, len);
1948 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1949 strlcat(ret, "/", len);
1950 strlcat(ret, p2, len);
1951
1952 return(ret);
1953}
1954