sftp-client.c revision 259065
1105197Ssam/* $OpenBSD: sftp-client.c,v 1.108 2013/11/08 00:39:15 djm Exp $ */
2105197Ssam/*
3139823Simp * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4105197Ssam *
5105197Ssam * Permission to use, copy, modify, and distribute this software for any
6105197Ssam * purpose with or without fee is hereby granted, provided that the above
7105197Ssam * copyright notice and this permission notice appear in all copies.
8105197Ssam *
9105197Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10105197Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11105197Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12105197Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13105197Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14105197Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15105197Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16105197Ssam */
17105197Ssam
18105197Ssam/* XXX: memleaks */
19105197Ssam/* XXX: signed vs unsigned */
20105197Ssam/* XXX: remove all logging, only return status codes */
21105197Ssam/* XXX: copy between two remote sites */
22105197Ssam
23105197Ssam#include "includes.h"
24105197Ssam
25105197Ssam#include <sys/types.h>
26105197Ssam#include <sys/param.h>
27105197Ssam#ifdef HAVE_SYS_STATVFS_H
28105197Ssam#include <sys/statvfs.h>
29105197Ssam#endif
30105197Ssam#include "openbsd-compat/sys-queue.h"
31105197Ssam#ifdef HAVE_SYS_STAT_H
32105197Ssam# include <sys/stat.h>
33105197Ssam#endif
34105197Ssam#ifdef HAVE_SYS_TIME_H
35105197Ssam# include <sys/time.h>
36105197Ssam#endif
37105197Ssam#include <sys/uio.h>
38105197Ssam
39105197Ssam#include <dirent.h>
40105197Ssam#include <errno.h>
41105197Ssam#include <fcntl.h>
42105197Ssam#include <signal.h>
43105197Ssam#include <stdarg.h>
44159965Sthompsa#include <stdio.h>
45105197Ssam#include <string.h>
46105197Ssam#include <unistd.h>
47105197Ssam
48105197Ssam#include "xmalloc.h"
49105197Ssam#include "buffer.h"
50105197Ssam#include "log.h"
51105197Ssam#include "atomicio.h"
52105197Ssam#include "progressmeter.h"
53105197Ssam#include "misc.h"
54105197Ssam
55171497Sbz#include "sftp.h"
56105197Ssam#include "sftp-common.h"
57105197Ssam#include "sftp-client.h"
58185571Sbz
59105197Ssamextern volatile sig_atomic_t interrupted;
60105197Ssamextern int showprogress;
61105197Ssam
62105197Ssam/* Minimum amount of data to read at a time */
63105197Ssam#define MIN_READ_SIZE	512
64105197Ssam
65105197Ssam/* Maximum depth to descend in directory trees */
66105197Ssam#define MAX_DIR_DEPTH 64
67185571Sbz
68185571Sbzstruct sftp_conn {
69185571Sbz	int fd_in;
70105197Ssam	int fd_out;
71105197Ssam	u_int transfer_buflen;
72105197Ssam	u_int num_requests;
73105197Ssam	u_int version;
74105197Ssam	u_int msg_id;
75105197Ssam#define SFTP_EXT_POSIX_RENAME	0x00000001
76105197Ssam#define SFTP_EXT_STATVFS	0x00000002
77105197Ssam#define SFTP_EXT_FSTATVFS	0x00000004
78105197Ssam#define SFTP_EXT_HARDLINK	0x00000008
79105197Ssam	u_int exts;
80105197Ssam	u_int64_t limit_kbps;
81105197Ssam	struct bwlimit bwlimit_in, bwlimit_out;
82105197Ssam};
83105197Ssam
84105197Ssamstatic char *
85105197Ssamget_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
86105197Ssam    const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
87105197Ssam
88105197Ssam/* ARGSUSED */
89105197Ssamstatic int
90105197Ssamsftpio(void *_bwlimit, size_t amount)
91105197Ssam{
92105197Ssam	struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
93195699Srwatson
94253088Sae	bandwidth_limit(bwlimit, amount);
95253088Sae	return 0;
96105197Ssam}
97253088Sae
98253088Saestatic void
99253088Saesend_msg(struct sftp_conn *conn, Buffer *m)
100253088Sae{
101105197Ssam	u_char mlen[4];
102195699Srwatson	struct iovec iov[2];
103195699Srwatson
104253088Sae	if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
105253088Sae		fatal("Outbound message too long %u", buffer_len(m));
106253088Sae
107105197Ssam	/* Send length first */
108105197Ssam	put_u32(mlen, buffer_len(m));
109105197Ssam	iov[0].iov_base = mlen;
110105197Ssam	iov[0].iov_len = sizeof(mlen);
111105197Ssam	iov[1].iov_base = buffer_ptr(m);
112105197Ssam	iov[1].iov_len = buffer_len(m);
113105197Ssam
114105197Ssam	if (atomiciov6(writev, conn->fd_out, iov, 2,
115105197Ssam	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
116105197Ssam	    buffer_len(m) + sizeof(mlen))
117105197Ssam		fatal("Couldn't send packet: %s", strerror(errno));
118105197Ssam
119105197Ssam	buffer_clear(m);
120105197Ssam}
121105197Ssam
122181803Sbzstatic void
123120585Ssamget_msg(struct sftp_conn *conn, Buffer *m)
124252028Sae{
125105197Ssam	u_int msg_len;
126105197Ssam
127105197Ssam	buffer_append_space(m, 4);
128105197Ssam	if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
129105197Ssam	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
130105197Ssam		if (errno == EPIPE)
131105197Ssam			fatal("Connection closed");
132105197Ssam		else
133105197Ssam			fatal("Couldn't read packet: %s", strerror(errno));
134105197Ssam	}
135105197Ssam
136105197Ssam	msg_len = buffer_get_int(m);
137105197Ssam	if (msg_len > SFTP_MAX_MSG_LENGTH)
138105197Ssam		fatal("Received message too long %u", msg_len);
139157306Sbz
140105197Ssam	buffer_append_space(m, msg_len);
141105197Ssam	if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
142105197Ssam	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
143181803Sbz	    != msg_len) {
144120585Ssam		if (errno == EPIPE)
145252028Sae			fatal("Connection closed");
146105197Ssam		else
147105197Ssam			fatal("Read packet: %s", strerror(errno));
148105197Ssam	}
149105197Ssam}
150157306Sbz
151105197Ssamstatic void
152105197Ssamsend_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
153105197Ssam    u_int len)
154105197Ssam{
155105197Ssam	Buffer msg;
156105197Ssam
157105197Ssam	buffer_init(&msg);
158105197Ssam	buffer_put_char(&msg, code);
159105197Ssam	buffer_put_int(&msg, id);
160105197Ssam	buffer_put_string(&msg, s, len);
161105197Ssam	send_msg(conn, &msg);
162105197Ssam	debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
163105197Ssam	buffer_free(&msg);
164193947Sbz}
165105197Ssam
166193947Sbzstatic void
167105197Ssamsend_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
168105197Ssam    char *s, u_int len, Attrib *a)
169105197Ssam{
170105197Ssam	Buffer msg;
171105197Ssam
172105197Ssam	buffer_init(&msg);
173105197Ssam	buffer_put_char(&msg, code);
174105197Ssam	buffer_put_int(&msg, id);
175105197Ssam	buffer_put_string(&msg, s, len);
176105197Ssam	encode_attrib(&msg, a);
177105197Ssam	send_msg(conn, &msg);
178105197Ssam	debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
179105197Ssam	buffer_free(&msg);
180105197Ssam}
181252028Sae
182105197Ssamstatic u_int
183105197Ssamget_status(struct sftp_conn *conn, u_int expected_id)
184105197Ssam{
185105197Ssam	Buffer msg;
186105197Ssam	u_int type, id, status;
187105197Ssam
188105197Ssam	buffer_init(&msg);
189105197Ssam	get_msg(conn, &msg);
190105197Ssam	type = buffer_get_char(&msg);
191105197Ssam	id = buffer_get_int(&msg);
192105197Ssam
193105197Ssam	if (id != expected_id)
194105197Ssam		fatal("ID mismatch (%u != %u)", id, expected_id);
195105197Ssam	if (type != SSH2_FXP_STATUS)
196105197Ssam		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
197252028Sae		    SSH2_FXP_STATUS, type);
198105197Ssam
199105197Ssam	status = buffer_get_int(&msg);
200105197Ssam	buffer_free(&msg);
201105197Ssam
202105197Ssam	debug3("SSH2_FXP_STATUS %u", status);
203105197Ssam
204105197Ssam	return status;
205120585Ssam}
206252028Sae
207105197Ssamstatic char *
208105197Ssamget_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
209105197Ssam    const char *errfmt, ...)
210105197Ssam{
211105197Ssam	Buffer msg;
212105197Ssam	u_int type, id;
213105197Ssam	char *handle, errmsg[256];
214105197Ssam	va_list args;
215105197Ssam	int status;
216105197Ssam
217105197Ssam	va_start(args, errfmt);
218105197Ssam	if (errfmt != NULL)
219105197Ssam		vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
220105197Ssam	va_end(args);
221105197Ssam
222105197Ssam	buffer_init(&msg);
223105197Ssam	get_msg(conn, &msg);
224105197Ssam	type = buffer_get_char(&msg);
225105197Ssam	id = buffer_get_int(&msg);
226105197Ssam
227105197Ssam	if (id != expected_id)
228105197Ssam		fatal("%s: ID mismatch (%u != %u)",
229105197Ssam		    errfmt == NULL ? __func__ : errmsg, id, expected_id);
230105197Ssam	if (type == SSH2_FXP_STATUS) {
231105197Ssam		status = buffer_get_int(&msg);
232105197Ssam		if (errfmt != NULL)
233105197Ssam			error("%s: %s", errmsg, fx2txt(status));
234105197Ssam		buffer_free(&msg);
235105197Ssam		return(NULL);
236105197Ssam	} else if (type != SSH2_FXP_HANDLE)
237105197Ssam		fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
238105197Ssam		    errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
239105197Ssam
240105197Ssam	handle = buffer_get_string(&msg, len);
241105197Ssam	buffer_free(&msg);
242105197Ssam
243252028Sae	return(handle);
244105197Ssam}
245105197Ssam
246105197Ssamstatic Attrib *
247105197Ssamget_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
248105197Ssam{
249105197Ssam	Buffer msg;
250105197Ssam	u_int type, id;
251105197Ssam	Attrib *a;
252105197Ssam
253105197Ssam	buffer_init(&msg);
254105197Ssam	get_msg(conn, &msg);
255105197Ssam
256105197Ssam	type = buffer_get_char(&msg);
257105197Ssam	id = buffer_get_int(&msg);
258105197Ssam
259105197Ssam	debug3("Received stat reply T:%u I:%u", type, id);
260105197Ssam	if (id != expected_id)
261105197Ssam		fatal("ID mismatch (%u != %u)", id, expected_id);
262105197Ssam	if (type == SSH2_FXP_STATUS) {
263252028Sae		int status = buffer_get_int(&msg);
264105197Ssam
265105197Ssam		if (quiet)
266105197Ssam			debug("Couldn't stat remote file: %s", fx2txt(status));
267105197Ssam		else
268105197Ssam			error("Couldn't stat remote file: %s", fx2txt(status));
269105197Ssam		buffer_free(&msg);
270105197Ssam		return(NULL);
271105197Ssam	} else if (type != SSH2_FXP_ATTRS) {
272105197Ssam		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
273120585Ssam		    SSH2_FXP_ATTRS, type);
274252028Sae	}
275105197Ssam	a = decode_attrib(&msg);
276105197Ssam	buffer_free(&msg);
277105197Ssam
278105197Ssam	return(a);
279105197Ssam}
280105197Ssam
281105197Ssamstatic int
282105197Ssamget_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
283105197Ssam    u_int expected_id, int quiet)
284105197Ssam{
285105197Ssam	Buffer msg;
286105197Ssam	u_int type, id, flag;
287105197Ssam
288105197Ssam	buffer_init(&msg);
289105197Ssam	get_msg(conn, &msg);
290105197Ssam
291181803Sbz	type = buffer_get_char(&msg);
292105197Ssam	id = buffer_get_int(&msg);
293105197Ssam
294105197Ssam	debug3("Received statvfs reply T:%u I:%u", type, id);
295105197Ssam	if (id != expected_id)
296105197Ssam		fatal("ID mismatch (%u != %u)", id, expected_id);
297105197Ssam	if (type == SSH2_FXP_STATUS) {
298105197Ssam		int status = buffer_get_int(&msg);
299181803Sbz
300105197Ssam		if (quiet)
301105197Ssam			debug("Couldn't statvfs: %s", fx2txt(status));
302105197Ssam		else
303105197Ssam			error("Couldn't statvfs: %s", fx2txt(status));
304105197Ssam		buffer_free(&msg);
305105197Ssam		return -1;
306105197Ssam	} else if (type != SSH2_FXP_EXTENDED_REPLY) {
307105197Ssam		fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
308105197Ssam		    SSH2_FXP_EXTENDED_REPLY, type);
309105197Ssam	}
310105197Ssam
311181803Sbz	bzero(st, sizeof(*st));
312196481Srwatson	st->f_bsize = buffer_get_int64(&msg);
313181803Sbz	st->f_frsize = buffer_get_int64(&msg);
314128370Sluigi	st->f_blocks = buffer_get_int64(&msg);
315105197Ssam	st->f_bfree = buffer_get_int64(&msg);
316105197Ssam	st->f_bavail = buffer_get_int64(&msg);
317105197Ssam	st->f_files = buffer_get_int64(&msg);
318105197Ssam	st->f_ffree = buffer_get_int64(&msg);
319105197Ssam	st->f_favail = buffer_get_int64(&msg);
320105197Ssam	st->f_fsid = buffer_get_int64(&msg);
321105197Ssam	flag = buffer_get_int64(&msg);
322105197Ssam	st->f_namemax = buffer_get_int64(&msg);
323105197Ssam
324105197Ssam	st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
325252028Sae	st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
326105197Ssam
327196481Srwatson	buffer_free(&msg);
328105197Ssam
329105197Ssam	return 0;
330105197Ssam}
331105197Ssam
332105197Ssamstruct sftp_conn *
333105197Ssamdo_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
334105197Ssam    u_int64_t limit_kbps)
335105197Ssam{
336105197Ssam	u_int type;
337105197Ssam	Buffer msg;
338105197Ssam	struct sftp_conn *ret;
339105197Ssam
340105197Ssam	ret = xmalloc(sizeof(*ret));
341105197Ssam	ret->fd_in = fd_in;
342252028Sae	ret->fd_out = fd_out;
343105197Ssam	ret->transfer_buflen = transfer_buflen;
344196481Srwatson	ret->num_requests = num_requests;
345105197Ssam	ret->exts = 0;
346105197Ssam	ret->limit_kbps = 0;
347105197Ssam
348105197Ssam	buffer_init(&msg);
349105197Ssam	buffer_put_char(&msg, SSH2_FXP_INIT);
350105197Ssam	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
351105197Ssam	send_msg(ret, &msg);
352196481Srwatson
353105197Ssam	buffer_clear(&msg);
354105197Ssam
355105197Ssam	get_msg(ret, &msg);
356252028Sae
357105197Ssam	/* Expecting a VERSION reply */
358159965Sthompsa	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
359174054Sbz		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
360174054Sbz		    type);
361174054Sbz		buffer_free(&msg);
362174054Sbz		return(NULL);
363174054Sbz	}
364174054Sbz	ret->version = buffer_get_int(&msg);
365174054Sbz
366174054Sbz	debug2("Remote version: %u", ret->version);
367174054Sbz
368174054Sbz	/* Check for extensions */
369174054Sbz	while (buffer_len(&msg) > 0) {
370174054Sbz		char *name = buffer_get_string(&msg, NULL);
371174054Sbz		char *value = buffer_get_string(&msg, NULL);
372174054Sbz		int known = 0;
373159965Sthompsa
374174054Sbz		if (strcmp(name, "posix-rename@openssh.com") == 0 &&
375159965Sthompsa		    strcmp(value, "1") == 0) {
376159965Sthompsa			ret->exts |= SFTP_EXT_POSIX_RENAME;
377159965Sthompsa			known = 1;
378105197Ssam		} else if (strcmp(name, "statvfs@openssh.com") == 0 &&
379105197Ssam		    strcmp(value, "2") == 0) {
380105197Ssam			ret->exts |= SFTP_EXT_STATVFS;
381105197Ssam			known = 1;
382105197Ssam		} else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
383105197Ssam		    strcmp(value, "2") == 0) {
384105197Ssam			ret->exts |= SFTP_EXT_FSTATVFS;
385105197Ssam			known = 1;
386105197Ssam		} else if (strcmp(name, "hardlink@openssh.com") == 0 &&
387105197Ssam		    strcmp(value, "1") == 0) {
388105197Ssam			ret->exts |= SFTP_EXT_HARDLINK;
389105197Ssam			known = 1;
390105197Ssam		}
391105197Ssam		if (known) {
392105197Ssam			debug2("Server supports extension \"%s\" revision %s",
393105197Ssam			    name, value);
394105197Ssam		} else {
395105197Ssam			debug2("Unrecognised server extension \"%s\"", name);
396105197Ssam		}
397105197Ssam		free(name);
398120585Ssam		free(value);
399105197Ssam	}
400105197Ssam
401134391Sandre	buffer_free(&msg);
402252028Sae
403120585Ssam	/* Some filexfer v.0 servers don't support large packets */
404120585Ssam	if (ret->version == 0)
405105197Ssam		ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
406105197Ssam
407105197Ssam	ret->limit_kbps = limit_kbps;
408105197Ssam	if (ret->limit_kbps > 0) {
409105197Ssam		bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
410105197Ssam		    ret->transfer_buflen);
411105197Ssam		bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
412105197Ssam		    ret->transfer_buflen);
413105197Ssam	}
414105197Ssam
415105197Ssam	return ret;
416105197Ssam}
417105197Ssam
418105197Ssamu_int
419105197Ssamsftp_proto_version(struct sftp_conn *conn)
420105197Ssam{
421221129Sbz	return conn->version;
422221129Sbz}
423221129Sbz
424105197Ssamint
425105197Ssamdo_close(struct sftp_conn *conn, char *handle, u_int handle_len)
426105197Ssam{
427105197Ssam	u_int id, status;
428105197Ssam	Buffer msg;
429105197Ssam
430105197Ssam	buffer_init(&msg);
431105197Ssam
432120585Ssam	id = conn->msg_id++;
433120585Ssam	buffer_put_char(&msg, SSH2_FXP_CLOSE);
434105197Ssam	buffer_put_int(&msg, id);
435105197Ssam	buffer_put_string(&msg, handle, handle_len);
436105197Ssam	send_msg(conn, &msg);
437105197Ssam	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
438105197Ssam
439105197Ssam	status = get_status(conn, id);
440105197Ssam	if (status != SSH2_FX_OK)
441105197Ssam		error("Couldn't close file: %s", fx2txt(status));
442105197Ssam
443105197Ssam	buffer_free(&msg);
444105197Ssam
445105197Ssam	return status;
446105197Ssam}
447120585Ssam
448120585Ssam
449105197Ssamstatic int
450105197Ssamdo_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
451252028Sae    SFTP_DIRENT ***dir)
452105197Ssam{
453105197Ssam	Buffer msg;
454105197Ssam	u_int count, type, id, handle_len, i, expected_id, ents = 0;
455105197Ssam	char *handle;
456243882Sglebius
457105197Ssam	id = conn->msg_id++;
458120585Ssam
459252028Sae	buffer_init(&msg);
460105197Ssam	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
461105197Ssam	buffer_put_int(&msg, id);
462105197Ssam	buffer_put_cstring(&msg, path);
463105197Ssam	send_msg(conn, &msg);
464105197Ssam
465105197Ssam	handle = get_handle(conn, id, &handle_len,
466105197Ssam	    "remote readdir(\"%s\")", path);
467105197Ssam	if (handle == NULL) {
468105197Ssam		buffer_free(&msg);
469181803Sbz		return -1;
470105197Ssam	}
471105197Ssam
472105197Ssam	if (dir) {
473105197Ssam		ents = 0;
474133720Sdwmalone		*dir = xcalloc(1, sizeof(**dir));
475105197Ssam		(*dir)[0] = NULL;
476105197Ssam	}
477221129Sbz
478221129Sbz	for (; !interrupted;) {
479105197Ssam		id = expected_id = conn->msg_id++;
480105197Ssam
481105197Ssam		debug3("Sending SSH2_FXP_READDIR I:%u", id);
482105197Ssam
483105197Ssam		buffer_clear(&msg);
484105197Ssam		buffer_put_char(&msg, SSH2_FXP_READDIR);
485105197Ssam		buffer_put_int(&msg, id);
486105197Ssam		buffer_put_string(&msg, handle, handle_len);
487105197Ssam		send_msg(conn, &msg);
488105197Ssam
489105197Ssam		buffer_clear(&msg);
490105197Ssam
491105197Ssam		get_msg(conn, &msg);
492105197Ssam
493105197Ssam		type = buffer_get_char(&msg);
494105197Ssam		id = buffer_get_int(&msg);
495105197Ssam
496221129Sbz		debug3("Received reply T:%u I:%u", type, id);
497105197Ssam
498221129Sbz		if (id != expected_id)
499221129Sbz			fatal("ID mismatch (%u != %u)", id, expected_id);
500105197Ssam
501105197Ssam		if (type == SSH2_FXP_STATUS) {
502105197Ssam			int status = buffer_get_int(&msg);
503105197Ssam
504105197Ssam			debug3("Received SSH2_FXP_STATUS %d", status);
505105197Ssam
506105197Ssam			if (status == SSH2_FX_EOF) {
507105197Ssam				break;
508105197Ssam			} else {
509221129Sbz				error("Couldn't read directory: %s",
510105197Ssam				    fx2txt(status));
511105197Ssam				do_close(conn, handle, handle_len);
512221129Sbz				free(handle);
513105197Ssam				buffer_free(&msg);
514105197Ssam				return(status);
515105197Ssam			}
516105197Ssam		} else if (type != SSH2_FXP_NAME)
517105197Ssam			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
518105197Ssam			    SSH2_FXP_NAME, type);
519105197Ssam
520105197Ssam		count = buffer_get_int(&msg);
521105197Ssam		if (count == 0)
522105197Ssam			break;
523105197Ssam		debug3("Received %d SSH2_FXP_NAME responses", count);
524105197Ssam		for (i = 0; i < count; i++) {
525105197Ssam			char *filename, *longname;
526105197Ssam			Attrib *a;
527120585Ssam
528120585Ssam			filename = buffer_get_string(&msg, NULL);
529105197Ssam			longname = buffer_get_string(&msg, NULL);
530105197Ssam			a = decode_attrib(&msg);
531252028Sae
532105197Ssam			if (printflag)
533105197Ssam				printf("%s\n", longname);
534105197Ssam
535105197Ssam			/*
536105197Ssam			 * Directory entries should never contain '/'
537105197Ssam			 * These can be used to attack recursive ops
538105197Ssam			 * (e.g. send '../../../../etc/passwd')
539105197Ssam			 */
540105197Ssam			if (strchr(filename, '/') != NULL) {
541105197Ssam				error("Server sent suspect path \"%s\" "
542105197Ssam				    "during readdir of \"%s\"", filename, path);
543243882Sglebius				goto next;
544105197Ssam			}
545120585Ssam
546252028Sae			if (dir) {
547105197Ssam				*dir = xrealloc(*dir, ents + 2, sizeof(**dir));
548105197Ssam				(*dir)[ents] = xcalloc(1, sizeof(***dir));
549105197Ssam				(*dir)[ents]->filename = xstrdup(filename);
550105197Ssam				(*dir)[ents]->longname = xstrdup(longname);
551105197Ssam				memcpy(&(*dir)[ents]->a, a, sizeof(*a));
552105197Ssam				(*dir)[++ents] = NULL;
553105197Ssam			}
554105197Ssam next:
555105197Ssam			free(filename);
556105197Ssam			free(longname);
557181803Sbz		}
558105197Ssam	}
559105197Ssam
560105197Ssam	buffer_free(&msg);
561221129Sbz	do_close(conn, handle, handle_len);
562105197Ssam	free(handle);
563221129Sbz
564105197Ssam	/* Don't return partial matches on interrupt */
565105197Ssam	if (interrupted && dir != NULL && *dir != NULL) {
566105197Ssam		free_sftp_dirents(*dir);
567105197Ssam		*dir = xcalloc(1, sizeof(**dir));
568105197Ssam		**dir = NULL;
569105197Ssam	}
570105197Ssam
571221129Sbz	return 0;
572105197Ssam}
573221129Sbz
574221129Sbzint
575221129Sbzdo_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
576105197Ssam{
577221129Sbz	return(do_lsreaddir(conn, path, 0, dir));
578221129Sbz}
579221129Sbz
580221129Sbzvoid free_sftp_dirents(SFTP_DIRENT **s)
581221129Sbz{
582105197Ssam	int i;
583221129Sbz
584240630Skevlo	for (i = 0; s[i]; i++) {
585221129Sbz		free(s[i]->filename);
586221129Sbz		free(s[i]->longname);
587221129Sbz		free(s[i]);
588221129Sbz	}
589105197Ssam	free(s);
590105197Ssam}
591105197Ssam
592105197Ssamint
593105197Ssamdo_rm(struct sftp_conn *conn, char *path)
594105197Ssam{
595105197Ssam	u_int status, id;
596105197Ssam
597105197Ssam	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
598120585Ssam
599105197Ssam	id = conn->msg_id++;
600252028Sae	send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
601105197Ssam	status = get_status(conn, id);
602105197Ssam	if (status != SSH2_FX_OK)
603105197Ssam		error("Couldn't delete file: %s", fx2txt(status));
604105197Ssam	return(status);
605252028Sae}
606105197Ssam
607105197Ssamint
608105197Ssamdo_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
609105197Ssam{
610105197Ssam	u_int status, id;
611105197Ssam
612105197Ssam	id = conn->msg_id++;
613105197Ssam	send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
614105197Ssam	    strlen(path), a);
615252028Sae
616252028Sae	status = get_status(conn, id);
617105197Ssam	if (status != SSH2_FX_OK && printflag)
618105197Ssam		error("Couldn't create directory: %s", fx2txt(status));
619105197Ssam
620105197Ssam	return(status);
621105197Ssam}
622105197Ssam
623105197Ssamint
624105197Ssamdo_rmdir(struct sftp_conn *conn, char *path)
625105197Ssam{
626105197Ssam	u_int status, id;
627252028Sae
628252028Sae	id = conn->msg_id++;
629105197Ssam	send_string_request(conn, id, SSH2_FXP_RMDIR, path,
630105197Ssam	    strlen(path));
631105197Ssam
632105197Ssam	status = get_status(conn, id);
633105197Ssam	if (status != SSH2_FX_OK)
634105197Ssam		error("Couldn't remove directory: %s", fx2txt(status));
635124765Ssam
636124765Ssam	return(status);
637105197Ssam}
638105197Ssam
639105197SsamAttrib *
640171167Sgnndo_stat(struct sftp_conn *conn, char *path, int quiet)
641221129Sbz{
642105197Ssam	u_int id;
643105197Ssam
644105197Ssam	id = conn->msg_id++;
645105197Ssam
646105197Ssam	send_string_request(conn, id,
647105197Ssam	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
648105197Ssam	    path, strlen(path));
649105197Ssam
650105197Ssam	return(get_decode_stat(conn, id, quiet));
651105197Ssam}
652105197Ssam
653105197SsamAttrib *
654105197Ssamdo_lstat(struct sftp_conn *conn, char *path, int quiet)
655105197Ssam{
656105197Ssam	u_int id;
657105197Ssam
658105197Ssam	if (conn->version == 0) {
659105197Ssam		if (quiet)
660120585Ssam			debug("Server version does not support lstat operation");
661105197Ssam		else
662105197Ssam			logit("Server version does not support lstat operation");
663105197Ssam		return(do_stat(conn, path, quiet));
664105197Ssam	}
665105197Ssam
666105197Ssam	id = conn->msg_id++;
667105197Ssam	send_string_request(conn, id, SSH2_FXP_LSTAT, path,
668105197Ssam	    strlen(path));
669105197Ssam
670105197Ssam	return(get_decode_stat(conn, id, quiet));
671105197Ssam}
672221129Sbz
673221129Sbz#ifdef notyet
674186791SbzAttrib *
675186791Sbzdo_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
676186791Sbz{
677186791Sbz	u_int id;
678186791Sbz
679186791Sbz	id = conn->msg_id++;
680186791Sbz	send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
681186791Sbz	    handle_len);
682157306Sbz
683221129Sbz	return(get_decode_stat(conn, id, quiet));
684221129Sbz}
685186791Sbz#endif
686186791Sbz
687186791Sbzint
688186791Sbzdo_setstat(struct sftp_conn *conn, char *path, Attrib *a)
689186791Sbz{
690186791Sbz	u_int status, id;
691186791Sbz
692186791Sbz	id = conn->msg_id++;
693157306Sbz	send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
694221129Sbz	    strlen(path), a);
695105197Ssam
696230442Sbz	status = get_status(conn, id);
697105197Ssam	if (status != SSH2_FX_OK)
698105197Ssam		error("Couldn't setstat on \"%s\": %s", path,
699105197Ssam		    fx2txt(status));
700105197Ssam
701105197Ssam	return(status);
702105197Ssam}
703105197Ssam
704105197Ssamint
705105197Ssamdo_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
706105197Ssam    Attrib *a)
707105197Ssam{
708105197Ssam	u_int status, id;
709105197Ssam
710105197Ssam	id = conn->msg_id++;
711221129Sbz	send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
712105197Ssam	    handle_len, a);
713105197Ssam
714105197Ssam	status = get_status(conn, id);
715105197Ssam	if (status != SSH2_FX_OK)
716185088Szec		error("Couldn't fsetstat: %s", fx2txt(status));
717105197Ssam
718105197Ssam	return(status);
719105197Ssam}
720221129Sbz
721105197Ssamchar *
722157306Sbzdo_realpath(struct sftp_conn *conn, char *path)
723221129Sbz{
724221129Sbz	Buffer msg;
725105197Ssam	u_int type, expected_id, count, id;
726157306Sbz	char *filename, *longname;
727105197Ssam	Attrib *a;
728105197Ssam
729105197Ssam	expected_id = id = conn->msg_id++;
730171167Sgnn	send_string_request(conn, id, SSH2_FXP_REALPATH, path,
731	    strlen(path));
732
733	buffer_init(&msg);
734
735	get_msg(conn, &msg);
736	type = buffer_get_char(&msg);
737	id = buffer_get_int(&msg);
738
739	if (id != expected_id)
740		fatal("ID mismatch (%u != %u)", id, expected_id);
741
742	if (type == SSH2_FXP_STATUS) {
743		u_int status = buffer_get_int(&msg);
744
745		error("Couldn't canonicalise: %s", fx2txt(status));
746		buffer_free(&msg);
747		return NULL;
748	} else if (type != SSH2_FXP_NAME)
749		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
750		    SSH2_FXP_NAME, type);
751
752	count = buffer_get_int(&msg);
753	if (count != 1)
754		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
755
756	filename = buffer_get_string(&msg, NULL);
757	longname = buffer_get_string(&msg, NULL);
758	a = decode_attrib(&msg);
759
760	debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
761	    (unsigned long)a->size);
762
763	free(longname);
764
765	buffer_free(&msg);
766
767	return(filename);
768}
769
770int
771do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
772{
773	Buffer msg;
774	u_int status, id;
775
776	buffer_init(&msg);
777
778	/* Send rename request */
779	id = conn->msg_id++;
780	if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
781		buffer_put_char(&msg, SSH2_FXP_EXTENDED);
782		buffer_put_int(&msg, id);
783		buffer_put_cstring(&msg, "posix-rename@openssh.com");
784	} else {
785		buffer_put_char(&msg, SSH2_FXP_RENAME);
786		buffer_put_int(&msg, id);
787	}
788	buffer_put_cstring(&msg, oldpath);
789	buffer_put_cstring(&msg, newpath);
790	send_msg(conn, &msg);
791	debug3("Sent message %s \"%s\" -> \"%s\"",
792	    (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
793	    "SSH2_FXP_RENAME", oldpath, newpath);
794	buffer_free(&msg);
795
796	status = get_status(conn, id);
797	if (status != SSH2_FX_OK)
798		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
799		    newpath, fx2txt(status));
800
801	return(status);
802}
803
804int
805do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
806{
807	Buffer msg;
808	u_int status, id;
809
810	if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
811		error("Server does not support hardlink@openssh.com extension");
812		return -1;
813	}
814
815	buffer_init(&msg);
816
817	/* Send link request */
818	id = conn->msg_id++;
819	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
820	buffer_put_int(&msg, id);
821	buffer_put_cstring(&msg, "hardlink@openssh.com");
822	buffer_put_cstring(&msg, oldpath);
823	buffer_put_cstring(&msg, newpath);
824	send_msg(conn, &msg);
825	debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
826	       oldpath, newpath);
827	buffer_free(&msg);
828
829	status = get_status(conn, id);
830	if (status != SSH2_FX_OK)
831		error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
832		    newpath, fx2txt(status));
833
834	return(status);
835}
836
837int
838do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
839{
840	Buffer msg;
841	u_int status, id;
842
843	if (conn->version < 3) {
844		error("This server does not support the symlink operation");
845		return(SSH2_FX_OP_UNSUPPORTED);
846	}
847
848	buffer_init(&msg);
849
850	/* Send symlink request */
851	id = conn->msg_id++;
852	buffer_put_char(&msg, SSH2_FXP_SYMLINK);
853	buffer_put_int(&msg, id);
854	buffer_put_cstring(&msg, oldpath);
855	buffer_put_cstring(&msg, newpath);
856	send_msg(conn, &msg);
857	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
858	    newpath);
859	buffer_free(&msg);
860
861	status = get_status(conn, id);
862	if (status != SSH2_FX_OK)
863		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
864		    newpath, fx2txt(status));
865
866	return(status);
867}
868
869#ifdef notyet
870char *
871do_readlink(struct sftp_conn *conn, char *path)
872{
873	Buffer msg;
874	u_int type, expected_id, count, id;
875	char *filename, *longname;
876	Attrib *a;
877
878	expected_id = id = conn->msg_id++;
879	send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
880
881	buffer_init(&msg);
882
883	get_msg(conn, &msg);
884	type = buffer_get_char(&msg);
885	id = buffer_get_int(&msg);
886
887	if (id != expected_id)
888		fatal("ID mismatch (%u != %u)", id, expected_id);
889
890	if (type == SSH2_FXP_STATUS) {
891		u_int status = buffer_get_int(&msg);
892
893		error("Couldn't readlink: %s", fx2txt(status));
894		buffer_free(&msg);
895		return(NULL);
896	} else if (type != SSH2_FXP_NAME)
897		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
898		    SSH2_FXP_NAME, type);
899
900	count = buffer_get_int(&msg);
901	if (count != 1)
902		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
903
904	filename = buffer_get_string(&msg, NULL);
905	longname = buffer_get_string(&msg, NULL);
906	a = decode_attrib(&msg);
907
908	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
909
910	free(longname);
911
912	buffer_free(&msg);
913
914	return(filename);
915}
916#endif
917
918int
919do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
920    int quiet)
921{
922	Buffer msg;
923	u_int id;
924
925	if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
926		error("Server does not support statvfs@openssh.com extension");
927		return -1;
928	}
929
930	id = conn->msg_id++;
931
932	buffer_init(&msg);
933	buffer_clear(&msg);
934	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
935	buffer_put_int(&msg, id);
936	buffer_put_cstring(&msg, "statvfs@openssh.com");
937	buffer_put_cstring(&msg, path);
938	send_msg(conn, &msg);
939	buffer_free(&msg);
940
941	return get_decode_statvfs(conn, st, id, quiet);
942}
943
944#ifdef notyet
945int
946do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
947    struct sftp_statvfs *st, int quiet)
948{
949	Buffer msg;
950	u_int id;
951
952	if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
953		error("Server does not support fstatvfs@openssh.com extension");
954		return -1;
955	}
956
957	id = conn->msg_id++;
958
959	buffer_init(&msg);
960	buffer_clear(&msg);
961	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
962	buffer_put_int(&msg, id);
963	buffer_put_cstring(&msg, "fstatvfs@openssh.com");
964	buffer_put_string(&msg, handle, handle_len);
965	send_msg(conn, &msg);
966	buffer_free(&msg);
967
968	return get_decode_statvfs(conn, st, id, quiet);
969}
970#endif
971
972static void
973send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
974    u_int len, char *handle, u_int handle_len)
975{
976	Buffer msg;
977
978	buffer_init(&msg);
979	buffer_clear(&msg);
980	buffer_put_char(&msg, SSH2_FXP_READ);
981	buffer_put_int(&msg, id);
982	buffer_put_string(&msg, handle, handle_len);
983	buffer_put_int64(&msg, offset);
984	buffer_put_int(&msg, len);
985	send_msg(conn, &msg);
986	buffer_free(&msg);
987}
988
989int
990do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
991    Attrib *a, int pflag, int resume)
992{
993	Attrib junk;
994	Buffer msg;
995	char *handle;
996	int local_fd = -1, status = 0, write_error;
997	int read_error, write_errno, reordered = 0;
998	u_int64_t offset = 0, size, highwater;
999	u_int handle_len, mode, type, id, buflen, num_req, max_req;
1000	off_t progress_counter;
1001	struct stat st;
1002	struct request {
1003		u_int id;
1004		u_int len;
1005		u_int64_t offset;
1006		TAILQ_ENTRY(request) tq;
1007	};
1008	TAILQ_HEAD(reqhead, request) requests;
1009	struct request *req;
1010
1011	TAILQ_INIT(&requests);
1012
1013	if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1014		return -1;
1015
1016	/* Do not preserve set[ug]id here, as we do not preserve ownership */
1017	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1018		mode = a->perm & 0777;
1019	else
1020		mode = 0666;
1021
1022	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1023	    (!S_ISREG(a->perm))) {
1024		error("Cannot download non-regular file: %s", remote_path);
1025		return(-1);
1026	}
1027
1028	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1029		size = a->size;
1030	else
1031		size = 0;
1032
1033	buflen = conn->transfer_buflen;
1034	buffer_init(&msg);
1035
1036	/* Send open request */
1037	id = conn->msg_id++;
1038	buffer_put_char(&msg, SSH2_FXP_OPEN);
1039	buffer_put_int(&msg, id);
1040	buffer_put_cstring(&msg, remote_path);
1041	buffer_put_int(&msg, SSH2_FXF_READ);
1042	attrib_clear(&junk); /* Send empty attributes */
1043	encode_attrib(&msg, &junk);
1044	send_msg(conn, &msg);
1045	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1046
1047	handle = get_handle(conn, id, &handle_len,
1048	    "remote open(\"%s\")", remote_path);
1049	if (handle == NULL) {
1050		buffer_free(&msg);
1051		return(-1);
1052	}
1053
1054	local_fd = open(local_path, O_WRONLY | O_CREAT | (resume ? 0 : O_TRUNC),
1055	    mode | S_IWUSR);
1056	if (local_fd == -1) {
1057		error("Couldn't open local file \"%s\" for writing: %s",
1058		    local_path, strerror(errno));
1059		goto fail;
1060	}
1061	offset = highwater = 0;
1062	if (resume) {
1063		if (fstat(local_fd, &st) == -1) {
1064			error("Unable to stat local file \"%s\": %s",
1065			    local_path, strerror(errno));
1066			goto fail;
1067		}
1068		if ((size_t)st.st_size > size) {
1069			error("Unable to resume download of \"%s\": "
1070			    "local file is larger than remote", local_path);
1071 fail:
1072			do_close(conn, handle, handle_len);
1073			buffer_free(&msg);
1074			free(handle);
1075			return -1;
1076		}
1077		offset = highwater = st.st_size;
1078	}
1079
1080	/* Read from remote and write to local */
1081	write_error = read_error = write_errno = num_req = 0;
1082	max_req = 1;
1083	progress_counter = offset;
1084
1085	if (showprogress && size != 0)
1086		start_progress_meter(remote_path, size, &progress_counter);
1087
1088	while (num_req > 0 || max_req > 0) {
1089		char *data;
1090		u_int len;
1091
1092		/*
1093		 * Simulate EOF on interrupt: stop sending new requests and
1094		 * allow outstanding requests to drain gracefully
1095		 */
1096		if (interrupted) {
1097			if (num_req == 0) /* If we haven't started yet... */
1098				break;
1099			max_req = 0;
1100		}
1101
1102		/* Send some more requests */
1103		while (num_req < max_req) {
1104			debug3("Request range %llu -> %llu (%d/%d)",
1105			    (unsigned long long)offset,
1106			    (unsigned long long)offset + buflen - 1,
1107			    num_req, max_req);
1108			req = xcalloc(1, sizeof(*req));
1109			req->id = conn->msg_id++;
1110			req->len = buflen;
1111			req->offset = offset;
1112			offset += buflen;
1113			num_req++;
1114			TAILQ_INSERT_TAIL(&requests, req, tq);
1115			send_read_request(conn, req->id, req->offset,
1116			    req->len, handle, handle_len);
1117		}
1118
1119		buffer_clear(&msg);
1120		get_msg(conn, &msg);
1121		type = buffer_get_char(&msg);
1122		id = buffer_get_int(&msg);
1123		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1124
1125		/* Find the request in our queue */
1126		for (req = TAILQ_FIRST(&requests);
1127		    req != NULL && req->id != id;
1128		    req = TAILQ_NEXT(req, tq))
1129			;
1130		if (req == NULL)
1131			fatal("Unexpected reply %u", id);
1132
1133		switch (type) {
1134		case SSH2_FXP_STATUS:
1135			status = buffer_get_int(&msg);
1136			if (status != SSH2_FX_EOF)
1137				read_error = 1;
1138			max_req = 0;
1139			TAILQ_REMOVE(&requests, req, tq);
1140			free(req);
1141			num_req--;
1142			break;
1143		case SSH2_FXP_DATA:
1144			data = buffer_get_string(&msg, &len);
1145			debug3("Received data %llu -> %llu",
1146			    (unsigned long long)req->offset,
1147			    (unsigned long long)req->offset + len - 1);
1148			if (len > req->len)
1149				fatal("Received more data than asked for "
1150				    "%u > %u", len, req->len);
1151			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1152			    atomicio(vwrite, local_fd, data, len) != len) &&
1153			    !write_error) {
1154				write_errno = errno;
1155				write_error = 1;
1156				max_req = 0;
1157			}
1158			else if (!reordered && req->offset <= highwater)
1159				highwater = req->offset + len;
1160			else if (!reordered && req->offset > highwater)
1161				reordered = 1;
1162			progress_counter += len;
1163			free(data);
1164
1165			if (len == req->len) {
1166				TAILQ_REMOVE(&requests, req, tq);
1167				free(req);
1168				num_req--;
1169			} else {
1170				/* Resend the request for the missing data */
1171				debug3("Short data block, re-requesting "
1172				    "%llu -> %llu (%2d)",
1173				    (unsigned long long)req->offset + len,
1174				    (unsigned long long)req->offset +
1175				    req->len - 1, num_req);
1176				req->id = conn->msg_id++;
1177				req->len -= len;
1178				req->offset += len;
1179				send_read_request(conn, req->id,
1180				    req->offset, req->len, handle, handle_len);
1181				/* Reduce the request size */
1182				if (len < buflen)
1183					buflen = MAX(MIN_READ_SIZE, len);
1184			}
1185			if (max_req > 0) { /* max_req = 0 iff EOF received */
1186				if (size > 0 && offset > size) {
1187					/* Only one request at a time
1188					 * after the expected EOF */
1189					debug3("Finish at %llu (%2d)",
1190					    (unsigned long long)offset,
1191					    num_req);
1192					max_req = 1;
1193				} else if (max_req <= conn->num_requests) {
1194					++max_req;
1195				}
1196			}
1197			break;
1198		default:
1199			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1200			    SSH2_FXP_DATA, type);
1201		}
1202	}
1203
1204	if (showprogress && size)
1205		stop_progress_meter();
1206
1207	/* Sanity check */
1208	if (TAILQ_FIRST(&requests) != NULL)
1209		fatal("Transfer complete, but requests still in queue");
1210	/* Truncate at highest contiguous point to avoid holes on interrupt */
1211	if (read_error || write_error || interrupted) {
1212		if (reordered && resume) {
1213			error("Unable to resume download of \"%s\": "
1214			    "server reordered requests", local_path);
1215		}
1216		debug("truncating at %llu", (unsigned long long)highwater);
1217		ftruncate(local_fd, highwater);
1218	}
1219	if (read_error) {
1220		error("Couldn't read from remote file \"%s\" : %s",
1221		    remote_path, fx2txt(status));
1222		do_close(conn, handle, handle_len);
1223	} else if (write_error) {
1224		error("Couldn't write to \"%s\": %s", local_path,
1225		    strerror(write_errno));
1226		status = -1;
1227		do_close(conn, handle, handle_len);
1228	} else {
1229		status = do_close(conn, handle, handle_len);
1230		if (interrupted)
1231			status = -1;
1232		/* Override umask and utimes if asked */
1233#ifdef HAVE_FCHMOD
1234		if (pflag && fchmod(local_fd, mode) == -1)
1235#else
1236		if (pflag && chmod(local_path, mode) == -1)
1237#endif /* HAVE_FCHMOD */
1238			error("Couldn't set mode on \"%s\": %s", local_path,
1239			    strerror(errno));
1240		if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1241			struct timeval tv[2];
1242			tv[0].tv_sec = a->atime;
1243			tv[1].tv_sec = a->mtime;
1244			tv[0].tv_usec = tv[1].tv_usec = 0;
1245			if (utimes(local_path, tv) == -1)
1246				error("Can't set times on \"%s\": %s",
1247				    local_path, strerror(errno));
1248		}
1249	}
1250	close(local_fd);
1251	buffer_free(&msg);
1252	free(handle);
1253
1254	return(status);
1255}
1256
1257static int
1258download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1259    Attrib *dirattrib, int pflag, int printflag, int depth, int resume)
1260{
1261	int i, ret = 0;
1262	SFTP_DIRENT **dir_entries;
1263	char *filename, *new_src, *new_dst;
1264	mode_t mode = 0777;
1265
1266	if (depth >= MAX_DIR_DEPTH) {
1267		error("Maximum directory depth exceeded: %d levels", depth);
1268		return -1;
1269	}
1270
1271	if (dirattrib == NULL &&
1272	    (dirattrib = do_stat(conn, src, 1)) == NULL) {
1273		error("Unable to stat remote directory \"%s\"", src);
1274		return -1;
1275	}
1276	if (!S_ISDIR(dirattrib->perm)) {
1277		error("\"%s\" is not a directory", src);
1278		return -1;
1279	}
1280	if (printflag)
1281		printf("Retrieving %s\n", src);
1282
1283	if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1284		mode = dirattrib->perm & 01777;
1285	else {
1286		debug("Server did not send permissions for "
1287		    "directory \"%s\"", dst);
1288	}
1289
1290	if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1291		error("mkdir %s: %s", dst, strerror(errno));
1292		return -1;
1293	}
1294
1295	if (do_readdir(conn, src, &dir_entries) == -1) {
1296		error("%s: Failed to get directory contents", src);
1297		return -1;
1298	}
1299
1300	for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1301		filename = dir_entries[i]->filename;
1302
1303		new_dst = path_append(dst, filename);
1304		new_src = path_append(src, filename);
1305
1306		if (S_ISDIR(dir_entries[i]->a.perm)) {
1307			if (strcmp(filename, ".") == 0 ||
1308			    strcmp(filename, "..") == 0)
1309				continue;
1310			if (download_dir_internal(conn, new_src, new_dst,
1311			    &(dir_entries[i]->a), pflag, printflag,
1312			    depth + 1, resume) == -1)
1313				ret = -1;
1314		} else if (S_ISREG(dir_entries[i]->a.perm) ) {
1315			if (do_download(conn, new_src, new_dst,
1316			    &(dir_entries[i]->a), pflag, resume) == -1) {
1317				error("Download of file %s to %s failed",
1318				    new_src, new_dst);
1319				ret = -1;
1320			}
1321		} else
1322			logit("%s: not a regular file\n", new_src);
1323
1324		free(new_dst);
1325		free(new_src);
1326	}
1327
1328	if (pflag) {
1329		if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1330			struct timeval tv[2];
1331			tv[0].tv_sec = dirattrib->atime;
1332			tv[1].tv_sec = dirattrib->mtime;
1333			tv[0].tv_usec = tv[1].tv_usec = 0;
1334			if (utimes(dst, tv) == -1)
1335				error("Can't set times on \"%s\": %s",
1336				    dst, strerror(errno));
1337		} else
1338			debug("Server did not send times for directory "
1339			    "\"%s\"", dst);
1340	}
1341
1342	free_sftp_dirents(dir_entries);
1343
1344	return ret;
1345}
1346
1347int
1348download_dir(struct sftp_conn *conn, char *src, char *dst,
1349    Attrib *dirattrib, int pflag, int printflag, int resume)
1350{
1351	char *src_canon;
1352	int ret;
1353
1354	if ((src_canon = do_realpath(conn, src)) == NULL) {
1355		error("Unable to canonicalise path \"%s\"", src);
1356		return -1;
1357	}
1358
1359	ret = download_dir_internal(conn, src_canon, dst,
1360	    dirattrib, pflag, printflag, 0, resume);
1361	free(src_canon);
1362	return ret;
1363}
1364
1365int
1366do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1367    int pflag)
1368{
1369	int local_fd;
1370	int status = SSH2_FX_OK;
1371	u_int handle_len, id, type;
1372	off_t offset, progress_counter;
1373	char *handle, *data;
1374	Buffer msg;
1375	struct stat sb;
1376	Attrib a;
1377	u_int32_t startid;
1378	u_int32_t ackid;
1379	struct outstanding_ack {
1380		u_int id;
1381		u_int len;
1382		off_t offset;
1383		TAILQ_ENTRY(outstanding_ack) tq;
1384	};
1385	TAILQ_HEAD(ackhead, outstanding_ack) acks;
1386	struct outstanding_ack *ack = NULL;
1387
1388	TAILQ_INIT(&acks);
1389
1390	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1391		error("Couldn't open local file \"%s\" for reading: %s",
1392		    local_path, strerror(errno));
1393		return(-1);
1394	}
1395	if (fstat(local_fd, &sb) == -1) {
1396		error("Couldn't fstat local file \"%s\": %s",
1397		    local_path, strerror(errno));
1398		close(local_fd);
1399		return(-1);
1400	}
1401	if (!S_ISREG(sb.st_mode)) {
1402		error("%s is not a regular file", local_path);
1403		close(local_fd);
1404		return(-1);
1405	}
1406	stat_to_attrib(&sb, &a);
1407
1408	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1409	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1410	a.perm &= 0777;
1411	if (!pflag)
1412		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1413
1414	buffer_init(&msg);
1415
1416	/* Send open request */
1417	id = conn->msg_id++;
1418	buffer_put_char(&msg, SSH2_FXP_OPEN);
1419	buffer_put_int(&msg, id);
1420	buffer_put_cstring(&msg, remote_path);
1421	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1422	encode_attrib(&msg, &a);
1423	send_msg(conn, &msg);
1424	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1425
1426	buffer_clear(&msg);
1427
1428	handle = get_handle(conn, id, &handle_len,
1429	    "remote open(\"%s\")", remote_path);
1430	if (handle == NULL) {
1431		close(local_fd);
1432		buffer_free(&msg);
1433		return -1;
1434	}
1435
1436	startid = ackid = id + 1;
1437	data = xmalloc(conn->transfer_buflen);
1438
1439	/* Read from local and write to remote */
1440	offset = progress_counter = 0;
1441	if (showprogress)
1442		start_progress_meter(local_path, sb.st_size,
1443		    &progress_counter);
1444
1445	for (;;) {
1446		int len;
1447
1448		/*
1449		 * Can't use atomicio here because it returns 0 on EOF,
1450		 * thus losing the last block of the file.
1451		 * Simulate an EOF on interrupt, allowing ACKs from the
1452		 * server to drain.
1453		 */
1454		if (interrupted || status != SSH2_FX_OK)
1455			len = 0;
1456		else do
1457			len = read(local_fd, data, conn->transfer_buflen);
1458		while ((len == -1) &&
1459		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
1460
1461		if (len == -1)
1462			fatal("Couldn't read from \"%s\": %s", local_path,
1463			    strerror(errno));
1464
1465		if (len != 0) {
1466			ack = xcalloc(1, sizeof(*ack));
1467			ack->id = ++id;
1468			ack->offset = offset;
1469			ack->len = len;
1470			TAILQ_INSERT_TAIL(&acks, ack, tq);
1471
1472			buffer_clear(&msg);
1473			buffer_put_char(&msg, SSH2_FXP_WRITE);
1474			buffer_put_int(&msg, ack->id);
1475			buffer_put_string(&msg, handle, handle_len);
1476			buffer_put_int64(&msg, offset);
1477			buffer_put_string(&msg, data, len);
1478			send_msg(conn, &msg);
1479			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1480			    id, (unsigned long long)offset, len);
1481		} else if (TAILQ_FIRST(&acks) == NULL)
1482			break;
1483
1484		if (ack == NULL)
1485			fatal("Unexpected ACK %u", id);
1486
1487		if (id == startid || len == 0 ||
1488		    id - ackid >= conn->num_requests) {
1489			u_int r_id;
1490
1491			buffer_clear(&msg);
1492			get_msg(conn, &msg);
1493			type = buffer_get_char(&msg);
1494			r_id = buffer_get_int(&msg);
1495
1496			if (type != SSH2_FXP_STATUS)
1497				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1498				    "got %d", SSH2_FXP_STATUS, type);
1499
1500			status = buffer_get_int(&msg);
1501			debug3("SSH2_FXP_STATUS %d", status);
1502
1503			/* Find the request in our queue */
1504			for (ack = TAILQ_FIRST(&acks);
1505			    ack != NULL && ack->id != r_id;
1506			    ack = TAILQ_NEXT(ack, tq))
1507				;
1508			if (ack == NULL)
1509				fatal("Can't find request for ID %u", r_id);
1510			TAILQ_REMOVE(&acks, ack, tq);
1511			debug3("In write loop, ack for %u %u bytes at %lld",
1512			    ack->id, ack->len, (long long)ack->offset);
1513			++ackid;
1514			progress_counter += ack->len;
1515			free(ack);
1516		}
1517		offset += len;
1518		if (offset < 0)
1519			fatal("%s: offset < 0", __func__);
1520	}
1521	buffer_free(&msg);
1522
1523	if (showprogress)
1524		stop_progress_meter();
1525	free(data);
1526
1527	if (status != SSH2_FX_OK) {
1528		error("Couldn't write to remote file \"%s\": %s",
1529		    remote_path, fx2txt(status));
1530		status = -1;
1531	}
1532
1533	if (close(local_fd) == -1) {
1534		error("Couldn't close local file \"%s\": %s", local_path,
1535		    strerror(errno));
1536		status = -1;
1537	}
1538
1539	/* Override umask and utimes if asked */
1540	if (pflag)
1541		do_fsetstat(conn, handle, handle_len, &a);
1542
1543	if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1544		status = -1;
1545	free(handle);
1546
1547	return status;
1548}
1549
1550static int
1551upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1552    int pflag, int printflag, int depth)
1553{
1554	int ret = 0, status;
1555	DIR *dirp;
1556	struct dirent *dp;
1557	char *filename, *new_src, *new_dst;
1558	struct stat sb;
1559	Attrib a;
1560
1561	if (depth >= MAX_DIR_DEPTH) {
1562		error("Maximum directory depth exceeded: %d levels", depth);
1563		return -1;
1564	}
1565
1566	if (stat(src, &sb) == -1) {
1567		error("Couldn't stat directory \"%s\": %s",
1568		    src, strerror(errno));
1569		return -1;
1570	}
1571	if (!S_ISDIR(sb.st_mode)) {
1572		error("\"%s\" is not a directory", src);
1573		return -1;
1574	}
1575	if (printflag)
1576		printf("Entering %s\n", src);
1577
1578	attrib_clear(&a);
1579	stat_to_attrib(&sb, &a);
1580	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1581	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1582	a.perm &= 01777;
1583	if (!pflag)
1584		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1585
1586	status = do_mkdir(conn, dst, &a, 0);
1587	/*
1588	 * we lack a portable status for errno EEXIST,
1589	 * so if we get a SSH2_FX_FAILURE back we must check
1590	 * if it was created successfully.
1591	 */
1592	if (status != SSH2_FX_OK) {
1593		if (status != SSH2_FX_FAILURE)
1594			return -1;
1595		if (do_stat(conn, dst, 0) == NULL)
1596			return -1;
1597	}
1598
1599	if ((dirp = opendir(src)) == NULL) {
1600		error("Failed to open dir \"%s\": %s", src, strerror(errno));
1601		return -1;
1602	}
1603
1604	while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1605		if (dp->d_ino == 0)
1606			continue;
1607		filename = dp->d_name;
1608		new_dst = path_append(dst, filename);
1609		new_src = path_append(src, filename);
1610
1611		if (lstat(new_src, &sb) == -1) {
1612			logit("%s: lstat failed: %s", filename,
1613			    strerror(errno));
1614			ret = -1;
1615		} else if (S_ISDIR(sb.st_mode)) {
1616			if (strcmp(filename, ".") == 0 ||
1617			    strcmp(filename, "..") == 0)
1618				continue;
1619
1620			if (upload_dir_internal(conn, new_src, new_dst,
1621			    pflag, printflag, depth + 1) == -1)
1622				ret = -1;
1623		} else if (S_ISREG(sb.st_mode)) {
1624			if (do_upload(conn, new_src, new_dst, pflag) == -1) {
1625				error("Uploading of file %s to %s failed!",
1626				    new_src, new_dst);
1627				ret = -1;
1628			}
1629		} else
1630			logit("%s: not a regular file\n", filename);
1631		free(new_dst);
1632		free(new_src);
1633	}
1634
1635	do_setstat(conn, dst, &a);
1636
1637	(void) closedir(dirp);
1638	return ret;
1639}
1640
1641int
1642upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
1643    int pflag)
1644{
1645	char *dst_canon;
1646	int ret;
1647
1648	if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1649		error("Unable to canonicalise path \"%s\"", dst);
1650		return -1;
1651	}
1652
1653	ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
1654	free(dst_canon);
1655	return ret;
1656}
1657
1658char *
1659path_append(char *p1, char *p2)
1660{
1661	char *ret;
1662	size_t len = strlen(p1) + strlen(p2) + 2;
1663
1664	ret = xmalloc(len);
1665	strlcpy(ret, p1, len);
1666	if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1667		strlcat(ret, "/", len);
1668	strlcat(ret, p2, len);
1669
1670	return(ret);
1671}
1672
1673