blob: b00dc70bd878aac726cc328419cd1920b3c205ec [file] [log] [blame]
Damien Miller9aec9192006-08-05 10:57:45 +10001/* $OpenBSD: sftp-client.c,v 1.71 2006/07/25 02:59:21 stevesk 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>
26#ifdef HAVE_SYS_STAT_H
27# include <sys/stat.h>
28#endif
Damien Miller9aec9192006-08-05 10:57:45 +100029#ifdef HAVE_SYS_TIME_H
30# include <sys/time.h>
31#endif
Damien Miller57cf6382006-07-10 21:13:46 +100032
Darren Tucker39972492006-07-12 22:22:46 +100033#include <errno.h>
Damien Miller57cf6382006-07-10 21:13:46 +100034#include <fcntl.h>
35#include <signal.h>
Damien Millere3476ed2006-07-24 14:13:33 +100036#include <string.h>
Damien Millere6b3b612006-07-24 14:01:23 +100037#include <unistd.h>
Damien Miller16a13332002-02-13 14:03:56 +110038
Damien Miller9b481512002-09-12 10:43:29 +100039#include "openbsd-compat/sys-queue.h"
Damien Miller33804262001-02-04 23:20:18 +110040
Damien Miller33804262001-02-04 23:20:18 +110041#include "buffer.h"
42#include "bufaux.h"
Damien Miller33804262001-02-04 23:20:18 +110043#include "xmalloc.h"
44#include "log.h"
45#include "atomicio.h"
Damien Miller62d57f62003-01-10 21:43:24 +110046#include "progressmeter.h"
Damien Miller3f941882006-03-31 23:13:02 +110047#include "misc.h"
Damien Miller33804262001-02-04 23:20:18 +110048
49#include "sftp.h"
50#include "sftp-common.h"
51#include "sftp-client.h"
52
Darren Tuckercdf547a2004-05-24 10:12:19 +100053extern volatile sig_atomic_t interrupted;
Damien Miller62d57f62003-01-10 21:43:24 +110054extern int showprogress;
55
Damien Miller0c8d8f62006-03-15 11:34:25 +110056/* Minimum amount of data to read at a time */
Damien Miller16a13332002-02-13 14:03:56 +110057#define MIN_READ_SIZE 512
58
Damien Miller3db5f532002-02-13 14:10:32 +110059struct sftp_conn {
60 int fd_in;
61 int fd_out;
62 u_int transfer_buflen;
63 u_int num_requests;
64 u_int version;
65 u_int msg_id;
66};
Ben Lindstrom288cc392001-02-09 02:58:04 +000067
Ben Lindstrombba81212001-06-25 05:01:22 +000068static void
Damien Miller33804262001-02-04 23:20:18 +110069send_msg(int fd, Buffer *m)
70{
Damien Millera7f3aaa2003-01-10 21:43:58 +110071 u_char mlen[4];
Damien Miller58ca98b2006-04-23 12:06:35 +100072 struct iovec iov[2];
Damien Miller33804262001-02-04 23:20:18 +110073
Damien Miller54446182006-01-02 23:40:50 +110074 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
Damien Millera7f3aaa2003-01-10 21:43:58 +110075 fatal("Outbound message too long %u", buffer_len(m));
Damien Miller33804262001-02-04 23:20:18 +110076
Damien Millera7f3aaa2003-01-10 21:43:58 +110077 /* Send length first */
Damien Miller3f941882006-03-31 23:13:02 +110078 put_u32(mlen, buffer_len(m));
Damien Miller58ca98b2006-04-23 12:06:35 +100079 iov[0].iov_base = mlen;
80 iov[0].iov_len = sizeof(mlen);
81 iov[1].iov_base = buffer_ptr(m);
82 iov[1].iov_len = buffer_len(m);
83
84 if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
Damien Millera7f3aaa2003-01-10 21:43:58 +110085 fatal("Couldn't send packet: %s", strerror(errno));
86
87 buffer_clear(m);
Damien Miller33804262001-02-04 23:20:18 +110088}
89
Ben Lindstrombba81212001-06-25 05:01:22 +000090static void
Damien Miller33804262001-02-04 23:20:18 +110091get_msg(int fd, Buffer *m)
92{
Damien Millera7f3aaa2003-01-10 21:43:58 +110093 u_int msg_len;
Damien Miller33804262001-02-04 23:20:18 +110094
Damien Millera7f3aaa2003-01-10 21:43:58 +110095 buffer_append_space(m, 4);
Damien Millerb253cc42005-05-26 12:23:44 +100096 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
97 if (errno == EPIPE)
98 fatal("Connection closed");
99 else
100 fatal("Couldn't read packet: %s", strerror(errno));
101 }
Damien Miller33804262001-02-04 23:20:18 +1100102
Damien Millera7f3aaa2003-01-10 21:43:58 +1100103 msg_len = buffer_get_int(m);
Damien Miller54446182006-01-02 23:40:50 +1100104 if (msg_len > SFTP_MAX_MSG_LENGTH)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000105 fatal("Received message too long %u", msg_len);
Damien Miller33804262001-02-04 23:20:18 +1100106
Damien Millera7f3aaa2003-01-10 21:43:58 +1100107 buffer_append_space(m, msg_len);
Damien Millerb253cc42005-05-26 12:23:44 +1000108 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
109 if (errno == EPIPE)
110 fatal("Connection closed");
111 else
112 fatal("Read packet: %s", strerror(errno));
113 }
Damien Miller33804262001-02-04 23:20:18 +1100114}
115
Ben Lindstrombba81212001-06-25 05:01:22 +0000116static void
Damien Miller33804262001-02-04 23:20:18 +1100117send_string_request(int fd, u_int id, u_int code, char *s,
118 u_int len)
119{
120 Buffer msg;
121
122 buffer_init(&msg);
123 buffer_put_char(&msg, code);
124 buffer_put_int(&msg, id);
125 buffer_put_string(&msg, s, len);
126 send_msg(fd, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000127 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100128 buffer_free(&msg);
129}
130
Ben Lindstrombba81212001-06-25 05:01:22 +0000131static void
Damien Miller33804262001-02-04 23:20:18 +1100132send_string_attrs_request(int fd, u_int id, u_int code, char *s,
133 u_int len, Attrib *a)
134{
135 Buffer msg;
136
137 buffer_init(&msg);
138 buffer_put_char(&msg, code);
139 buffer_put_int(&msg, id);
140 buffer_put_string(&msg, s, len);
141 encode_attrib(&msg, a);
142 send_msg(fd, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000143 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
Damien Miller33804262001-02-04 23:20:18 +1100144 buffer_free(&msg);
145}
146
Ben Lindstrombba81212001-06-25 05:01:22 +0000147static u_int
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000148get_status(int fd, u_int expected_id)
Damien Miller33804262001-02-04 23:20:18 +1100149{
150 Buffer msg;
151 u_int type, id, status;
152
153 buffer_init(&msg);
154 get_msg(fd, &msg);
155 type = buffer_get_char(&msg);
156 id = buffer_get_int(&msg);
157
158 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000159 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100160 if (type != SSH2_FXP_STATUS)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000161 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100162 SSH2_FXP_STATUS, type);
163
164 status = buffer_get_int(&msg);
165 buffer_free(&msg);
166
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000167 debug3("SSH2_FXP_STATUS %u", status);
Damien Miller33804262001-02-04 23:20:18 +1100168
169 return(status);
170}
171
Ben Lindstrombba81212001-06-25 05:01:22 +0000172static char *
Damien Miller33804262001-02-04 23:20:18 +1100173get_handle(int fd, u_int expected_id, u_int *len)
174{
175 Buffer msg;
176 u_int type, id;
177 char *handle;
178
179 buffer_init(&msg);
180 get_msg(fd, &msg);
181 type = buffer_get_char(&msg);
182 id = buffer_get_int(&msg);
183
184 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000185 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100186 if (type == SSH2_FXP_STATUS) {
187 int status = buffer_get_int(&msg);
188
189 error("Couldn't get handle: %s", fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100190 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100191 return(NULL);
192 } else if (type != SSH2_FXP_HANDLE)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000193 fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100194 SSH2_FXP_HANDLE, type);
195
196 handle = buffer_get_string(&msg, len);
197 buffer_free(&msg);
198
199 return(handle);
200}
201
Ben Lindstrombba81212001-06-25 05:01:22 +0000202static Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000203get_decode_stat(int fd, u_int expected_id, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100204{
205 Buffer msg;
206 u_int type, id;
207 Attrib *a;
208
209 buffer_init(&msg);
210 get_msg(fd, &msg);
211
212 type = buffer_get_char(&msg);
213 id = buffer_get_int(&msg);
214
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000215 debug3("Received stat reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100216 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000217 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100218 if (type == SSH2_FXP_STATUS) {
219 int status = buffer_get_int(&msg);
220
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000221 if (quiet)
222 debug("Couldn't stat remote file: %s", fx2txt(status));
223 else
224 error("Couldn't stat remote file: %s", fx2txt(status));
Darren Tuckercd516ef2004-12-06 22:43:43 +1100225 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100226 return(NULL);
227 } else if (type != SSH2_FXP_ATTRS) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000228 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100229 SSH2_FXP_ATTRS, type);
230 }
231 a = decode_attrib(&msg);
232 buffer_free(&msg);
233
234 return(a);
235}
236
Damien Miller3db5f532002-02-13 14:10:32 +1100237struct sftp_conn *
238do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
Damien Miller33804262001-02-04 23:20:18 +1100239{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000240 u_int type;
241 int version;
Damien Miller33804262001-02-04 23:20:18 +1100242 Buffer msg;
Damien Miller3db5f532002-02-13 14:10:32 +1100243 struct sftp_conn *ret;
Damien Miller33804262001-02-04 23:20:18 +1100244
245 buffer_init(&msg);
246 buffer_put_char(&msg, SSH2_FXP_INIT);
247 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
248 send_msg(fd_out, &msg);
249
250 buffer_clear(&msg);
251
252 get_msg(fd_in, &msg);
253
Kevin Stevesef4eea92001-02-05 12:42:17 +0000254 /* Expecting a VERSION reply */
Damien Miller33804262001-02-04 23:20:18 +1100255 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000256 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
Damien Miller33804262001-02-04 23:20:18 +1100257 type);
258 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100259 return(NULL);
Damien Miller33804262001-02-04 23:20:18 +1100260 }
261 version = buffer_get_int(&msg);
262
263 debug2("Remote version: %d", version);
264
265 /* Check for extensions */
266 while (buffer_len(&msg) > 0) {
267 char *name = buffer_get_string(&msg, NULL);
268 char *value = buffer_get_string(&msg, NULL);
269
270 debug2("Init extension: \"%s\"", name);
271 xfree(name);
272 xfree(value);
273 }
274
275 buffer_free(&msg);
Damien Miller058316f2001-03-08 10:08:49 +1100276
Damien Miller3db5f532002-02-13 14:10:32 +1100277 ret = xmalloc(sizeof(*ret));
278 ret->fd_in = fd_in;
279 ret->fd_out = fd_out;
280 ret->transfer_buflen = transfer_buflen;
281 ret->num_requests = num_requests;
282 ret->version = version;
283 ret->msg_id = 1;
284
285 /* Some filexfer v.0 servers don't support large packets */
286 if (version == 0)
Ben Lindstroma1d81142002-04-02 20:58:11 +0000287 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
Damien Miller3db5f532002-02-13 14:10:32 +1100288
289 return(ret);
290}
291
292u_int
293sftp_proto_version(struct sftp_conn *conn)
294{
295 return(conn->version);
Damien Miller33804262001-02-04 23:20:18 +1100296}
297
298int
Damien Miller3db5f532002-02-13 14:10:32 +1100299do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
Damien Miller33804262001-02-04 23:20:18 +1100300{
301 u_int id, status;
302 Buffer msg;
303
304 buffer_init(&msg);
305
Damien Miller3db5f532002-02-13 14:10:32 +1100306 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100307 buffer_put_char(&msg, SSH2_FXP_CLOSE);
308 buffer_put_int(&msg, id);
309 buffer_put_string(&msg, handle, handle_len);
Damien Miller3db5f532002-02-13 14:10:32 +1100310 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000311 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100312
Damien Miller3db5f532002-02-13 14:10:32 +1100313 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100314 if (status != SSH2_FX_OK)
315 error("Couldn't close file: %s", fx2txt(status));
316
317 buffer_free(&msg);
318
319 return(status);
320}
321
Damien Miller4870afd2001-03-14 10:27:09 +1100322
Ben Lindstrombba81212001-06-25 05:01:22 +0000323static int
Damien Miller3db5f532002-02-13 14:10:32 +1100324do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
Damien Miller4870afd2001-03-14 10:27:09 +1100325 SFTP_DIRENT ***dir)
Damien Miller33804262001-02-04 23:20:18 +1100326{
327 Buffer msg;
Damien Millereccb9de2005-06-17 12:59:34 +1000328 u_int count, type, id, handle_len, i, expected_id, ents = 0;
Damien Miller33804262001-02-04 23:20:18 +1100329 char *handle;
330
Damien Miller3db5f532002-02-13 14:10:32 +1100331 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100332
333 buffer_init(&msg);
334 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
335 buffer_put_int(&msg, id);
336 buffer_put_cstring(&msg, path);
Damien Miller3db5f532002-02-13 14:10:32 +1100337 send_msg(conn->fd_out, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100338
339 buffer_clear(&msg);
340
Damien Miller3db5f532002-02-13 14:10:32 +1100341 handle = get_handle(conn->fd_in, id, &handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100342 if (handle == NULL)
343 return(-1);
344
Damien Miller4870afd2001-03-14 10:27:09 +1100345 if (dir) {
346 ents = 0;
347 *dir = xmalloc(sizeof(**dir));
348 (*dir)[0] = NULL;
349 }
Damien Miller4870afd2001-03-14 10:27:09 +1100350
Darren Tuckercdf547a2004-05-24 10:12:19 +1000351 for (; !interrupted;) {
Damien Miller3db5f532002-02-13 14:10:32 +1100352 id = expected_id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100353
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000354 debug3("Sending SSH2_FXP_READDIR I:%u", id);
Damien Miller33804262001-02-04 23:20:18 +1100355
356 buffer_clear(&msg);
357 buffer_put_char(&msg, SSH2_FXP_READDIR);
358 buffer_put_int(&msg, id);
359 buffer_put_string(&msg, handle, handle_len);
Damien Miller3db5f532002-02-13 14:10:32 +1100360 send_msg(conn->fd_out, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100361
362 buffer_clear(&msg);
363
Damien Miller3db5f532002-02-13 14:10:32 +1100364 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100365
366 type = buffer_get_char(&msg);
367 id = buffer_get_int(&msg);
368
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000369 debug3("Received reply T:%u I:%u", type, id);
Damien Miller33804262001-02-04 23:20:18 +1100370
371 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000372 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100373
374 if (type == SSH2_FXP_STATUS) {
375 int status = buffer_get_int(&msg);
376
377 debug3("Received SSH2_FXP_STATUS %d", status);
378
379 if (status == SSH2_FX_EOF) {
380 break;
381 } else {
382 error("Couldn't read directory: %s",
383 fx2txt(status));
Damien Miller3db5f532002-02-13 14:10:32 +1100384 do_close(conn, handle, handle_len);
Damien Miller00111382003-03-10 11:21:17 +1100385 xfree(handle);
Ben Lindstrom10ac33f2001-02-10 21:53:40 +0000386 return(status);
Damien Miller33804262001-02-04 23:20:18 +1100387 }
388 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000389 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100390 SSH2_FXP_NAME, type);
391
392 count = buffer_get_int(&msg);
Damien Millerd7686fd2001-02-10 00:40:03 +1100393 if (count == 0)
394 break;
395 debug3("Received %d SSH2_FXP_NAME responses", count);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100396 for (i = 0; i < count; i++) {
Damien Miller33804262001-02-04 23:20:18 +1100397 char *filename, *longname;
398 Attrib *a;
399
400 filename = buffer_get_string(&msg, NULL);
401 longname = buffer_get_string(&msg, NULL);
402 a = decode_attrib(&msg);
403
Damien Miller4870afd2001-03-14 10:27:09 +1100404 if (printflag)
405 printf("%s\n", longname);
406
407 if (dir) {
Damien Miller36812092006-03-26 14:22:47 +1100408 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100409 (*dir)[ents] = xmalloc(sizeof(***dir));
410 (*dir)[ents]->filename = xstrdup(filename);
411 (*dir)[ents]->longname = xstrdup(longname);
412 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
413 (*dir)[++ents] = NULL;
414 }
Damien Miller33804262001-02-04 23:20:18 +1100415
416 xfree(filename);
417 xfree(longname);
418 }
419 }
420
421 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100422 do_close(conn, handle, handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100423 xfree(handle);
424
Darren Tuckercdf547a2004-05-24 10:12:19 +1000425 /* Don't return partial matches on interrupt */
426 if (interrupted && dir != NULL && *dir != NULL) {
427 free_sftp_dirents(*dir);
428 *dir = xmalloc(sizeof(**dir));
429 **dir = NULL;
430 }
431
Damien Miller33804262001-02-04 23:20:18 +1100432 return(0);
433}
434
435int
Damien Miller3db5f532002-02-13 14:10:32 +1100436do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
Damien Miller4870afd2001-03-14 10:27:09 +1100437{
Damien Miller3db5f532002-02-13 14:10:32 +1100438 return(do_lsreaddir(conn, path, 0, dir));
Damien Miller4870afd2001-03-14 10:27:09 +1100439}
440
441void free_sftp_dirents(SFTP_DIRENT **s)
442{
443 int i;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100444
445 for (i = 0; s[i]; i++) {
Damien Miller4870afd2001-03-14 10:27:09 +1100446 xfree(s[i]->filename);
447 xfree(s[i]->longname);
448 xfree(s[i]);
449 }
450 xfree(s);
451}
452
453int
Damien Miller3db5f532002-02-13 14:10:32 +1100454do_rm(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100455{
456 u_int status, id;
457
458 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
459
Damien Miller3db5f532002-02-13 14:10:32 +1100460 id = conn->msg_id++;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000461 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
Damien Miller3db5f532002-02-13 14:10:32 +1100462 strlen(path));
463 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100464 if (status != SSH2_FX_OK)
465 error("Couldn't delete file: %s", fx2txt(status));
466 return(status);
467}
468
469int
Damien Miller3db5f532002-02-13 14:10:32 +1100470do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100471{
472 u_int status, id;
473
Damien Miller3db5f532002-02-13 14:10:32 +1100474 id = conn->msg_id++;
475 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
Damien Miller33804262001-02-04 23:20:18 +1100476 strlen(path), a);
477
Damien Miller3db5f532002-02-13 14:10:32 +1100478 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100479 if (status != SSH2_FX_OK)
480 error("Couldn't create directory: %s", fx2txt(status));
481
482 return(status);
483}
484
485int
Damien Miller3db5f532002-02-13 14:10:32 +1100486do_rmdir(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100487{
488 u_int status, id;
489
Damien Miller3db5f532002-02-13 14:10:32 +1100490 id = conn->msg_id++;
491 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
492 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100493
Damien Miller3db5f532002-02-13 14:10:32 +1100494 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100495 if (status != SSH2_FX_OK)
496 error("Couldn't remove directory: %s", fx2txt(status));
497
498 return(status);
499}
500
501Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100502do_stat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100503{
504 u_int id;
505
Damien Miller3db5f532002-02-13 14:10:32 +1100506 id = conn->msg_id++;
507
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000508 send_string_request(conn->fd_out, id,
509 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
Damien Miller3db5f532002-02-13 14:10:32 +1100510 path, strlen(path));
511
512 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100513}
514
515Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100516do_lstat(struct sftp_conn *conn, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100517{
518 u_int id;
519
Damien Miller3db5f532002-02-13 14:10:32 +1100520 if (conn->version == 0) {
521 if (quiet)
522 debug("Server version does not support lstat operation");
523 else
Damien Miller996acd22003-04-09 20:59:48 +1000524 logit("Server version does not support lstat operation");
Ben Lindstromf26ff5b2002-04-02 21:00:31 +0000525 return(do_stat(conn, path, quiet));
Damien Miller3db5f532002-02-13 14:10:32 +1100526 }
527
528 id = conn->msg_id++;
529 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
530 strlen(path));
531
532 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100533}
534
535Attrib *
Damien Miller3db5f532002-02-13 14:10:32 +1100536do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100537{
538 u_int id;
539
Damien Miller3db5f532002-02-13 14:10:32 +1100540 id = conn->msg_id++;
541 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
542 handle_len);
543
544 return(get_decode_stat(conn->fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100545}
546
547int
Damien Miller3db5f532002-02-13 14:10:32 +1100548do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
Damien Miller33804262001-02-04 23:20:18 +1100549{
550 u_int status, id;
551
Damien Miller3db5f532002-02-13 14:10:32 +1100552 id = conn->msg_id++;
553 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
Damien Miller33804262001-02-04 23:20:18 +1100554 strlen(path), a);
555
Damien Miller3db5f532002-02-13 14:10:32 +1100556 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100557 if (status != SSH2_FX_OK)
558 error("Couldn't setstat on \"%s\": %s", path,
559 fx2txt(status));
560
561 return(status);
562}
563
564int
Damien Miller3db5f532002-02-13 14:10:32 +1100565do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
Damien Miller33804262001-02-04 23:20:18 +1100566 Attrib *a)
567{
568 u_int status, id;
569
Damien Miller3db5f532002-02-13 14:10:32 +1100570 id = conn->msg_id++;
571 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
Damien Miller33804262001-02-04 23:20:18 +1100572 handle_len, a);
573
Damien Miller3db5f532002-02-13 14:10:32 +1100574 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100575 if (status != SSH2_FX_OK)
576 error("Couldn't fsetstat: %s", fx2txt(status));
577
578 return(status);
579}
580
581char *
Damien Miller3db5f532002-02-13 14:10:32 +1100582do_realpath(struct sftp_conn *conn, char *path)
Damien Miller33804262001-02-04 23:20:18 +1100583{
584 Buffer msg;
585 u_int type, expected_id, count, id;
586 char *filename, *longname;
587 Attrib *a;
588
Damien Miller3db5f532002-02-13 14:10:32 +1100589 expected_id = id = conn->msg_id++;
590 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
591 strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100592
593 buffer_init(&msg);
594
Damien Miller3db5f532002-02-13 14:10:32 +1100595 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100596 type = buffer_get_char(&msg);
597 id = buffer_get_int(&msg);
598
599 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000600 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller33804262001-02-04 23:20:18 +1100601
602 if (type == SSH2_FXP_STATUS) {
603 u_int status = buffer_get_int(&msg);
604
605 error("Couldn't canonicalise: %s", fx2txt(status));
606 return(NULL);
607 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000608 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100609 SSH2_FXP_NAME, type);
610
611 count = buffer_get_int(&msg);
612 if (count != 1)
613 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
614
615 filename = buffer_get_string(&msg, NULL);
616 longname = buffer_get_string(&msg, NULL);
617 a = decode_attrib(&msg);
618
619 debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
620
621 xfree(longname);
622
623 buffer_free(&msg);
624
625 return(filename);
626}
627
628int
Damien Miller3db5f532002-02-13 14:10:32 +1100629do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
Damien Miller33804262001-02-04 23:20:18 +1100630{
631 Buffer msg;
632 u_int status, id;
633
634 buffer_init(&msg);
635
636 /* Send rename request */
Damien Miller3db5f532002-02-13 14:10:32 +1100637 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100638 buffer_put_char(&msg, SSH2_FXP_RENAME);
639 buffer_put_int(&msg, id);
640 buffer_put_cstring(&msg, oldpath);
641 buffer_put_cstring(&msg, newpath);
Damien Miller3db5f532002-02-13 14:10:32 +1100642 send_msg(conn->fd_out, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100643 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
644 newpath);
645 buffer_free(&msg);
646
Damien Miller3db5f532002-02-13 14:10:32 +1100647 status = get_status(conn->fd_in, id);
Damien Miller33804262001-02-04 23:20:18 +1100648 if (status != SSH2_FX_OK)
Damien Miller3db5f532002-02-13 14:10:32 +1100649 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
650 newpath, fx2txt(status));
Damien Miller33804262001-02-04 23:20:18 +1100651
652 return(status);
653}
654
655int
Damien Miller3db5f532002-02-13 14:10:32 +1100656do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
Damien Miller058316f2001-03-08 10:08:49 +1100657{
658 Buffer msg;
659 u_int status, id;
660
Damien Miller3db5f532002-02-13 14:10:32 +1100661 if (conn->version < 3) {
662 error("This server does not support the symlink operation");
663 return(SSH2_FX_OP_UNSUPPORTED);
664 }
665
Damien Miller058316f2001-03-08 10:08:49 +1100666 buffer_init(&msg);
667
Darren Tuckerdca6a4d2004-04-19 22:10:52 +1000668 /* Send symlink request */
Damien Miller3db5f532002-02-13 14:10:32 +1100669 id = conn->msg_id++;
Damien Miller058316f2001-03-08 10:08:49 +1100670 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
671 buffer_put_int(&msg, id);
672 buffer_put_cstring(&msg, oldpath);
673 buffer_put_cstring(&msg, newpath);
Damien Miller3db5f532002-02-13 14:10:32 +1100674 send_msg(conn->fd_out, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100675 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
676 newpath);
677 buffer_free(&msg);
678
Damien Miller3db5f532002-02-13 14:10:32 +1100679 status = get_status(conn->fd_in, id);
Damien Miller058316f2001-03-08 10:08:49 +1100680 if (status != SSH2_FX_OK)
Ben Lindstrom8e879cf2002-11-09 15:48:49 +0000681 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
Damien Miller3db5f532002-02-13 14:10:32 +1100682 newpath, fx2txt(status));
Damien Miller058316f2001-03-08 10:08:49 +1100683
684 return(status);
685}
686
687char *
Damien Miller3db5f532002-02-13 14:10:32 +1100688do_readlink(struct sftp_conn *conn, char *path)
Damien Miller058316f2001-03-08 10:08:49 +1100689{
690 Buffer msg;
691 u_int type, expected_id, count, id;
692 char *filename, *longname;
693 Attrib *a;
694
Damien Miller3db5f532002-02-13 14:10:32 +1100695 expected_id = id = conn->msg_id++;
696 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
697 strlen(path));
Damien Miller058316f2001-03-08 10:08:49 +1100698
699 buffer_init(&msg);
700
Damien Miller3db5f532002-02-13 14:10:32 +1100701 get_msg(conn->fd_in, &msg);
Damien Miller058316f2001-03-08 10:08:49 +1100702 type = buffer_get_char(&msg);
703 id = buffer_get_int(&msg);
704
705 if (id != expected_id)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000706 fatal("ID mismatch (%u != %u)", id, expected_id);
Damien Miller058316f2001-03-08 10:08:49 +1100707
708 if (type == SSH2_FXP_STATUS) {
709 u_int status = buffer_get_int(&msg);
710
711 error("Couldn't readlink: %s", fx2txt(status));
712 return(NULL);
713 } else if (type != SSH2_FXP_NAME)
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000714 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
Damien Miller058316f2001-03-08 10:08:49 +1100715 SSH2_FXP_NAME, type);
716
717 count = buffer_get_int(&msg);
718 if (count != 1)
719 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
720
721 filename = buffer_get_string(&msg, NULL);
722 longname = buffer_get_string(&msg, NULL);
723 a = decode_attrib(&msg);
724
725 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
726
727 xfree(longname);
728
729 buffer_free(&msg);
730
731 return(filename);
732}
733
Damien Miller16a13332002-02-13 14:03:56 +1100734static void
735send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
736 char *handle, u_int handle_len)
737{
738 Buffer msg;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000739
Damien Miller16a13332002-02-13 14:03:56 +1100740 buffer_init(&msg);
741 buffer_clear(&msg);
742 buffer_put_char(&msg, SSH2_FXP_READ);
743 buffer_put_int(&msg, id);
744 buffer_put_string(&msg, handle, handle_len);
745 buffer_put_int64(&msg, offset);
746 buffer_put_int(&msg, len);
747 send_msg(fd_out, &msg);
748 buffer_free(&msg);
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000749}
Damien Miller16a13332002-02-13 14:03:56 +1100750
Damien Miller058316f2001-03-08 10:08:49 +1100751int
Damien Miller3db5f532002-02-13 14:10:32 +1100752do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
753 int pflag)
Damien Miller33804262001-02-04 23:20:18 +1100754{
Damien Miller33804262001-02-04 23:20:18 +1100755 Attrib junk, *a;
Damien Miller16a13332002-02-13 14:03:56 +1100756 Buffer msg;
757 char *handle;
Darren Tucker40858532005-08-02 17:07:07 +1000758 int local_fd, status = 0, write_error;
Damien Miller16a13332002-02-13 14:03:56 +1100759 int read_error, write_errno;
760 u_int64_t offset, size;
Damien Millereccb9de2005-06-17 12:59:34 +1000761 u_int handle_len, mode, type, id, buflen, num_req, max_req;
Damien Miller62d57f62003-01-10 21:43:24 +1100762 off_t progress_counter;
Damien Miller16a13332002-02-13 14:03:56 +1100763 struct request {
764 u_int id;
765 u_int len;
766 u_int64_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000767 TAILQ_ENTRY(request) tq;
Damien Miller16a13332002-02-13 14:03:56 +1100768 };
769 TAILQ_HEAD(reqhead, request) requests;
770 struct request *req;
771
772 TAILQ_INIT(&requests);
Damien Miller33804262001-02-04 23:20:18 +1100773
Damien Miller3db5f532002-02-13 14:10:32 +1100774 a = do_stat(conn, remote_path, 0);
Damien Miller33804262001-02-04 23:20:18 +1100775 if (a == NULL)
776 return(-1);
777
778 /* XXX: should we preserve set[ug]id? */
779 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
Damien Miller770b3742003-01-08 14:04:53 +1100780 mode = a->perm & 0777;
Damien Miller33804262001-02-04 23:20:18 +1100781 else
782 mode = 0666;
783
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000784 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
Damien Miller5fa01fd2003-01-14 22:24:47 +1100785 (!S_ISREG(a->perm))) {
786 error("Cannot download non-regular file: %s", remote_path);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000787 return(-1);
788 }
789
Damien Miller16a13332002-02-13 14:03:56 +1100790 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
791 size = a->size;
792 else
793 size = 0;
794
Damien Miller3db5f532002-02-13 14:10:32 +1100795 buflen = conn->transfer_buflen;
Damien Miller33804262001-02-04 23:20:18 +1100796 buffer_init(&msg);
797
798 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +1100799 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100800 buffer_put_char(&msg, SSH2_FXP_OPEN);
801 buffer_put_int(&msg, id);
802 buffer_put_cstring(&msg, remote_path);
803 buffer_put_int(&msg, SSH2_FXF_READ);
804 attrib_clear(&junk); /* Send empty attributes */
805 encode_attrib(&msg, &junk);
Damien Miller3db5f532002-02-13 14:10:32 +1100806 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000807 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +1100808
Damien Miller3db5f532002-02-13 14:10:32 +1100809 handle = get_handle(conn->fd_in, id, &handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100810 if (handle == NULL) {
811 buffer_free(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100812 return(-1);
813 }
814
Damien Millera8e06ce2003-11-21 23:48:55 +1100815 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
Damien Miller770b3742003-01-08 14:04:53 +1100816 mode | S_IWRITE);
Damien Miller3db5f532002-02-13 14:10:32 +1100817 if (local_fd == -1) {
818 error("Couldn't open local file \"%s\" for writing: %s",
819 local_path, strerror(errno));
Ben Lindstrom021fcd32002-02-26 18:02:43 +0000820 buffer_free(&msg);
821 xfree(handle);
Damien Miller3db5f532002-02-13 14:10:32 +1100822 return(-1);
823 }
824
Damien Miller33804262001-02-04 23:20:18 +1100825 /* Read from remote and write to local */
Damien Miller16a13332002-02-13 14:03:56 +1100826 write_error = read_error = write_errno = num_req = offset = 0;
827 max_req = 1;
Damien Miller62d57f62003-01-10 21:43:24 +1100828 progress_counter = 0;
829
Damien Miller9ba30692004-03-08 23:12:02 +1100830 if (showprogress && size != 0)
831 start_progress_meter(remote_path, size, &progress_counter);
Damien Miller62d57f62003-01-10 21:43:24 +1100832
Damien Miller16a13332002-02-13 14:03:56 +1100833 while (num_req > 0 || max_req > 0) {
Damien Miller33804262001-02-04 23:20:18 +1100834 char *data;
Damien Miller16a13332002-02-13 14:03:56 +1100835 u_int len;
Damien Miller33804262001-02-04 23:20:18 +1100836
Darren Tuckercdf547a2004-05-24 10:12:19 +1000837 /*
Darren Tuckerfc959702004-07-17 16:12:08 +1000838 * Simulate EOF on interrupt: stop sending new requests and
Darren Tuckercdf547a2004-05-24 10:12:19 +1000839 * allow outstanding requests to drain gracefully
840 */
841 if (interrupted) {
842 if (num_req == 0) /* If we haven't started yet... */
843 break;
844 max_req = 0;
845 }
846
Damien Miller16a13332002-02-13 14:03:56 +1100847 /* Send some more requests */
848 while (num_req < max_req) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000849 debug3("Request range %llu -> %llu (%d/%d)",
Ben Lindstromd45f28c2002-03-22 01:00:57 +0000850 (unsigned long long)offset,
851 (unsigned long long)offset + buflen - 1,
852 num_req, max_req);
Damien Miller16a13332002-02-13 14:03:56 +1100853 req = xmalloc(sizeof(*req));
Damien Miller3db5f532002-02-13 14:10:32 +1100854 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +1100855 req->len = buflen;
856 req->offset = offset;
857 offset += buflen;
858 num_req++;
859 TAILQ_INSERT_TAIL(&requests, req, tq);
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000860 send_read_request(conn->fd_out, req->id, req->offset,
Damien Miller16a13332002-02-13 14:03:56 +1100861 req->len, handle, handle_len);
862 }
Damien Miller33804262001-02-04 23:20:18 +1100863
864 buffer_clear(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +1100865 get_msg(conn->fd_in, &msg);
Damien Miller33804262001-02-04 23:20:18 +1100866 type = buffer_get_char(&msg);
867 id = buffer_get_int(&msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000868 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
Damien Miller33804262001-02-04 23:20:18 +1100869
Damien Miller16a13332002-02-13 14:03:56 +1100870 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +1100871 for (req = TAILQ_FIRST(&requests);
Damien Miller16a13332002-02-13 14:03:56 +1100872 req != NULL && req->id != id;
873 req = TAILQ_NEXT(req, tq))
874 ;
875 if (req == NULL)
876 fatal("Unexpected reply %u", id);
877
878 switch (type) {
879 case SSH2_FXP_STATUS:
880 status = buffer_get_int(&msg);
881 if (status != SSH2_FX_EOF)
882 read_error = 1;
883 max_req = 0;
884 TAILQ_REMOVE(&requests, req, tq);
885 xfree(req);
886 num_req--;
887 break;
888 case SSH2_FXP_DATA:
889 data = buffer_get_string(&msg, &len);
Ben Lindstromeb505452002-03-22 01:03:15 +0000890 debug3("Received data %llu -> %llu",
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000891 (unsigned long long)req->offset,
Ben Lindstromeb505452002-03-22 01:03:15 +0000892 (unsigned long long)req->offset + len - 1);
Damien Miller16a13332002-02-13 14:03:56 +1100893 if (len > req->len)
894 fatal("Received more data than asked for "
Ben Lindstrom93576d92002-12-23 02:06:19 +0000895 "%u > %u", len, req->len);
Damien Miller16a13332002-02-13 14:03:56 +1100896 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
Darren Tucker9f63f222003-07-03 13:46:56 +1000897 atomicio(vwrite, local_fd, data, len) != len) &&
Damien Miller16a13332002-02-13 14:03:56 +1100898 !write_error) {
899 write_errno = errno;
900 write_error = 1;
901 max_req = 0;
Damien Miller33804262001-02-04 23:20:18 +1100902 }
Damien Miller62d57f62003-01-10 21:43:24 +1100903 progress_counter += len;
Damien Miller16a13332002-02-13 14:03:56 +1100904 xfree(data);
905
906 if (len == req->len) {
907 TAILQ_REMOVE(&requests, req, tq);
908 xfree(req);
909 num_req--;
910 } else {
911 /* Resend the request for the missing data */
912 debug3("Short data block, re-requesting "
Ben Lindstromeb505452002-03-22 01:03:15 +0000913 "%llu -> %llu (%2d)",
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000914 (unsigned long long)req->offset + len,
Ben Lindstrom83b79e42002-03-22 01:05:27 +0000915 (unsigned long long)req->offset +
916 req->len - 1, num_req);
Damien Miller3db5f532002-02-13 14:10:32 +1100917 req->id = conn->msg_id++;
Damien Miller16a13332002-02-13 14:03:56 +1100918 req->len -= len;
919 req->offset += len;
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000920 send_read_request(conn->fd_out, req->id,
Damien Miller3db5f532002-02-13 14:10:32 +1100921 req->offset, req->len, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +1100922 /* Reduce the request size */
923 if (len < buflen)
924 buflen = MAX(MIN_READ_SIZE, len);
925 }
926 if (max_req > 0) { /* max_req = 0 iff EOF received */
927 if (size > 0 && offset > size) {
928 /* Only one request at a time
929 * after the expected EOF */
930 debug3("Finish at %llu (%2d)",
Ben Lindstromeb505452002-03-22 01:03:15 +0000931 (unsigned long long)offset,
932 num_req);
Damien Miller16a13332002-02-13 14:03:56 +1100933 max_req = 1;
Darren Tuckercdf547a2004-05-24 10:12:19 +1000934 } else if (max_req <= conn->num_requests) {
Damien Miller16a13332002-02-13 14:03:56 +1100935 ++max_req;
936 }
937 }
938 break;
939 default:
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000940 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
Damien Miller33804262001-02-04 23:20:18 +1100941 SSH2_FXP_DATA, type);
942 }
Damien Miller33804262001-02-04 23:20:18 +1100943 }
Damien Miller33804262001-02-04 23:20:18 +1100944
Damien Miller62d57f62003-01-10 21:43:24 +1100945 if (showprogress && size)
946 stop_progress_meter();
947
Damien Miller16a13332002-02-13 14:03:56 +1100948 /* Sanity check */
949 if (TAILQ_FIRST(&requests) != NULL)
950 fatal("Transfer complete, but requests still in queue");
951
952 if (read_error) {
Ben Lindstrom6328ab32002-03-22 02:54:23 +0000953 error("Couldn't read from remote file \"%s\" : %s",
Damien Miller3db5f532002-02-13 14:10:32 +1100954 remote_path, fx2txt(status));
955 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +1100956 } else if (write_error) {
Damien Miller3db5f532002-02-13 14:10:32 +1100957 error("Couldn't write to \"%s\": %s", local_path,
958 strerror(write_errno));
959 status = -1;
960 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +1100961 } else {
Damien Miller3db5f532002-02-13 14:10:32 +1100962 status = do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +1100963
964 /* Override umask and utimes if asked */
Ben Lindstrom6dc75f52001-02-17 16:47:47 +0000965#ifdef HAVE_FCHMOD
Damien Miller16a13332002-02-13 14:03:56 +1100966 if (pflag && fchmod(local_fd, mode) == -1)
Damien Millera8e06ce2003-11-21 23:48:55 +1100967#else
Damien Miller16a13332002-02-13 14:03:56 +1100968 if (pflag && chmod(local_path, mode) == -1)
Ben Lindstrom6dc75f52001-02-17 16:47:47 +0000969#endif /* HAVE_FCHMOD */
Damien Miller16a13332002-02-13 14:03:56 +1100970 error("Couldn't set mode on \"%s\": %s", local_path,
Ben Lindstrom93576d92002-12-23 02:06:19 +0000971 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +1100972 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
973 struct timeval tv[2];
974 tv[0].tv_sec = a->atime;
975 tv[1].tv_sec = a->mtime;
976 tv[0].tv_usec = tv[1].tv_usec = 0;
977 if (utimes(local_path, tv) == -1)
978 error("Can't set times on \"%s\": %s",
Ben Lindstrom93576d92002-12-23 02:06:19 +0000979 local_path, strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +1100980 }
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000981 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100982 close(local_fd);
983 buffer_free(&msg);
984 xfree(handle);
Damien Miller3db5f532002-02-13 14:10:32 +1100985
986 return(status);
Damien Miller33804262001-02-04 23:20:18 +1100987}
988
989int
Damien Miller3db5f532002-02-13 14:10:32 +1100990do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
991 int pflag)
Damien Miller33804262001-02-04 23:20:18 +1100992{
Damien Miller8829d362002-02-08 22:04:05 +1100993 int local_fd, status;
Damien Miller5873dfd2002-02-13 14:04:37 +1100994 u_int handle_len, id, type;
Damien Miller33804262001-02-04 23:20:18 +1100995 u_int64_t offset;
Damien Miller8829d362002-02-08 22:04:05 +1100996 char *handle, *data;
Damien Miller33804262001-02-04 23:20:18 +1100997 Buffer msg;
998 struct stat sb;
999 Attrib a;
Damien Miller16a13332002-02-13 14:03:56 +11001000 u_int32_t startid;
1001 u_int32_t ackid;
Damien Miller5873dfd2002-02-13 14:04:37 +11001002 struct outstanding_ack {
1003 u_int id;
1004 u_int len;
1005 u_int64_t offset;
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001006 TAILQ_ENTRY(outstanding_ack) tq;
Damien Miller5873dfd2002-02-13 14:04:37 +11001007 };
1008 TAILQ_HEAD(ackhead, outstanding_ack) acks;
Damien Miller7cf17eb2004-06-15 10:28:56 +10001009 struct outstanding_ack *ack = NULL;
Damien Miller5873dfd2002-02-13 14:04:37 +11001010
1011 TAILQ_INIT(&acks);
Damien Miller33804262001-02-04 23:20:18 +11001012
1013 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1014 error("Couldn't open local file \"%s\" for reading: %s",
1015 local_path, strerror(errno));
1016 return(-1);
1017 }
1018 if (fstat(local_fd, &sb) == -1) {
1019 error("Couldn't fstat local file \"%s\": %s",
1020 local_path, strerror(errno));
1021 close(local_fd);
1022 return(-1);
1023 }
Damien Miller5fa01fd2003-01-14 22:24:47 +11001024 if (!S_ISREG(sb.st_mode)) {
1025 error("%s is not a regular file", local_path);
1026 close(local_fd);
1027 return(-1);
1028 }
Damien Miller33804262001-02-04 23:20:18 +11001029 stat_to_attrib(&sb, &a);
1030
1031 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1032 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1033 a.perm &= 0777;
1034 if (!pflag)
1035 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1036
1037 buffer_init(&msg);
1038
1039 /* Send open request */
Damien Miller3db5f532002-02-13 14:10:32 +11001040 id = conn->msg_id++;
Damien Miller33804262001-02-04 23:20:18 +11001041 buffer_put_char(&msg, SSH2_FXP_OPEN);
1042 buffer_put_int(&msg, id);
1043 buffer_put_cstring(&msg, remote_path);
1044 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1045 encode_attrib(&msg, &a);
Damien Miller3db5f532002-02-13 14:10:32 +11001046 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001047 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
Damien Miller33804262001-02-04 23:20:18 +11001048
1049 buffer_clear(&msg);
1050
Damien Miller3db5f532002-02-13 14:10:32 +11001051 handle = get_handle(conn->fd_in, id, &handle_len);
Damien Miller33804262001-02-04 23:20:18 +11001052 if (handle == NULL) {
1053 close(local_fd);
1054 buffer_free(&msg);
1055 return(-1);
1056 }
1057
Damien Miller16a13332002-02-13 14:03:56 +11001058 startid = ackid = id + 1;
Damien Miller3db5f532002-02-13 14:10:32 +11001059 data = xmalloc(conn->transfer_buflen);
Damien Miller8829d362002-02-08 22:04:05 +11001060
Damien Miller33804262001-02-04 23:20:18 +11001061 /* Read from local and write to remote */
1062 offset = 0;
Damien Miller62d57f62003-01-10 21:43:24 +11001063 if (showprogress)
1064 start_progress_meter(local_path, sb.st_size, &offset);
Damien Miller62d57f62003-01-10 21:43:24 +11001065
Damien Miller9f0f5c62001-12-21 14:45:46 +11001066 for (;;) {
Damien Miller33804262001-02-04 23:20:18 +11001067 int len;
Damien Miller33804262001-02-04 23:20:18 +11001068
1069 /*
Darren Tuckerfc959702004-07-17 16:12:08 +10001070 * Can't use atomicio here because it returns 0 on EOF,
Darren Tuckercdf547a2004-05-24 10:12:19 +10001071 * thus losing the last block of the file.
Darren Tuckerfc959702004-07-17 16:12:08 +10001072 * Simulate an EOF on interrupt, allowing ACKs from the
Darren Tuckercdf547a2004-05-24 10:12:19 +10001073 * server to drain.
Damien Miller33804262001-02-04 23:20:18 +11001074 */
Darren Tuckercdf547a2004-05-24 10:12:19 +10001075 if (interrupted)
1076 len = 0;
1077 else do
Damien Miller3db5f532002-02-13 14:10:32 +11001078 len = read(local_fd, data, conn->transfer_buflen);
Damien Miller33804262001-02-04 23:20:18 +11001079 while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1080
1081 if (len == -1)
1082 fatal("Couldn't read from \"%s\": %s", local_path,
1083 strerror(errno));
Damien Miller16a13332002-02-13 14:03:56 +11001084
1085 if (len != 0) {
Damien Miller5873dfd2002-02-13 14:04:37 +11001086 ack = xmalloc(sizeof(*ack));
1087 ack->id = ++id;
1088 ack->offset = offset;
1089 ack->len = len;
1090 TAILQ_INSERT_TAIL(&acks, ack, tq);
1091
Damien Miller16a13332002-02-13 14:03:56 +11001092 buffer_clear(&msg);
1093 buffer_put_char(&msg, SSH2_FXP_WRITE);
Damien Miller5873dfd2002-02-13 14:04:37 +11001094 buffer_put_int(&msg, ack->id);
Damien Miller16a13332002-02-13 14:03:56 +11001095 buffer_put_string(&msg, handle, handle_len);
1096 buffer_put_int64(&msg, offset);
1097 buffer_put_string(&msg, data, len);
Damien Miller3db5f532002-02-13 14:10:32 +11001098 send_msg(conn->fd_out, &msg);
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001099 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001100 id, (unsigned long long)offset, len);
Damien Miller5873dfd2002-02-13 14:04:37 +11001101 } else if (TAILQ_FIRST(&acks) == NULL)
Damien Miller33804262001-02-04 23:20:18 +11001102 break;
1103
Damien Miller5873dfd2002-02-13 14:04:37 +11001104 if (ack == NULL)
1105 fatal("Unexpected ACK %u", id);
1106
Ben Lindstrom6328ab32002-03-22 02:54:23 +00001107 if (id == startid || len == 0 ||
Damien Miller3db5f532002-02-13 14:10:32 +11001108 id - ackid >= conn->num_requests) {
Ben Lindstrom5a6abda2002-06-09 19:41:48 +00001109 u_int r_id;
Ben Lindstrom06e95152002-04-06 04:16:45 +00001110
Damien Miller5873dfd2002-02-13 14:04:37 +11001111 buffer_clear(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +11001112 get_msg(conn->fd_in, &msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001113 type = buffer_get_char(&msg);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001114 r_id = buffer_get_int(&msg);
Damien Miller5873dfd2002-02-13 14:04:37 +11001115
1116 if (type != SSH2_FXP_STATUS)
1117 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1118 "got %d", SSH2_FXP_STATUS, type);
1119
1120 status = buffer_get_int(&msg);
1121 debug3("SSH2_FXP_STATUS %d", status);
1122
1123 /* Find the request in our queue */
Darren Tucker47eede72005-03-14 23:08:12 +11001124 for (ack = TAILQ_FIRST(&acks);
Ben Lindstrom06e95152002-04-06 04:16:45 +00001125 ack != NULL && ack->id != r_id;
Damien Miller5873dfd2002-02-13 14:04:37 +11001126 ack = TAILQ_NEXT(ack, tq))
1127 ;
1128 if (ack == NULL)
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001129 fatal("Can't find request for ID %u", r_id);
Damien Miller5873dfd2002-02-13 14:04:37 +11001130 TAILQ_REMOVE(&acks, ack, tq);
1131
Damien Miller16a13332002-02-13 14:03:56 +11001132 if (status != SSH2_FX_OK) {
1133 error("Couldn't write to remote file \"%s\": %s",
Ben Lindstrom93576d92002-12-23 02:06:19 +00001134 remote_path, fx2txt(status));
Damien Miller3db5f532002-02-13 14:10:32 +11001135 do_close(conn, handle, handle_len);
Damien Miller16a13332002-02-13 14:03:56 +11001136 close(local_fd);
Damien Miller00111382003-03-10 11:21:17 +11001137 xfree(data);
1138 xfree(ack);
Damien Miller16a13332002-02-13 14:03:56 +11001139 goto done;
1140 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +00001141 debug3("In write loop, ack for %u %u bytes at %llu",
Damien Miller0dc1bef2005-07-17 17:22:45 +10001142 ack->id, ack->len, (unsigned long long)ack->offset);
Damien Miller16a13332002-02-13 14:03:56 +11001143 ++ackid;
Ben Lindstromeec16fc2002-07-04 00:06:15 +00001144 xfree(ack);
Damien Miller33804262001-02-04 23:20:18 +11001145 }
Damien Miller33804262001-02-04 23:20:18 +11001146 offset += len;
1147 }
Damien Miller62d57f62003-01-10 21:43:24 +11001148 if (showprogress)
1149 stop_progress_meter();
Damien Miller8829d362002-02-08 22:04:05 +11001150 xfree(data);
Damien Miller33804262001-02-04 23:20:18 +11001151
1152 if (close(local_fd) == -1) {
1153 error("Couldn't close local file \"%s\": %s", local_path,
1154 strerror(errno));
Damien Miller3db5f532002-02-13 14:10:32 +11001155 do_close(conn, handle, handle_len);
Damien Millerd7686fd2001-02-10 00:40:03 +11001156 status = -1;
1157 goto done;
Damien Miller33804262001-02-04 23:20:18 +11001158 }
1159
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001160 /* Override umask and utimes if asked */
1161 if (pflag)
Damien Miller3db5f532002-02-13 14:10:32 +11001162 do_fsetstat(conn, handle, handle_len, &a);
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +00001163
Damien Miller3db5f532002-02-13 14:10:32 +11001164 status = do_close(conn, handle, handle_len);
Damien Millerd7686fd2001-02-10 00:40:03 +11001165
1166done:
1167 xfree(handle);
1168 buffer_free(&msg);
Damien Miller3db5f532002-02-13 14:10:32 +11001169 return(status);
Damien Miller33804262001-02-04 23:20:18 +11001170}