blob: 14c172d2fa9b3dfd75ffb3882259283922c06179 [file] [log] [blame]
Darren Tuckerc22f0902009-10-07 08:24:19 +11001/* $OpenBSD: sftp-client.c,v 1.88 2009/08/14 18:17:49 djm Exp $ */
Damien Miller33804262001-02-04 23:20:18 +11002/*
Damien Miller4e60ed72004-02-17 17:07:59 +11003 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
Damien Miller33804262001-02-04 23:20:18 +11004 *
Damien Miller4e60ed72004-02-17 17:07:59 +11005 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
Damien Miller33804262001-02-04 23:20:18 +11008 *
Damien Miller4e60ed72004-02-17 17:07:59 +11009 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Damien Miller33804262001-02-04 23:20:18 +110016 */
17
18/* XXX: memleaks */
19/* XXX: signed vs unsigned */
Damien Miller3db5f532002-02-13 14:10:32 +110020/* XXX: remove all logging, only return status codes */
Damien Miller33804262001-02-04 23:20:18 +110021/* XXX: copy between two remote sites */
22
23#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110024
25#include <sys/types.h>
Damien Miller8dbffe72006-08-05 11:02:17 +100026#include <sys/param.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100027#ifdef HAVE_SYS_STATVFS_H
Damien Millerd671e5a2008-05-19 14:53:33 +100028#include <sys/statvfs.h>
Darren Tucker5b2e2ba2008-06-08 09:25:28 +100029#endif
Damien Millerd7834352006-08-05 12:39:39 +100030#include "openbsd-compat/sys-queue.h"
Damien Millerf17883e2006-03-15 11:45:54 +110031#ifdef HAVE_SYS_STAT_H
32# include <sys/stat.h>
33#endif
Damien Miller9aec9192006-08-05 10:57:45 +100034#ifdef HAVE_SYS_TIME_H
35# include <sys/time.h>
36#endif
Damien Millerd7834352006-08-05 12:39:39 +100037#include <sys/uio.h>
Damien Miller57cf6382006-07-10 21:13:46 +100038
Darren 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 Millere3476ed2006-07-24 14:13:33 +100044#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100045#include <unistd.h>
Damien Miller16a13332002-02-13 14:03:56 +110046
Damien Miller33804262001-02-04 23:20:18 +110047#include "xmalloc.h"
Damien Millerd7834352006-08-05 12:39:39 +100048#include "buffer.h"
Damien Miller33804262001-02-04 23:20:18 +110049#include "log.h"
50#include "atomicio.h"
Damien Miller62d57f62003-01-10 21:43:24 +110051#include "progressmeter.h"
Damien Miller3f941882006-03-31 23:13:02 +110052#include "misc.h"
Damien Miller33804262001-02-04 23:20:18 +110053
54#include "sftp.h"
55#include "sftp-common.h"
56#include "sftp-client.h"
57
Darren Tuckercdf547a2004-05-24 10:12:19 +100058extern volatile sig_atomic_t interrupted;
Damien Miller62d57f62003-01-10 21:43:24 +110059extern int showprogress;
60
Damien Miller0c8d8f62006-03-15 11:34:25 +110061/* Minimum amount of data to read at a time */
Damien Miller16a13332002-02-13 14:03:56 +110062#define MIN_READ_SIZE 512
63
Damien Miller3db5f532002-02-13 14:10:32 +110064struct sftp_conn {
65 int fd_in;
66 int fd_out;
67 u_int transfer_buflen;
68 u_int num_requests;
69 u_int version;
70 u_int msg_id;
Damien Millerd671e5a2008-05-19 14:53:33 +100071#define SFTP_EXT_POSIX_RENAME 0x00000001
72#define SFTP_EXT_STATVFS 0x00000002
73#define SFTP_EXT_FSTATVFS 0x00000004
Damien Miller7a3e1d02008-03-27 10:59:57 +110074 u_int exts;
Damien Miller3db5f532002-02-13 14:10:32 +110075};
Ben Lindstrom288cc392001-02-09 02:58:04 +000076
Darren Tuckerc22f0902009-10-07 08:24:19 +110077static char *
78get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
79 __attribute__((format(printf, 4, 5)));
80
Ben Lindstrombba81212001-06-25 05:01:22 +000081static void
Damien Miller33804262001-02-04 23:20:18 +110082send_msg(int fd, Buffer *m)
83{
Damien Millera7f3aaa2003-01-10 21:43:58 +110084 u_char mlen[4];
Damien Miller58ca98b2006-04-23 12:06:35 +100085 struct iovec iov[2];
Damien Miller33804262001-02-04 23:20:18 +110086
Damien Miller54446182006-01-02 23:40:50 +110087 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
Damien Millera7f3aaa2003-01-10 21:43:58 +110088 fatal("Outbound message too long %u", buffer_len(m));
Damien Miller33804262001-02-04 23:20:18 +110089
Damien Millera7f3aaa2003-01-10 21:43:58 +110090 /* Send length first */
Damien Miller3f941882006-03-31 23:13:02 +110091 put_u32(mlen, buffer_len(m));
Damien Miller58ca98b2006-04-23 12:06:35 +100092 iov[0].iov_base = mlen;
93 iov[0].iov_len = sizeof(mlen);
94 iov[1].iov_base = buffer_ptr(m);
95 iov[1].iov_len = buffer_len(m);
Damien Millerd7834352006-08-05 12:39:39 +100096
Damien Miller58ca98b2006-04-23 12:06:35 +100097 if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
Damien Millera7f3aaa2003-01-10 21:43:58 +110098 fatal("Couldn't send packet: %s", strerror(errno));
99
100 buffer_clear(m);
Damien Miller33804262001-02-04 23:20:18 +1100101}
102
Ben Lindstrombba81212001-06-25 05:01:22 +0000103static void
Damien Miller33804262001-02-04 23:20:18 +1100104get_msg(int fd, Buffer *m)
105{
Damien Millera7f3aaa2003-01-10 21:43:58 +1100106 u_int msg_len;
Damien Miller33804262001-02-04 23:20:18 +1100107
Damien Millera7f3aaa2003-01-10 21:43:58 +1100108 buffer_append_space(m, 4);
Damien Millerb253cc42005-05-26 12:23:44 +1000109 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
110 if (errno == EPIPE)
111 fatal("Connection closed");
112 else
113 fatal("Couldn't read packet: %s", strerror(errno));
114 }
Damien Miller33804262001-02-04 23:20:18 +1100115
Damien Millera7f3aaa2003-01-10 21:43:58 +1100116 msg_len = buffer_get_int(m);
Damien Miller54446182006-01-02 23:40:50 +1100117 if (msg_len > SFTP_MAX_MSG_LENGTH)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000118 fatal("Received message too long %u", msg_len);
Damien Miller33804262001-02-04 23:20:18 +1100119
Damien Millera7f3aaa2003-01-10 21:43:58 +1100120 buffer_append_space(m, msg_len);
Damien Millerb253cc42005-05-26 12:23:44 +1000121 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
122 if (errno == EPIPE)
123 fatal("Connection closed");
124 else
125 fatal("Read packet: %s", strerror(errno));
126 }
Damien Miller33804262001-02-04 23:20:18 +1100127}
128
Ben Lindstrombba81212001-06-25 05:01:22 +0000129static void
Damien Miller33804262001-02-04 23:20:18 +1100130send_string_request(int fd, u_int id, u_int code, char *s,
131 u_int len)
132{
133 Buffer msg;
134
135 buffer_init(&msg);
136 buffer_put_char(&msg, code);
137 buffer_put_int(&msg, id);
138 buffer_put_string(&msg, s, len);
139 send_msg(fd, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000140 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100141 buffer_free(&msg);
142}
143
Ben Lindstrombba81212001-06-25 05:01:22 +0000144static void
Damien Miller33804262001-02-04 23:20:18 +1100145send_string_attrs_request(int fd, u_int id, u_int code, char *s,
146 u_int len, Attrib *a)
147{
148 Buffer msg;
149
150 buffer_init(&msg);
151 buffer_put_char(&msg, code);
152 buffer_put_int(&msg, id);
153 buffer_put_string(&msg, s, len);
154 encode_attrib(&msg, a);
155 send_msg(fd, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000156 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100157 buffer_free(&msg);
158}
159
Ben Lindstrombba81212001-06-25 05:01:22 +0000160static u_int
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000161get_status(int fd, u_int expected_id)
Damien Miller33804262001-02-04 23:20:18 +1100162{
163 Buffer msg;
164 u_int type, id, status;
165
166 buffer_init(&msg);
167 get_msg(fd, &msg);
168 type = buffer_get_char(&msg);
169 id = buffer_get_int(&msg);
170
171 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000172 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100173 if (type != SSH2_FXP_STATUS)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000174 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100175 SSH2_FXP_STATUS, type);
176
177 status = buffer_get_int(&msg);
178 buffer_free(&msg);
179
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000180 debug3("SSH2_FXP_STATUS %u", status);
Damien Miller33804262001-02-04 23:20:18 +1100181
182 return(status);
183}
184
Ben Lindstrombba81212001-06-25 05:01:22 +0000185static char *
Darren Tuckerc22f0902009-10-07 08:24:19 +1100186get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
Damien Miller33804262001-02-04 23:20:18 +1100187{
188 Buffer msg;
189 u_int type, id;
Darren Tuckerc22f0902009-10-07 08:24:19 +1100190 char *handle, errmsg[256];
191 va_list args;
192 int status;
193
194 va_start(args, errfmt);
195 if (errfmt != NULL)
196 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
197 va_end(args);
Damien Miller33804262001-02-04 23:20:18 +1100198
199 buffer_init(&msg);
200 get_msg(fd, &msg);
201 type = buffer_get_char(&msg);
202 id = buffer_get_int(&msg);
203
204 if (id != expected_id)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100205 fatal("%s: ID mismatch (%u != %u)",
206 errfmt == NULL ? __func__ : errmsg, id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100207 if (type == SSH2_FXP_STATUS) {
Darren Tuckerc22f0902009-10-07 08:24:19 +1100208 status = buffer_get_int(&msg);
209 if (errfmt != NULL)
210 error("%s: %s", errmsg, fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100211 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100212 return(NULL);
213 } else if (type != SSH2_FXP_HANDLE)
Darren Tuckerc22f0902009-10-07 08:24:19 +1100214 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
215 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
Damien Miller33804262001-02-04 23:20:18 +1100216
217 handle = buffer_get_string(&msg, len);
218 buffer_free(&msg);
219
220 return(handle);
221}
222
Ben Lindstrombba81212001-06-25 05:01:22 +0000223static Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000224get_decode_stat(int fd, u_int expected_id, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100225{
226 Buffer msg;
227 u_int type, id;
228 Attrib *a;
229
230 buffer_init(&msg);
231 get_msg(fd, &msg);
232
233 type = buffer_get_char(&msg);
234 id = buffer_get_int(&msg);
235
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000236 debug3("Received stat reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100237 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000238 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100239 if (type == SSH2_FXP_STATUS) {
240 int status = buffer_get_int(&msg);
241
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000242 if (quiet)
243 debug("Couldn't stat remote file: %s", fx2txt(status));
244 else
245 error("Couldn't stat remote file: %s", fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100246 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100247 return(NULL);
248 } else if (type != SSH2_FXP_ATTRS) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000249 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100250 SSH2_FXP_ATTRS, type);
251 }
252 a = decode_attrib(&msg);
253 buffer_free(&msg);
254
255 return(a);
256}
257
Damien Millerd671e5a2008-05-19 14:53:33 +1000258static int
Darren Tucker7b598892008-06-09 22:49:36 +1000259get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
260 int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +1000261{
262 Buffer msg;
263 u_int type, id, flag;
264
265 buffer_init(&msg);
266 get_msg(fd, &msg);
267
268 type = buffer_get_char(&msg);
269 id = buffer_get_int(&msg);
270
271 debug3("Received statvfs reply T:%u I:%u", type, id);
272 if (id != expected_id)
273 fatal("ID mismatch (%u != %u)", id, expected_id);
274 if (type == SSH2_FXP_STATUS) {
275 int status = buffer_get_int(&msg);
276
277 if (quiet)
278 debug("Couldn't statvfs: %s", fx2txt(status));
279 else
280 error("Couldn't statvfs: %s", fx2txt(status));
281 buffer_free(&msg);
282 return -1;
283 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
284 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
285 SSH2_FXP_EXTENDED_REPLY, type);
286 }
287
288 bzero(st, sizeof(*st));
Darren Tucker7b598892008-06-09 22:49:36 +1000289 st->f_bsize = buffer_get_int64(&msg);
290 st->f_frsize = buffer_get_int64(&msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000291 st->f_blocks = buffer_get_int64(&msg);
292 st->f_bfree = buffer_get_int64(&msg);
293 st->f_bavail = buffer_get_int64(&msg);
294 st->f_files = buffer_get_int64(&msg);
295 st->f_ffree = buffer_get_int64(&msg);
296 st->f_favail = buffer_get_int64(&msg);
Darren Tucker294b8412008-06-08 12:57:08 +1000297 st->f_fsid = buffer_get_int64(&msg);
Darren Tucker7b598892008-06-09 22:49:36 +1000298 flag = buffer_get_int64(&msg);
299 st->f_namemax = buffer_get_int64(&msg);
Damien Millerd671e5a2008-05-19 14:53:33 +1000300
301 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
302 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
303
304 buffer_free(&msg);
305
306 return 0;
307}
308
Damien Miller3db5f532002-02-13 14:10:32 +1100309struct sftp_conn *
310do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
Damien Miller33804262001-02-04 23:20:18 +1100311{
Damien Miller7a3e1d02008-03-27 10:59:57 +1100312 u_int type, exts = 0;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000313 int version;
Damien Miller33804262001-02-04 23:20:18 +1100314 Buffer msg;
Damien Miller3db5f532002-02-13 14:10:32 +1100315 struct sftp_conn *ret;
Damien Miller33804262001-02-04 23:20:18 +1100316
317 buffer_init(&msg);
318 buffer_put_char(&msg, SSH2_FXP_INIT);
319 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
320 send_msg(fd_out, &msg);
321
322 buffer_clear(&msg);
323
324 get_msg(fd_in, &msg);
325
Kevin Stevesef4eea92001-02-05 12:42:17 +0000326 /* Expecting a VERSION reply */
Damien Miller33804262001-02-04 23:20:18 +1100327 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000328 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
Damien Miller33804262001-02-04 23:20:18 +1100329 type);
330 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100331 return(NULL);
Damien Miller33804262001-02-04 23:20:18 +1100332 }
333 version = buffer_get_int(&msg);
334
335 debug2("Remote version: %d", version);
336
337 /* Check for extensions */
338 while (buffer_len(&msg) > 0) {
339 char *name = buffer_get_string(&msg, NULL);
340 char *value = buffer_get_string(&msg, NULL);
Darren Tuckera64ab332008-06-13 07:01:29 +1000341 int known = 0;
Damien Miller33804262001-02-04 23:20:18 +1100342
Damien Millerd671e5a2008-05-19 14:53:33 +1000343 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
Darren Tuckera64ab332008-06-13 07:01:29 +1000344 strcmp(value, "1") == 0) {
Damien Miller7a3e1d02008-03-27 10:59:57 +1100345 exts |= SFTP_EXT_POSIX_RENAME;
Darren Tuckera64ab332008-06-13 07:01:29 +1000346 known = 1;
347 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
348 strcmp(value, "2") == 0) {
Damien Millerd671e5a2008-05-19 14:53:33 +1000349 exts |= SFTP_EXT_STATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000350 known = 1;
351 } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
352 strcmp(value, "2") == 0) {
Damien Millerd671e5a2008-05-19 14:53:33 +1000353 exts |= SFTP_EXT_FSTATVFS;
Darren Tuckera64ab332008-06-13 07:01:29 +1000354 known = 1;
355 }
356 if (known) {
357 debug2("Server supports extension \"%s\" revision %s",
358 name, value);
359 } else {
360 debug2("Unrecognised server extension \"%s\"", name);
361 }
Damien Miller33804262001-02-04 23:20:18 +1100362 xfree(name);
363 xfree(value);
364 }
365
366 buffer_free(&msg);
Damien Miller058316f2001-03-08 10:08:49 +1100367
Damien Miller3db5f532002-02-13 14:10:32 +1100368 ret = xmalloc(sizeof(*ret));
369 ret->fd_in = fd_in;
370 ret->fd_out = fd_out;
371 ret->transfer_buflen = transfer_buflen;
372 ret->num_requests = num_requests;
373 ret->version = version;
374 ret->msg_id = 1;
Damien Miller7a3e1d02008-03-27 10:59:57 +1100375 ret->exts = exts;
Damien Miller3db5f532002-02-13 14:10:32 +1100376
377 /* Some filexfer v.0 servers don't support large packets */
378 if (version == 0)
Ben Lindstroma1d81142002-04-02 20:58:11 +0000379 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
Damien Miller3db5f532002-02-13 14:10:32 +1100380
381 return(ret);
382}
383
384u_int
385sftp_proto_version(struct sftp_conn *conn)
386{
387 return(conn->version);
Damien Miller33804262001-02-04 23:20:18 +1100388}
389
390int
Damien Miller3db5f532002-02-13 14:10:32 +1100391do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
Damien Miller33804262001-02-04 23:20:18 +1100392{
393 u_int id, status;
394 Buffer msg;
395
396 buffer_init(&msg);
397
Damien Miller3db5f532002-02-13 14:10:32 +1100398 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100399 buffer_put_char(&msg, SSH2_FXP_CLOSE);
400 buffer_put_int(&msg, id);
401 buffer_put_string(&msg, handle, handle_len);
Damien Miller3db5f532002-02-13 14:10:32 +1100402 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000403 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100404
Damien Miller3db5f532002-02-13 14:10:32 +1100405 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100406 if (status != SSH2_FX_OK)
407 error("Couldn't close file: %s", fx2txt(status));
408
409 buffer_free(&msg);
410
411 return(status);
412}
413
Damien Miller4870afd2001-03-14 10:27:09 +1100414
Ben Lindstrombba81212001-06-25 05:01:22 +0000415static int
Damien Miller3db5f532002-02-13 14:10:32 +1100416do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
Damien Miller4870afd2001-03-14 10:27:09 +1100417 SFTP_DIRENT ***dir)
Damien Miller33804262001-02-04 23:20:18 +1100418{
419 Buffer msg;
Damien Millereccb9de2005-06-17 12:59:34 +1000420 u_int count, type, id, handle_len, i, expected_id, ents = 0;
Damien Miller33804262001-02-04 23:20:18 +1100421 char *handle;
422
Damien Miller3db5f532002-02-13 14:10:32 +1100423 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100424
425 buffer_init(&msg);
426 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
427 buffer_put_int(&msg, id);
428 buffer_put_cstring(&msg, path);
Damien Miller3db5f532002-02-13 14:10:32 +1100429 send_msg(conn->fd_out, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100430
431 buffer_clear(&msg);
432
Darren Tuckerc22f0902009-10-07 08:24:19 +1100433 handle = get_handle(conn->fd_in, id, &handle_len,
434 "remote readdir(\"%s\")", path);
Damien Miller33804262001-02-04 23:20:18 +1100435 if (handle == NULL)
436 return(-1);
437
Damien Miller4870afd2001-03-14 10:27:09 +1100438 if (dir) {
439 ents = 0;
440 *dir = xmalloc(sizeof(**dir));
441 (*dir)[0] = NULL;
442 }
Damien Miller4870afd2001-03-14 10:27:09 +1100443
Darren Tuckercdf547a2004-05-24 10:12:19 +1000444 for (; !interrupted;) {
Damien Miller3db5f532002-02-13 14:10:32 +1100445 id = expected_id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100446
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000447 debug3("Sending SSH2_FXP_READDIR I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100448
449 buffer_clear(&msg);
450 buffer_put_char(&msg, SSH2_FXP_READDIR);
451 buffer_put_int(&msg, id);
452 buffer_put_string(&msg, handle, handle_len);
Damien Miller3db5f532002-02-13 14:10:32 +1100453 send_msg(conn->fd_out, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100454
455 buffer_clear(&msg);
456
Damien Miller3db5f532002-02-13 14:10:32 +1100457 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100458
459 type = buffer_get_char(&msg);
460 id = buffer_get_int(&msg);
461
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000462 debug3("Received reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100463
464 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000465 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100466
467 if (type == SSH2_FXP_STATUS) {
468 int status = buffer_get_int(&msg);
469
470 debug3("Received SSH2_FXP_STATUS %d", status);
471
472 if (status == SSH2_FX_EOF) {
473 break;
474 } else {
475 error("Couldn't read directory: %s",
476 fx2txt(status));
Damien Miller3db5f532002-02-13 14:10:32 +1100477 do_close(conn, handle, handle_len);
Damien Miller00111382003-03-10 11:21:17 +1100478 xfree(handle);
Ben Lindstrom10ac33f2001-02-10 21:53:40 +0000479 return(status);
Damien Miller33804262001-02-04 23:20:18 +1100480 }
481 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000482 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100483 SSH2_FXP_NAME, type);
484
485 count = buffer_get_int(&msg);
Damien Millerd7686fd2001-02-10 00:40:03 +1100486 if (count == 0)
487 break;
488 debug3("Received %d SSH2_FXP_NAME responses", count);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100489 for (i = 0; i < count; i++) {
Damien Miller33804262001-02-04 23:20:18 +1100490 char *filename, *longname;
491 Attrib *a;
492
493 filename = buffer_get_string(&msg, NULL);
494 longname = buffer_get_string(&msg, NULL);
495 a = decode_attrib(&msg);
496
Damien Miller4870afd2001-03-14 10:27:09 +1100497 if (printflag)
498 printf("%s\n", longname);
499
500 if (dir) {
Damien Miller36812092006-03-26 14:22:47 +1100501 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100502 (*dir)[ents] = xmalloc(sizeof(***dir));
503 (*dir)[ents]->filename = xstrdup(filename);
504 (*dir)[ents]->longname = xstrdup(longname);
505 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
506 (*dir)[++ents] = NULL;
507 }
Damien Miller33804262001-02-04 23:20:18 +1100508
509 xfree(filename);
510 xfree(longname);
511 }
512 }
513
514 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100515 do_close(conn, handle, handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100516 xfree(handle);
517
Darren Tuckercdf547a2004-05-24 10:12:19 +1000518 /* Don't return partial matches on interrupt */
519 if (interrupted && dir != NULL && *dir != NULL) {
520 free_sftp_dirents(*dir);
521 *dir = xmalloc(sizeof(**dir));
522 **dir = NULL;
523 }
524
Damien Miller33804262001-02-04 23:20:18 +1100525 return(0);
526}
527
528int
Damien Miller3db5f532002-02-13 14:10:32 +1100529do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
Damien Miller4870afd2001-03-14 10:27:09 +1100530{
Damien Miller3db5f532002-02-13 14:10:32 +1100531 return(do_lsreaddir(conn, path, 0, dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100532}
533
534void free_sftp_dirents(SFTP_DIRENT **s)
535{
536 int i;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100537
538 for (i = 0; s[i]; i++) {
Damien Miller4870afd2001-03-14 10:27:09 +1100539 xfree(s[i]->filename);
540 xfree(s[i]->longname);
541 xfree(s[i]);
542 }
543 xfree(s);
544}
545
546int
Damien Miller3db5f532002-02-13 14:10:32 +1100547do_rm(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100548{
549 u_int status, id;
550
551 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
552
Damien Miller3db5f532002-02-13 14:10:32 +1100553 id = conn->msg_id++;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000554 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100555 strlen(path));
556 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100557 if (status != SSH2_FX_OK)
558 error("Couldn't delete file: %s", fx2txt(status));
559 return(status);
560}
561
562int
Damien Miller3db5f532002-02-13 14:10:32 +1100563do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100564{
565 u_int status, id;
566
Damien Miller3db5f532002-02-13 14:10:32 +1100567 id = conn->msg_id++;
568 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
Damien Miller33804262001-02-04 23:20:18 +1100569 strlen(path), a);
570
Damien Miller3db5f532002-02-13 14:10:32 +1100571 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100572 if (status != SSH2_FX_OK)
573 error("Couldn't create directory: %s", fx2txt(status));
574
575 return(status);
576}
577
578int
Damien Miller3db5f532002-02-13 14:10:32 +1100579do_rmdir(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100580{
581 u_int status, id;
582
Damien Miller3db5f532002-02-13 14:10:32 +1100583 id = conn->msg_id++;
584 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
585 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100586
Damien Miller3db5f532002-02-13 14:10:32 +1100587 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100588 if (status != SSH2_FX_OK)
589 error("Couldn't remove directory: %s", fx2txt(status));
590
591 return(status);
592}
593
594Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100595do_stat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100596{
597 u_int id;
598
Damien Miller3db5f532002-02-13 14:10:32 +1100599 id = conn->msg_id++;
600
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000601 send_string_request(conn->fd_out, id,
602 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
Damien Miller3db5f532002-02-13 14:10:32 +1100603 path, strlen(path));
604
605 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100606}
607
608Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100609do_lstat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100610{
611 u_int id;
612
Damien Miller3db5f532002-02-13 14:10:32 +1100613 if (conn->version == 0) {
614 if (quiet)
615 debug("Server version does not support lstat operation");
616 else
Damien Miller996acd22003-04-09 20:59:48 +1000617 logit("Server version does not support lstat operation");
Ben Lindstromf26ff5b2002-04-02 21:00:31 +0000618 return(do_stat(conn, path, quiet));
Damien Miller3db5f532002-02-13 14:10:32 +1100619 }
620
621 id = conn->msg_id++;
622 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
623 strlen(path));
624
625 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100626}
627
Damien Millercfe23d32008-02-10 22:20:44 +1100628#ifdef notyet
Damien Miller33804262001-02-04 23:20:18 +1100629Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100630do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100631{
632 u_int id;
633
Damien Miller3db5f532002-02-13 14:10:32 +1100634 id = conn->msg_id++;
635 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
636 handle_len);
637
638 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100639}
Damien Millercfe23d32008-02-10 22:20:44 +1100640#endif
Damien Miller33804262001-02-04 23:20:18 +1100641
642int
Damien Miller3db5f532002-02-13 14:10:32 +1100643do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100644{
645 u_int status, id;
646
Damien Miller3db5f532002-02-13 14:10:32 +1100647 id = conn->msg_id++;
648 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
Damien Miller33804262001-02-04 23:20:18 +1100649 strlen(path), a);
650
Damien Miller3db5f532002-02-13 14:10:32 +1100651 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100652 if (status != SSH2_FX_OK)
653 error("Couldn't setstat on \"%s\": %s", path,
654 fx2txt(status));
655
656 return(status);
657}
658
659int
Damien Miller3db5f532002-02-13 14:10:32 +1100660do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
Damien Miller33804262001-02-04 23:20:18 +1100661 Attrib *a)
662{
663 u_int status, id;
664
Damien Miller3db5f532002-02-13 14:10:32 +1100665 id = conn->msg_id++;
666 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
Damien Miller33804262001-02-04 23:20:18 +1100667 handle_len, a);
668
Damien Miller3db5f532002-02-13 14:10:32 +1100669 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100670 if (status != SSH2_FX_OK)
671 error("Couldn't fsetstat: %s", fx2txt(status));
672
673 return(status);
674}
675
676char *
Damien Miller3db5f532002-02-13 14:10:32 +1100677do_realpath(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100678{
679 Buffer msg;
680 u_int type, expected_id, count, id;
681 char *filename, *longname;
682 Attrib *a;
683
Damien Miller3db5f532002-02-13 14:10:32 +1100684 expected_id = id = conn->msg_id++;
685 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
686 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100687
688 buffer_init(&msg);
689
Damien Miller3db5f532002-02-13 14:10:32 +1100690 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100691 type = buffer_get_char(&msg);
692 id = buffer_get_int(&msg);
693
694 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000695 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100696
697 if (type == SSH2_FXP_STATUS) {
698 u_int status = buffer_get_int(&msg);
699
700 error("Couldn't canonicalise: %s", fx2txt(status));
701 return(NULL);
702 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000703 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100704 SSH2_FXP_NAME, type);
705
706 count = buffer_get_int(&msg);
707 if (count != 1)
708 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
709
710 filename = buffer_get_string(&msg, NULL);
711 longname = buffer_get_string(&msg, NULL);
712 a = decode_attrib(&msg);
713
714 debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
715
716 xfree(longname);
717
718 buffer_free(&msg);
719
720 return(filename);
721}
722
723int
Damien Miller3db5f532002-02-13 14:10:32 +1100724do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
Damien Miller33804262001-02-04 23:20:18 +1100725{
726 Buffer msg;
727 u_int status, id;
728
729 buffer_init(&msg);
730
731 /* Send rename request */
Damien Miller3db5f532002-02-13 14:10:32 +1100732 id = conn->msg_id++;
Damien Miller7a3e1d02008-03-27 10:59:57 +1100733 if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
734 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
735 buffer_put_int(&msg, id);
736 buffer_put_cstring(&msg, "posix-rename@openssh.com");
737 } else {
738 buffer_put_char(&msg, SSH2_FXP_RENAME);
739 buffer_put_int(&msg, id);
740 }
Damien Miller33804262001-02-04 23:20:18 +1100741 buffer_put_cstring(&msg, oldpath);
742 buffer_put_cstring(&msg, newpath);
Damien Miller3db5f532002-02-13 14:10:32 +1100743 send_msg(conn->fd_out, &msg);
Damien Miller7a3e1d02008-03-27 10:59:57 +1100744 debug3("Sent message %s \"%s\" -> \"%s\"",
745 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
746 "SSH2_FXP_RENAME", oldpath, newpath);
Damien Miller33804262001-02-04 23:20:18 +1100747 buffer_free(&msg);
748
Damien Miller3db5f532002-02-13 14:10:32 +1100749 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100750 if (status != SSH2_FX_OK)
Damien Miller3db5f532002-02-13 14:10:32 +1100751 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
752 newpath, fx2txt(status));
Damien Miller33804262001-02-04 23:20:18 +1100753
754 return(status);
755}
756
757int
Damien Miller3db5f532002-02-13 14:10:32 +1100758do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
Damien Miller058316f2001-03-08 10:08:49 +1100759{
760 Buffer msg;
761 u_int status, id;
762
Damien Miller3db5f532002-02-13 14:10:32 +1100763 if (conn->version < 3) {
764 error("This server does not support the symlink operation");
765 return(SSH2_FX_OP_UNSUPPORTED);
766 }
767
Damien Miller058316f2001-03-08 10:08:49 +1100768 buffer_init(&msg);
769
Darren Tuckerdca6a4d2004-04-19 22:10:52 +1000770 /* Send symlink request */
Damien Miller3db5f532002-02-13 14:10:32 +1100771 id = conn->msg_id++;
Damien Miller058316f2001-03-08 10:08:49 +1100772 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
773 buffer_put_int(&msg, id);
774 buffer_put_cstring(&msg, oldpath);
775 buffer_put_cstring(&msg, newpath);
Damien Miller3db5f532002-02-13 14:10:32 +1100776 send_msg(conn->fd_out, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100777 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
778 newpath);
779 buffer_free(&msg);
780
Damien Miller3db5f532002-02-13 14:10:32 +1100781 status = get_status(conn->fd_in, id);
Damien Miller058316f2001-03-08 10:08:49 +1100782 if (status != SSH2_FX_OK)
Ben Lindstrom8e879cf2002-11-09 15:48:49 +0000783 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
Damien Miller3db5f532002-02-13 14:10:32 +1100784 newpath, fx2txt(status));
Damien Miller058316f2001-03-08 10:08:49 +1100785
786 return(status);
787}
788
Damien Millercfe23d32008-02-10 22:20:44 +1100789#ifdef notyet
Damien Miller058316f2001-03-08 10:08:49 +1100790char *
Damien Miller3db5f532002-02-13 14:10:32 +1100791do_readlink(struct sftp_conn *conn, char *path)
Damien Miller058316f2001-03-08 10:08:49 +1100792{
793 Buffer msg;
794 u_int type, expected_id, count, id;
795 char *filename, *longname;
796 Attrib *a;
797
Damien Miller3db5f532002-02-13 14:10:32 +1100798 expected_id = id = conn->msg_id++;
799 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
800 strlen(path));
Damien Miller058316f2001-03-08 10:08:49 +1100801
802 buffer_init(&msg);
803
Damien Miller3db5f532002-02-13 14:10:32 +1100804 get_msg(conn->fd_in, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100805 type = buffer_get_char(&msg);
806 id = buffer_get_int(&msg);
807
808 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000809 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller058316f2001-03-08 10:08:49 +1100810
811 if (type == SSH2_FXP_STATUS) {
812 u_int status = buffer_get_int(&msg);
813
814 error("Couldn't readlink: %s", fx2txt(status));
815 return(NULL);
816 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000817 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller058316f2001-03-08 10:08:49 +1100818 SSH2_FXP_NAME, type);
819
820 count = buffer_get_int(&msg);
821 if (count != 1)
822 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
823
824 filename = buffer_get_string(&msg, NULL);
825 longname = buffer_get_string(&msg, NULL);
826 a = decode_attrib(&msg);
827
828 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
829
830 xfree(longname);
831
832 buffer_free(&msg);
833
834 return(filename);
835}
Damien Millercfe23d32008-02-10 22:20:44 +1100836#endif
Damien Miller058316f2001-03-08 10:08:49 +1100837
Damien Millerd671e5a2008-05-19 14:53:33 +1000838int
Darren Tucker7b598892008-06-09 22:49:36 +1000839do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
Damien Millerd671e5a2008-05-19 14:53:33 +1000840 int quiet)
841{
842 Buffer msg;
843 u_int id;
844
845 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
846 error("Server does not support statvfs@openssh.com extension");
847 return -1;
848 }
849
850 id = conn->msg_id++;
851
852 buffer_init(&msg);
853 buffer_clear(&msg);
854 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
855 buffer_put_int(&msg, id);
856 buffer_put_cstring(&msg, "statvfs@openssh.com");
857 buffer_put_cstring(&msg, path);
858 send_msg(conn->fd_out, &msg);
859 buffer_free(&msg);
860
861 return get_decode_statvfs(conn->fd_in, st, id, quiet);
862}
863
864#ifdef notyet
865int
866do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
Darren Tucker7b598892008-06-09 22:49:36 +1000867 struct sftp_statvfs *st, int quiet)
Damien Millerd671e5a2008-05-19 14:53:33 +1000868{
869 Buffer msg;
870 u_int id;
871
872 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
873 error("Server does not support fstatvfs@openssh.com extension");
874 return -1;
875 }
876
877 id = conn->msg_id++;
878
879 buffer_init(&msg);
880 buffer_clear(&msg);
881 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
882 buffer_put_int(&msg, id);
883 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
884 buffer_put_string(&msg, handle, handle_len);
885 send_msg(conn->fd_out, &msg);
886 buffer_free(&msg);
887
888 return get_decode_statvfs(conn->fd_in, st, id, quiet);
889}
890#endif
891
Damien Miller16a13332002-02-13 14:03:56 +1100892static void
893send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
894 char *handle, u_int handle_len)
895{
896 Buffer msg;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000897
Damien Miller16a13332002-02-13 14:03:56 +1100898 buffer_init(&msg);
899 buffer_clear(&msg);
900 buffer_put_char(&msg, SSH2_FXP_READ);
901 buffer_put_int(&msg, id);
902 buffer_put_string(&msg, handle, handle_len);
903 buffer_put_int64(&msg, offset);
904 buffer_put_int(&msg, len);
905 send_msg(fd_out, &msg);
906 buffer_free(&msg);
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000907}
Damien Miller16a13332002-02-13 14:03:56 +1100908
Damien Miller058316f2001-03-08 10:08:49 +1100909int
Damien Miller3db5f532002-02-13 14:10:32 +1100910do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
911 int pflag)
Damien Miller33804262001-02-04 23:20:18 +1100912{
Damien Miller33804262001-02-04 23:20:18 +1100913 Attrib junk, *a;
Damien Miller16a13332002-02-13 14:03:56 +1100914 Buffer msg;
915 char *handle;
Darren Tucker40858532005-08-02 17:07:07 +1000916 int local_fd, status = 0, write_error;
Damien Miller16a13332002-02-13 14:03:56 +1100917 int read_error, write_errno;
918 u_int64_t offset, size;
Damien Millereccb9de2005-06-17 12:59:34 +1000919 u_int handle_len, mode, type, id, buflen, num_req, max_req;
Damien Miller62d57f62003-01-10 21:43:24 +1100920 off_t progress_counter;
Damien Miller16a13332002-02-13 14:03:56 +1100921 struct request {
922 u_int id;
923 u_int len;
924 u_int64_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000925 TAILQ_ENTRY(request) tq;
Damien Miller16a13332002-02-13 14:03:56 +1100926 };
927 TAILQ_HEAD(reqhead, request) requests;
928 struct request *req;
929
930 TAILQ_INIT(&requests);
Damien Miller33804262001-02-04 23:20:18 +1100931
Damien Miller3db5f532002-02-13 14:10:32 +1100932 a = do_stat(conn, remote_path, 0);
Damien Miller33804262001-02-04 23:20:18 +1100933 if (a == NULL)
934 return(-1);
935
Damien Miller9e720282008-06-29 22:46:35 +1000936 /* Do not preserve set[ug]id here, as we do not preserve ownership */
Damien Miller33804262001-02-04 23:20:18 +1100937 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
Damien Miller770b3742003-01-08 14:04:53 +1100938 mode = a->perm & 0777;
Damien Miller33804262001-02-04 23:20:18 +1100939 else
940 mode = 0666;
941
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000942 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
Damien Miller5fa01fd2003-01-14 22:24:47 +1100943 (!S_ISREG(a->perm))) {
944 error("Cannot download non-regular file: %s", remote_path);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000945 return(-1);
946 }
947
Damien Miller16a13332002-02-13 14:03:56 +1100948 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
949 size = a->size;
950 else
951 size = 0;
952
Damien Miller3db5f532002-02-13 14:10:32 +1100953 buflen = conn->transfer_buflen;
Damien Miller33804262001-02-04 23:20:18 +1100954 buffer_init(&msg);
955
956 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +1100957 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100958 buffer_put_char(&msg, SSH2_FXP_OPEN);
959 buffer_put_int(&msg, id);
960 buffer_put_cstring(&msg, remote_path);
961 buffer_put_int(&msg, SSH2_FXF_READ);
962 attrib_clear(&junk); /* Send empty attributes */
963 encode_attrib(&msg, &junk);
Damien Miller3db5f532002-02-13 14:10:32 +1100964 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000965 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +1100966
Darren Tuckerc22f0902009-10-07 08:24:19 +1100967 handle = get_handle(conn->fd_in, id, &handle_len,
968 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +1100969 if (handle == NULL) {
970 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100971 return(-1);
972 }
973
Damien Millera8e06ce2003-11-21 23:48:55 +1100974 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
Damien Miller770b3742003-01-08 14:04:53 +1100975 mode | S_IWRITE);
Damien Miller3db5f532002-02-13 14:10:32 +1100976 if (local_fd == -1) {
977 error("Couldn't open local file \"%s\" for writing: %s",
978 local_path, strerror(errno));
Damien Miller6b0c8182008-02-10 22:23:41 +1100979 do_close(conn, handle, handle_len);
Ben Lindstrom021fcd32002-02-26 18:02:43 +0000980 buffer_free(&msg);
981 xfree(handle);
Damien Miller3db5f532002-02-13 14:10:32 +1100982 return(-1);
983 }
984
Damien Miller33804262001-02-04 23:20:18 +1100985 /* Read from remote and write to local */
Damien Miller16a13332002-02-13 14:03:56 +1100986 write_error = read_error = write_errno = num_req = offset = 0;
987 max_req = 1;
Damien Miller62d57f62003-01-10 21:43:24 +1100988 progress_counter = 0;
989
Damien Miller9ba30692004-03-08 23:12:02 +1100990 if (showprogress && size != 0)
991 start_progress_meter(remote_path, size, &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +1100992
Damien Miller16a13332002-02-13 14:03:56 +1100993 while (num_req > 0 || max_req > 0) {
Damien Miller33804262001-02-04 23:20:18 +1100994 char *data;
Damien Miller16a13332002-02-13 14:03:56 +1100995 u_int len;
Damien Miller33804262001-02-04 23:20:18 +1100996
Darren Tuckercdf547a2004-05-24 10:12:19 +1000997 /*
Darren Tuckerfc959702004-07-17 16:12:08 +1000998 * Simulate EOF on interrupt: stop sending new requests and
Darren Tuckercdf547a2004-05-24 10:12:19 +1000999 * allow outstanding requests to drain gracefully
1000 */
1001 if (interrupted) {
1002 if (num_req == 0) /* If we haven't started yet... */
1003 break;
1004 max_req = 0;
1005 }
1006
Damien Miller16a13332002-02-13 14:03:56 +11001007 /* Send some more requests */
1008 while (num_req < max_req) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001009 debug3("Request range %llu -> %llu (%d/%d)",
Ben Lindstromd45f28c2002-03-22 01:00:57 +00001010 (unsigned long long)offset,
1011 (unsigned long long)offset + buflen - 1,
1012 num_req, max_req);
Damien Miller16a13332002-02-13 14:03:56 +11001013 req = xmalloc(sizeof(*req));
Damien Miller3db5f532002-02-13 14:10:32 +11001014 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001015 req->len = buflen;
1016 req->offset = offset;
1017 offset += buflen;
1018 num_req++;
1019 TAILQ_INSERT_TAIL(&requests, req, tq);
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001020 send_read_request(conn->fd_out, req->id, req->offset,
Damien Miller16a13332002-02-13 14:03:56 +11001021 req->len, handle, handle_len);
1022 }
Damien Miller33804262001-02-04 23:20:18 +11001023
1024 buffer_clear(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +11001025 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +11001026 type = buffer_get_char(&msg);
1027 id = buffer_get_int(&msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001028 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
Damien Miller33804262001-02-04 23:20:18 +11001029
Damien Miller16a13332002-02-13 14:03:56 +11001030 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001031 for (req = TAILQ_FIRST(&requests);
Damien Miller16a13332002-02-13 14:03:56 +11001032 req != NULL && req->id != id;
1033 req = TAILQ_NEXT(req, tq))
1034 ;
1035 if (req == NULL)
1036 fatal("Unexpected reply %u", id);
1037
1038 switch (type) {
1039 case SSH2_FXP_STATUS:
1040 status = buffer_get_int(&msg);
1041 if (status != SSH2_FX_EOF)
1042 read_error = 1;
1043 max_req = 0;
1044 TAILQ_REMOVE(&requests, req, tq);
1045 xfree(req);
1046 num_req--;
1047 break;
1048 case SSH2_FXP_DATA:
1049 data = buffer_get_string(&msg, &len);
Ben Lindstromeb505452002-03-22 01:03:15 +00001050 debug3("Received data %llu -> %llu",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001051 (unsigned long long)req->offset,
Ben Lindstromeb505452002-03-22 01:03:15 +00001052 (unsigned long long)req->offset + len - 1);
Damien Miller16a13332002-02-13 14:03:56 +11001053 if (len > req->len)
1054 fatal("Received more data than asked for "
Ben Lindstrom93576d92002-12-23 02:06:19 +00001055 "%u > %u", len, req->len);
Damien Miller16a13332002-02-13 14:03:56 +11001056 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
Darren Tucker9f63f222003-07-03 13:46:56 +10001057 atomicio(vwrite, local_fd, data, len) != len) &&
Damien Miller16a13332002-02-13 14:03:56 +11001058 !write_error) {
1059 write_errno = errno;
1060 write_error = 1;
1061 max_req = 0;
Damien Miller33804262001-02-04 23:20:18 +11001062 }
Damien Miller62d57f62003-01-10 21:43:24 +11001063 progress_counter += len;
Damien Miller16a13332002-02-13 14:03:56 +11001064 xfree(data);
1065
1066 if (len == req->len) {
1067 TAILQ_REMOVE(&requests, req, tq);
1068 xfree(req);
1069 num_req--;
1070 } else {
1071 /* Resend the request for the missing data */
1072 debug3("Short data block, re-requesting "
Ben Lindstromeb505452002-03-22 01:03:15 +00001073 "%llu -> %llu (%2d)",
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001074 (unsigned long long)req->offset + len,
Ben Lindstrom83b79e42002-03-22 01:05:27 +00001075 (unsigned long long)req->offset +
1076 req->len - 1, num_req);
Damien Miller3db5f532002-02-13 14:10:32 +11001077 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +11001078 req->len -= len;
1079 req->offset += len;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001080 send_read_request(conn->fd_out, req->id,
Damien Miller3db5f532002-02-13 14:10:32 +11001081 req->offset, req->len, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001082 /* Reduce the request size */
1083 if (len < buflen)
1084 buflen = MAX(MIN_READ_SIZE, len);
1085 }
1086 if (max_req > 0) { /* max_req = 0 iff EOF received */
1087 if (size > 0 && offset > size) {
1088 /* Only one request at a time
1089 * after the expected EOF */
1090 debug3("Finish at %llu (%2d)",
Ben Lindstromeb505452002-03-22 01:03:15 +00001091 (unsigned long long)offset,
1092 num_req);
Damien Miller16a13332002-02-13 14:03:56 +11001093 max_req = 1;
Darren Tuckercdf547a2004-05-24 10:12:19 +10001094 } else if (max_req <= conn->num_requests) {
Damien Miller16a13332002-02-13 14:03:56 +11001095 ++max_req;
1096 }
1097 }
1098 break;
1099 default:
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001100 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +11001101 SSH2_FXP_DATA, type);
1102 }
Damien Miller33804262001-02-04 23:20:18 +11001103 }
Damien Miller33804262001-02-04 23:20:18 +11001104
Damien Miller62d57f62003-01-10 21:43:24 +11001105 if (showprogress && size)
1106 stop_progress_meter();
1107
Damien Miller16a13332002-02-13 14:03:56 +11001108 /* Sanity check */
1109 if (TAILQ_FIRST(&requests) != NULL)
1110 fatal("Transfer complete, but requests still in queue");
1111
1112 if (read_error) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001113 error("Couldn't read from remote file \"%s\" : %s",
Damien Miller3db5f532002-02-13 14:10:32 +11001114 remote_path, fx2txt(status));
1115 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001116 } else if (write_error) {
Damien Miller3db5f532002-02-13 14:10:32 +11001117 error("Couldn't write to \"%s\": %s", local_path,
1118 strerror(write_errno));
1119 status = -1;
1120 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001121 } else {
Damien Miller3db5f532002-02-13 14:10:32 +11001122 status = do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001123
1124 /* Override umask and utimes if asked */
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001125#ifdef HAVE_FCHMOD
Damien Miller16a13332002-02-13 14:03:56 +11001126 if (pflag && fchmod(local_fd, mode) == -1)
Damien Millera8e06ce2003-11-21 23:48:55 +11001127#else
Damien Miller16a13332002-02-13 14:03:56 +11001128 if (pflag && chmod(local_path, mode) == -1)
Ben Lindstrom6dc75f52001-02-17 16:47:47 +00001129#endif /* HAVE_FCHMOD */
Damien Miller16a13332002-02-13 14:03:56 +11001130 error("Couldn't set mode on \"%s\": %s", local_path,
Ben Lindstrom93576d92002-12-23 02:06:19 +00001131 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001132 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1133 struct timeval tv[2];
1134 tv[0].tv_sec = a->atime;
1135 tv[1].tv_sec = a->mtime;
1136 tv[0].tv_usec = tv[1].tv_usec = 0;
1137 if (utimes(local_path, tv) == -1)
1138 error("Can't set times on \"%s\": %s",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001139 local_path, strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001140 }
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001141 }
Damien Millerd7686fd2001-02-10 00:40:03 +11001142 close(local_fd);
1143 buffer_free(&msg);
1144 xfree(handle);
Damien Miller3db5f532002-02-13 14:10:32 +11001145
1146 return(status);
Damien Miller33804262001-02-04 23:20:18 +11001147}
1148
1149int
Damien Miller3db5f532002-02-13 14:10:32 +11001150do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1151 int pflag)
Damien Miller33804262001-02-04 23:20:18 +11001152{
Damien Milleracdf25b2008-02-10 22:27:24 +11001153 int local_fd;
1154 int status = SSH2_FX_OK;
Damien Miller5873dfd2002-02-13 14:04:37 +11001155 u_int handle_len, id, type;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001156 off_t offset;
Damien Miller8829d362002-02-08 22:04:05 +11001157 char *handle, *data;
Damien Miller33804262001-02-04 23:20:18 +11001158 Buffer msg;
1159 struct stat sb;
1160 Attrib a;
Damien Miller16a13332002-02-13 14:03:56 +11001161 u_int32_t startid;
1162 u_int32_t ackid;
Damien Miller5873dfd2002-02-13 14:04:37 +11001163 struct outstanding_ack {
1164 u_int id;
1165 u_int len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001166 off_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001167 TAILQ_ENTRY(outstanding_ack) tq;
Damien Miller5873dfd2002-02-13 14:04:37 +11001168 };
1169 TAILQ_HEAD(ackhead, outstanding_ack) acks;
Damien Miller7cf17eb2004-06-15 10:28:56 +10001170 struct outstanding_ack *ack = NULL;
Damien Miller5873dfd2002-02-13 14:04:37 +11001171
1172 TAILQ_INIT(&acks);
Damien Miller33804262001-02-04 23:20:18 +11001173
1174 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1175 error("Couldn't open local file \"%s\" for reading: %s",
1176 local_path, strerror(errno));
1177 return(-1);
1178 }
1179 if (fstat(local_fd, &sb) == -1) {
1180 error("Couldn't fstat local file \"%s\": %s",
1181 local_path, strerror(errno));
1182 close(local_fd);
1183 return(-1);
1184 }
Damien Miller5fa01fd2003-01-14 22:24:47 +11001185 if (!S_ISREG(sb.st_mode)) {
1186 error("%s is not a regular file", local_path);
1187 close(local_fd);
1188 return(-1);
1189 }
Damien Miller33804262001-02-04 23:20:18 +11001190 stat_to_attrib(&sb, &a);
1191
1192 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1193 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1194 a.perm &= 0777;
1195 if (!pflag)
1196 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1197
1198 buffer_init(&msg);
1199
1200 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001201 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +11001202 buffer_put_char(&msg, SSH2_FXP_OPEN);
1203 buffer_put_int(&msg, id);
1204 buffer_put_cstring(&msg, remote_path);
1205 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1206 encode_attrib(&msg, &a);
Damien Miller3db5f532002-02-13 14:10:32 +11001207 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001208 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001209
1210 buffer_clear(&msg);
1211
Darren Tuckerc22f0902009-10-07 08:24:19 +11001212 handle = get_handle(conn->fd_in, id, &handle_len,
1213 "remote open(\"%s\")", remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001214 if (handle == NULL) {
1215 close(local_fd);
1216 buffer_free(&msg);
Damien Milleracdf25b2008-02-10 22:27:24 +11001217 return -1;
Damien Miller33804262001-02-04 23:20:18 +11001218 }
1219
Damien Miller16a13332002-02-13 14:03:56 +11001220 startid = ackid = id + 1;
Damien Miller3db5f532002-02-13 14:10:32 +11001221 data = xmalloc(conn->transfer_buflen);
Damien Miller8829d362002-02-08 22:04:05 +11001222
Damien Miller33804262001-02-04 23:20:18 +11001223 /* Read from local and write to remote */
1224 offset = 0;
Damien Miller62d57f62003-01-10 21:43:24 +11001225 if (showprogress)
1226 start_progress_meter(local_path, sb.st_size, &offset);
Damien Miller62d57f62003-01-10 21:43:24 +11001227
Damien Miller9f0f5c62001-12-21 14:45:46 +11001228 for (;;) {
Damien Miller33804262001-02-04 23:20:18 +11001229 int len;
Damien Miller33804262001-02-04 23:20:18 +11001230
1231 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001232 * Can't use atomicio here because it returns 0 on EOF,
Darren Tuckercdf547a2004-05-24 10:12:19 +10001233 * thus losing the last block of the file.
Darren Tuckerfc959702004-07-17 16:12:08 +10001234 * Simulate an EOF on interrupt, allowing ACKs from the
Darren Tuckercdf547a2004-05-24 10:12:19 +10001235 * server to drain.
Damien Miller33804262001-02-04 23:20:18 +11001236 */
Damien Milleracdf25b2008-02-10 22:27:24 +11001237 if (interrupted || status != SSH2_FX_OK)
Darren Tuckercdf547a2004-05-24 10:12:19 +10001238 len = 0;
1239 else do
Damien Miller3db5f532002-02-13 14:10:32 +11001240 len = read(local_fd, data, conn->transfer_buflen);
Damien Millerd8968ad2008-07-04 23:10:49 +10001241 while ((len == -1) &&
1242 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
Damien Miller33804262001-02-04 23:20:18 +11001243
1244 if (len == -1)
1245 fatal("Couldn't read from \"%s\": %s", local_path,
1246 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001247
1248 if (len != 0) {
Damien Miller5873dfd2002-02-13 14:04:37 +11001249 ack = xmalloc(sizeof(*ack));
1250 ack->id = ++id;
1251 ack->offset = offset;
1252 ack->len = len;
1253 TAILQ_INSERT_TAIL(&acks, ack, tq);
1254
Damien Miller16a13332002-02-13 14:03:56 +11001255 buffer_clear(&msg);
1256 buffer_put_char(&msg, SSH2_FXP_WRITE);
Damien Miller5873dfd2002-02-13 14:04:37 +11001257 buffer_put_int(&msg, ack->id);
Damien Miller16a13332002-02-13 14:03:56 +11001258 buffer_put_string(&msg, handle, handle_len);
1259 buffer_put_int64(&msg, offset);
1260 buffer_put_string(&msg, data, len);
Damien Miller3db5f532002-02-13 14:10:32 +11001261 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001262 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001263 id, (unsigned long long)offset, len);
Damien Miller5873dfd2002-02-13 14:04:37 +11001264 } else if (TAILQ_FIRST(&acks) == NULL)
Damien Miller33804262001-02-04 23:20:18 +11001265 break;
1266
Damien Miller5873dfd2002-02-13 14:04:37 +11001267 if (ack == NULL)
1268 fatal("Unexpected ACK %u", id);
1269
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001270 if (id == startid || len == 0 ||
Damien Miller3db5f532002-02-13 14:10:32 +11001271 id - ackid >= conn->num_requests) {
Ben Lindstrom5a6abda2002-06-09 19:41:48 +00001272 u_int r_id;
Ben Lindstrom06e95152002-04-06 04:16:45 +00001273
Damien Miller5873dfd2002-02-13 14:04:37 +11001274 buffer_clear(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +11001275 get_msg(conn->fd_in, &msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001276 type = buffer_get_char(&msg);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001277 r_id = buffer_get_int(&msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001278
1279 if (type != SSH2_FXP_STATUS)
1280 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1281 "got %d", SSH2_FXP_STATUS, type);
1282
1283 status = buffer_get_int(&msg);
1284 debug3("SSH2_FXP_STATUS %d", status);
1285
1286 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001287 for (ack = TAILQ_FIRST(&acks);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001288 ack != NULL && ack->id != r_id;
Damien Miller5873dfd2002-02-13 14:04:37 +11001289 ack = TAILQ_NEXT(ack, tq))
1290 ;
1291 if (ack == NULL)
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001292 fatal("Can't find request for ID %u", r_id);
Damien Miller5873dfd2002-02-13 14:04:37 +11001293 TAILQ_REMOVE(&acks, ack, tq);
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001294 debug3("In write loop, ack for %u %u bytes at %lld",
1295 ack->id, ack->len, (long long)ack->offset);
Damien Miller16a13332002-02-13 14:03:56 +11001296 ++ackid;
Ben Lindstromeec16fc2002-07-04 00:06:15 +00001297 xfree(ack);
Damien Miller33804262001-02-04 23:20:18 +11001298 }
Damien Miller33804262001-02-04 23:20:18 +11001299 offset += len;
Damien Miller8b3fdfb2007-09-17 16:12:03 +10001300 if (offset < 0)
1301 fatal("%s: offset < 0", __func__);
Damien Miller33804262001-02-04 23:20:18 +11001302 }
Damien Milleracdf25b2008-02-10 22:27:24 +11001303 buffer_free(&msg);
1304
Damien Miller62d57f62003-01-10 21:43:24 +11001305 if (showprogress)
1306 stop_progress_meter();
Damien Miller8829d362002-02-08 22:04:05 +11001307 xfree(data);
Damien Miller33804262001-02-04 23:20:18 +11001308
Damien Milleracdf25b2008-02-10 22:27:24 +11001309 if (status != SSH2_FX_OK) {
1310 error("Couldn't write to remote file \"%s\": %s",
1311 remote_path, fx2txt(status));
1312 status = -1;
1313 }
1314
Damien Miller33804262001-02-04 23:20:18 +11001315 if (close(local_fd) == -1) {
1316 error("Couldn't close local file \"%s\": %s", local_path,
1317 strerror(errno));
Damien Millerd7686fd2001-02-10 00:40:03 +11001318 status = -1;
Damien Miller33804262001-02-04 23:20:18 +11001319 }
1320
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001321 /* Override umask and utimes if asked */
1322 if (pflag)
Damien Miller3db5f532002-02-13 14:10:32 +11001323 do_fsetstat(conn, handle, handle_len, &a);
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001324
Damien Milleracdf25b2008-02-10 22:27:24 +11001325 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1326 status = -1;
Damien Millerd7686fd2001-02-10 00:40:03 +11001327 xfree(handle);
Damien Milleracdf25b2008-02-10 22:27:24 +11001328
1329 return status;
Damien Miller33804262001-02-04 23:20:18 +11001330}