11305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* $OpenBSD: sftp-client.c,v 1.94 2010/12/04 00:18:01 djm Exp $ */
21305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/*
31305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
41305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood *
51305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * Permission to use, copy, modify, and distribute this software for any
61305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * purpose with or without fee is hereby granted, provided that the above
71305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * copyright notice and this permission notice appear in all copies.
81305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood *
91305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood */
171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* XXX: memleaks */
191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* XXX: signed vs unsigned */
201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* XXX: remove all logging, only return status codes */
211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* XXX: copy between two remote sites */
221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "includes.h"
241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <sys/types.h>
261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <sys/param.h>
271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef HAVE_SYS_STATVFS_H
281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <sys/statvfs.h>
291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "openbsd-compat/sys-queue.h"
311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef HAVE_SYS_STAT_H
321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood# include <sys/stat.h>
331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef HAVE_SYS_TIME_H
351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood# include <sys/time.h>
361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <sys/uio.h>
381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <dirent.h>
401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <errno.h>
411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <fcntl.h>
421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <signal.h>
431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <stdarg.h>
441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <stdio.h>
451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <string.h>
461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include <unistd.h>
471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "xmalloc.h"
491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "buffer.h"
501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "log.h"
511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "atomicio.h"
521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "progressmeter.h"
531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "misc.h"
541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "sftp.h"
561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "sftp-common.h"
571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#include "sftp-client.h"
581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodextern volatile sig_atomic_t interrupted;
601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodextern int showprogress;
611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* Minimum amount of data to read at a time */
631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define MIN_READ_SIZE	512
641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* Maximum depth to descend in directory trees */
661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define MAX_DIR_DEPTH 64
671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstruct sftp_conn {
691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int fd_in;
701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int fd_out;
711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int transfer_buflen;
721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int num_requests;
731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int version;
741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int msg_id;
751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define SFTP_EXT_POSIX_RENAME	0x00000001
761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define SFTP_EXT_STATVFS	0x00000002
771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define SFTP_EXT_FSTATVFS	0x00000004
781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#define SFTP_EXT_HARDLINK	0x00000008
791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int exts;
801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int64_t limit_kbps;
811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct bwlimit bwlimit_in, bwlimit_out;
821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood};
831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic char *
851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood/* ARGSUSED */
891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic int
901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsftpio(void *_bwlimit, size_t amount)
911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	bandwidth_limit(bwlimit, amount);
951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return 0;
961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic void
991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsend_msg(struct sftp_conn *conn, Buffer *m)
1001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
1011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_char mlen[4];
1021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct iovec iov[2];
1031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
1051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Outbound message too long %u", buffer_len(m));
1061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send length first */
1081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	put_u32(mlen, buffer_len(m));
1091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	iov[0].iov_base = mlen;
1101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	iov[0].iov_len = sizeof(mlen);
1111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	iov[1].iov_base = buffer_ptr(m);
1121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	iov[1].iov_len = buffer_len(m);
1131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (atomiciov6(writev, conn->fd_out, iov, 2,
1151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
1161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    buffer_len(m) + sizeof(mlen))
1171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Couldn't send packet: %s", strerror(errno));
1181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(m);
1201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic void
1231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_msg(struct sftp_conn *conn, Buffer *m)
1241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
1251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int msg_len;
1261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_append_space(m, 4);
1281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
1291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
1301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (errno == EPIPE)
1311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Connection closed");
1321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else
1331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Couldn't read packet: %s", strerror(errno));
1341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
1351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	msg_len = buffer_get_int(m);
1371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (msg_len > SFTP_MAX_MSG_LENGTH)
1381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Received message too long %u", msg_len);
1391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_append_space(m, msg_len);
1411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
1421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
1431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    != msg_len) {
1441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (errno == EPIPE)
1451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Connection closed");
1461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else
1471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Read packet: %s", strerror(errno));
1481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
1491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic void
1521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsend_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
1531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    u_int len)
1541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
1551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
1561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
1581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, code);
1591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
1601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_string(&msg, s, len);
1611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
1621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
1631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
1641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic void
1671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsend_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
1681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    char *s, u_int len, Attrib *a)
1691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
1701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
1711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
1731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, code);
1741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
1751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_string(&msg, s, len);
1761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	encode_attrib(&msg, a);
1771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
1781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
1791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
1801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
1811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic u_int
1831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_status(struct sftp_conn *conn, u_int expected_id)
1841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
1851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
1861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, id, status;
1871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
1891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
1901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
1911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
1921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
1941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("ID mismatch (%u != %u)", id, expected_id);
1951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type != SSH2_FXP_STATUS)
1961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
1971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    SSH2_FXP_STATUS, type);
1981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = buffer_get_int(&msg);
2001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
2011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("SSH2_FXP_STATUS %u", status);
2031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return status;
2051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
2061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic char *
2081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
2091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    const char *errfmt, ...)
2101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
2111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
2121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, id;
2131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *handle, errmsg[256];
2141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	va_list args;
2151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int status;
2161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	va_start(args, errfmt);
2181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (errfmt != NULL)
2191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
2201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	va_end(args);
2211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
2231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
2241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
2251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
2261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
2281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("%s: ID mismatch (%u != %u)",
2291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    errfmt == NULL ? __func__ : errmsg, id, expected_id);
2301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type == SSH2_FXP_STATUS) {
2311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = buffer_get_int(&msg);
2321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (errfmt != NULL)
2331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			error("%s: %s", errmsg, fx2txt(status));
2341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
2351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(NULL);
2361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (type != SSH2_FXP_HANDLE)
2371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
2381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
2391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	handle = buffer_get_string(&msg, len);
2411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
2421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(handle);
2441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
2451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic Attrib *
2471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
2481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
2491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
2501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, id;
2511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib *a;
2521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
2541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
2551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
2571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
2581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Received stat reply T:%u I:%u", type, id);
2601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
2611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("ID mismatch (%u != %u)", id, expected_id);
2621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type == SSH2_FXP_STATUS) {
2631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		int status = buffer_get_int(&msg);
2641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (quiet)
2661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug("Couldn't stat remote file: %s", fx2txt(status));
2671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else
2681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			error("Couldn't stat remote file: %s", fx2txt(status));
2691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
2701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(NULL);
2711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (type != SSH2_FXP_ATTRS) {
2721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
2731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    SSH2_FXP_ATTRS, type);
2741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
2751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a = decode_attrib(&msg);
2761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
2771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(a);
2791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
2801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic int
2821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodget_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
2831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    u_int expected_id, int quiet)
2841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
2851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
2861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, id, flag;
2871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
2891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
2901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
2921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
2931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
2941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Received statvfs reply T:%u I:%u", type, id);
2951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
2961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("ID mismatch (%u != %u)", id, expected_id);
2971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type == SSH2_FXP_STATUS) {
2981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		int status = buffer_get_int(&msg);
2991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (quiet)
3011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug("Couldn't statvfs: %s", fx2txt(status));
3021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else
3031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			error("Couldn't statvfs: %s", fx2txt(status));
3041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
3051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
3061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (type != SSH2_FXP_EXTENDED_REPLY) {
3071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
3081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    SSH2_FXP_EXTENDED_REPLY, type);
3091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
3101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	bzero(st, sizeof(*st));
3121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_bsize = buffer_get_int64(&msg);
3131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_frsize = buffer_get_int64(&msg);
3141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_blocks = buffer_get_int64(&msg);
3151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_bfree = buffer_get_int64(&msg);
3161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_bavail = buffer_get_int64(&msg);
3171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_files = buffer_get_int64(&msg);
3181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_ffree = buffer_get_int64(&msg);
3191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_favail = buffer_get_int64(&msg);
3201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_fsid = buffer_get_int64(&msg);
3211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	flag = buffer_get_int64(&msg);
3221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_namemax = buffer_get_int64(&msg);
3231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
3251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
3261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
3281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return 0;
3301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
3311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstruct sftp_conn *
3331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
3341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    u_int64_t limit_kbps)
3351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
3361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type;
3371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
3381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct sftp_conn *ret;
3391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret = xmalloc(sizeof(*ret));
3411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->fd_in = fd_in;
3421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->fd_out = fd_out;
3431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->transfer_buflen = transfer_buflen;
3441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->num_requests = num_requests;
3451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->exts = 0;
3461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->limit_kbps = 0;
3471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
3491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_INIT);
3501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
3511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(ret, &msg);
3521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
3541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(ret, &msg);
3561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Expecting a VERSION reply */
3581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
3591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
3601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    type);
3611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
3621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(NULL);
3631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
3641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->version = buffer_get_int(&msg);
3651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug2("Remote version: %u", ret->version);
3671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Check for extensions */
3691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	while (buffer_len(&msg) > 0) {
3701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		char *name = buffer_get_string(&msg, NULL);
3711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		char *value = buffer_get_string(&msg, NULL);
3721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		int known = 0;
3731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
3741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (strcmp(name, "posix-rename@openssh.com") == 0 &&
3751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strcmp(value, "1") == 0) {
3761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ret->exts |= SFTP_EXT_POSIX_RENAME;
3771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			known = 1;
3781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (strcmp(name, "statvfs@openssh.com") == 0 &&
3791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strcmp(value, "2") == 0) {
3801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ret->exts |= SFTP_EXT_STATVFS;
3811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			known = 1;
3821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
3831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strcmp(value, "2") == 0) {
3841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ret->exts |= SFTP_EXT_FSTATVFS;
3851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			known = 1;
3861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (strcmp(name, "hardlink@openssh.com") == 0 &&
3871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strcmp(value, "1") == 0) {
3881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ret->exts |= SFTP_EXT_HARDLINK;
3891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			known = 1;
3901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
3911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (known) {
3921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug2("Server supports extension \"%s\" revision %s",
3931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    name, value);
3941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else {
3951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug2("Unrecognised server extension \"%s\"", name);
3961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
3971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(name);
3981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(value);
3991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
4001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
4021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Some filexfer v.0 servers don't support large packets */
4041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (ret->version == 0)
4051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
4061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret->limit_kbps = limit_kbps;
4081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (ret->limit_kbps > 0) {
4091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
4101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    ret->transfer_buflen);
4111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
4121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    ret->transfer_buflen);
4131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
4141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return ret;
4161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
4171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodu_int
4191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsftp_proto_version(struct sftp_conn *conn)
4201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
4211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return conn->version;
4221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
4231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
4251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_close(struct sftp_conn *conn, char *handle, u_int handle_len)
4261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
4271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id, status;
4281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
4291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
4311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
4331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_CLOSE);
4341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
4351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_string(&msg, handle, handle_len);
4361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
4371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
4381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
4401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
4411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't close file: %s", fx2txt(status));
4421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
4441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return status;
4461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
4471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic int
4501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
4511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    SFTP_DIRENT ***dir)
4521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
4531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
4541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int count, type, id, handle_len, i, expected_id, ents = 0;
4551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *handle;
4561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
4581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
4601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
4611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
4621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, path);
4631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
4641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
4661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	handle = get_handle(conn, id, &handle_len,
4681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    "remote readdir(\"%s\")", path);
4691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (handle == NULL)
4701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
4711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (dir) {
4731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		ents = 0;
4741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		*dir = xmalloc(sizeof(**dir));
4751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		(*dir)[0] = NULL;
4761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
4771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	for (; !interrupted;) {
4791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		id = expected_id = conn->msg_id++;
4801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		debug3("Sending SSH2_FXP_READDIR I:%u", id);
4821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_clear(&msg);
4841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_char(&msg, SSH2_FXP_READDIR);
4851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_int(&msg, id);
4861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_string(&msg, handle, handle_len);
4871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		send_msg(conn, &msg);
4881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_clear(&msg);
4901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		get_msg(conn, &msg);
4921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		type = buffer_get_char(&msg);
4941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		id = buffer_get_int(&msg);
4951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		debug3("Received reply T:%u I:%u", type, id);
4971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
4981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (id != expected_id)
4991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("ID mismatch (%u != %u)", id, expected_id);
5001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (type == SSH2_FXP_STATUS) {
5021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			int status = buffer_get_int(&msg);
5031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("Received SSH2_FXP_STATUS %d", status);
5051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (status == SSH2_FX_EOF) {
5071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				break;
5081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			} else {
5091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Couldn't read directory: %s",
5101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    fx2txt(status));
5111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				do_close(conn, handle, handle_len);
5121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				xfree(handle);
5131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				return(status);
5141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
5151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (type != SSH2_FXP_NAME)
5161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
5171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    SSH2_FXP_NAME, type);
5181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		count = buffer_get_int(&msg);
5201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (count == 0)
5211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			break;
5221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		debug3("Received %d SSH2_FXP_NAME responses", count);
5231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		for (i = 0; i < count; i++) {
5241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			char *filename, *longname;
5251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			Attrib *a;
5261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			filename = buffer_get_string(&msg, NULL);
5281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			longname = buffer_get_string(&msg, NULL);
5291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			a = decode_attrib(&msg);
5301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (printflag)
5321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				printf("%s\n", longname);
5331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			/*
5351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			 * Directory entries should never contain '/'
5361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			 * These can be used to attack recursive ops
5371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			 * (e.g. send '../../../../etc/passwd')
5381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			 */
5391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (strchr(filename, '/') != NULL) {
5401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Server sent suspect path \"%s\" "
5411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    "during readdir of \"%s\"", filename, path);
5421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				goto next;
5431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
5441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (dir) {
5461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				*dir = xrealloc(*dir, ents + 2, sizeof(**dir));
5471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				(*dir)[ents] = xmalloc(sizeof(***dir));
5481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				(*dir)[ents]->filename = xstrdup(filename);
5491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				(*dir)[ents]->longname = xstrdup(longname);
5501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				memcpy(&(*dir)[ents]->a, a, sizeof(*a));
5511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				(*dir)[++ents] = NULL;
5521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
5531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood next:
5541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			xfree(filename);
5551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			xfree(longname);
5561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
5571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
5581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
5601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	do_close(conn, handle, handle_len);
5611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(handle);
5621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Don't return partial matches on interrupt */
5641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (interrupted && dir != NULL && *dir != NULL) {
5651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		free_sftp_dirents(*dir);
5661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		*dir = xmalloc(sizeof(**dir));
5671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		**dir = NULL;
5681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
5691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return 0;
5711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
5721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
5741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
5751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
5761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(do_lsreaddir(conn, path, 0, dir));
5771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
5781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodvoid free_sftp_dirents(SFTP_DIRENT **s)
5801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
5811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int i;
5821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	for (i = 0; s[i]; i++) {
5841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(s[i]->filename);
5851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(s[i]->longname);
5861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(s[i]);
5871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
5881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(s);
5891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
5901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
5921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_rm(struct sftp_conn *conn, char *path)
5931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
5941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
5951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
5971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
5981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
5991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
6001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
6011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
6021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't delete file: %s", fx2txt(status));
6031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
6041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
6071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
6081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
6101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
6131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    strlen(path), a);
6141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
6161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK && printflag)
6171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't create directory: %s", fx2txt(status));
6181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
6201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
6231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_rmdir(struct sftp_conn *conn, char *path)
6241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
6261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_RMDIR, path,
6291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    strlen(path));
6301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
6321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
6331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't remove directory: %s", fx2txt(status));
6341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
6361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6381305e95ba6ff9fa202d0818caf10405df4b0f648Mike LockwoodAttrib *
6391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_stat(struct sftp_conn *conn, char *path, int quiet)
6401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id;
6421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id,
6461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
6471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    path, strlen(path));
6481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(get_decode_stat(conn, id, quiet));
6501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6521305e95ba6ff9fa202d0818caf10405df4b0f648Mike LockwoodAttrib *
6531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_lstat(struct sftp_conn *conn, char *path, int quiet)
6541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id;
6561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (conn->version == 0) {
6581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (quiet)
6591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug("Server version does not support lstat operation");
6601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else
6611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			logit("Server version does not support lstat operation");
6621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(do_stat(conn, path, quiet));
6631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
6641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_LSTAT, path,
6671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    strlen(path));
6681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(get_decode_stat(conn, id, quiet));
6701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef notyet
6731305e95ba6ff9fa202d0818caf10405df4b0f648Mike LockwoodAttrib *
6741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
6751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id;
6771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
6801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    handle_len);
6811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(get_decode_stat(conn, id, quiet));
6831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
6841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
6851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
6871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_setstat(struct sftp_conn *conn, char *path, Attrib *a)
6881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
6891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
6901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
6921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
6931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    strlen(path), a);
6941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
6951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
6961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
6971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't setstat on \"%s\": %s", path,
6981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    fx2txt(status));
6991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
7011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
7021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
7041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
7051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    Attrib *a)
7061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
7071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
7081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
7101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
7111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    handle_len, a);
7121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
7141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
7151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't fsetstat: %s", fx2txt(status));
7161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
7181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
7191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodchar *
7211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_realpath(struct sftp_conn *conn, char *path)
7221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
7231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
7241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, expected_id, count, id;
7251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *filename, *longname;
7261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib *a;
7271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	expected_id = id = conn->msg_id++;
7291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_REALPATH, path,
7301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    strlen(path));
7311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
7331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
7351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
7361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
7371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
7391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("ID mismatch (%u != %u)", id, expected_id);
7401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type == SSH2_FXP_STATUS) {
7421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int status = buffer_get_int(&msg);
7431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't canonicalise: %s", fx2txt(status));
7451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
7461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return NULL;
7471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (type != SSH2_FXP_NAME)
7481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
7491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    SSH2_FXP_NAME, type);
7501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	count = buffer_get_int(&msg);
7521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (count != 1)
7531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
7541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	filename = buffer_get_string(&msg, NULL);
7561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	longname = buffer_get_string(&msg, NULL);
7571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a = decode_attrib(&msg);
7581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
7601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(longname);
7621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
7641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(filename);
7661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
7671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
7691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
7701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
7711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
7721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
7731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
7751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send rename request */
7771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
7781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
7791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_char(&msg, SSH2_FXP_EXTENDED);
7801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_int(&msg, id);
7811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_cstring(&msg, "posix-rename@openssh.com");
7821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else {
7831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_char(&msg, SSH2_FXP_RENAME);
7841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_put_int(&msg, id);
7851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
7861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, oldpath);
7871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, newpath);
7881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
7891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message %s \"%s\" -> \"%s\"",
7901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
7911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    "SSH2_FXP_RENAME", oldpath, newpath);
7921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
7931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
7951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
7961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
7971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    newpath, fx2txt(status));
7981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
7991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
8001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
8011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
8031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
8041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
8051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
8061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
8071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
8091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send link request */
8111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
8121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
8131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Server does not support hardlink@openssh.com extension");
8141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
8151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
8161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
8181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
8191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, "hardlink@openssh.com");
8201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, oldpath);
8211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, newpath);
8221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
8231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
8241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	       oldpath, newpath);
8251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
8261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
8281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
8291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
8301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    newpath, fx2txt(status));
8311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
8331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
8341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
8361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
8371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
8381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
8391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int status, id;
8401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (conn->version < 3) {
8421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("This server does not support the symlink operation");
8431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(SSH2_FX_OP_UNSUPPORTED);
8441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
8451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
8471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send symlink request */
8491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
8501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_SYMLINK);
8511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
8521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, oldpath);
8531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, newpath);
8541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
8551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
8561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    newpath);
8571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
8581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = get_status(conn, id);
8601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK)
8611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
8621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    newpath, fx2txt(status));
8631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
8651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
8661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef notyet
8681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodchar *
8691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_readlink(struct sftp_conn *conn, char *path)
8701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
8711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
8721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int type, expected_id, count, id;
8731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *filename, *longname;
8741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib *a;
8751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	expected_id = id = conn->msg_id++;
8771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
8781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
8801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	get_msg(conn, &msg);
8821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	type = buffer_get_char(&msg);
8831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = buffer_get_int(&msg);
8841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (id != expected_id)
8861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("ID mismatch (%u != %u)", id, expected_id);
8871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (type == SSH2_FXP_STATUS) {
8891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int status = buffer_get_int(&msg);
8901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't readlink: %s", fx2txt(status));
8921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(NULL);
8931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (type != SSH2_FXP_NAME)
8941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
8951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    SSH2_FXP_NAME, type);
8961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
8971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	count = buffer_get_int(&msg);
8981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (count != 1)
8991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
9001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	filename = buffer_get_string(&msg, NULL);
9021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	longname = buffer_get_string(&msg, NULL);
9031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a = decode_attrib(&msg);
9041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
9061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(longname);
9081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
9101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(filename);
9121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
9131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
9141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
9161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
9171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    int quiet)
9181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
9191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
9201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id;
9211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
9231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Server does not support statvfs@openssh.com extension");
9241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
9251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
9261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
9281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
9301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
9311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
9321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
9331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, "statvfs@openssh.com");
9341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, path);
9351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
9361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
9371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return get_decode_statvfs(conn, st, id, quiet);
9391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
9401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef notyet
9421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
9431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
9441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    struct sftp_statvfs *st, int quiet)
9451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
9461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
9471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int id;
9481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
9501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Server does not support fstatvfs@openssh.com extension");
9511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
9521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
9531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
9551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
9571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
9581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
9591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
9601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, "fstatvfs@openssh.com");
9611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_string(&msg, handle, handle_len);
9621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
9631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
9641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return get_decode_statvfs(conn, st, id, quiet);
9661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
9671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif
9681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic void
9701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodsend_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
9711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    u_int len, char *handle, u_int handle_len)
9721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
9731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
9741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
9761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
9771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_READ);
9781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
9791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_string(&msg, handle, handle_len);
9801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int64(&msg, offset);
9811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, len);
9821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
9831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
9841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
9851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
9861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
9871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_download(struct sftp_conn *conn, char *remote_path, char *local_path,
9881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    Attrib *a, int pflag)
9891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
9901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib junk;
9911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
9921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *handle;
9931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int local_fd, status = 0, write_error;
9941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int read_error, write_errno;
9951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int64_t offset, size;
9961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int handle_len, mode, type, id, buflen, num_req, max_req;
9971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	off_t progress_counter;
9981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct request {
9991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int id;
10001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int len;
10011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int64_t offset;
10021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		TAILQ_ENTRY(request) tq;
10031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	};
10041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	TAILQ_HEAD(reqhead, request) requests;
10051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct request *req;
10061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	TAILQ_INIT(&requests);
10081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
10101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
10111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Do not preserve set[ug]id here, as we do not preserve ownership */
10131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
10141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		mode = a->perm & 0777;
10151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	else
10161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		mode = 0666;
10171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
10191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    (!S_ISREG(a->perm))) {
10201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Cannot download non-regular file: %s", remote_path);
10211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
10221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
10231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
10251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		size = a->size;
10261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	else
10271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		size = 0;
10281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buflen = conn->transfer_buflen;
10301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
10311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send open request */
10331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
10341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_OPEN);
10351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
10361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, remote_path);
10371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, SSH2_FXF_READ);
10381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	attrib_clear(&junk); /* Send empty attributes */
10391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	encode_attrib(&msg, &junk);
10401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
10411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
10421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	handle = get_handle(conn, id, &handle_len,
10441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    "remote open(\"%s\")", remote_path);
10451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (handle == NULL) {
10461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
10471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
10481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
10491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
10511b6cc98e30329f380546d5f22b1c9c975e3df4f8Mike Lockwood	    mode | S_IWUSR);
10521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (local_fd == -1) {
10531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't open local file \"%s\" for writing: %s",
10541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    local_path, strerror(errno));
10551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		do_close(conn, handle, handle_len);
10561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
10571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(handle);
10581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
10591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
10601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Read from remote and write to local */
10621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	write_error = read_error = write_errno = num_req = offset = 0;
10631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	max_req = 1;
10641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	progress_counter = 0;
10651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (showprogress && size != 0)
10671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		start_progress_meter(remote_path, size, &progress_counter);
10681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	while (num_req > 0 || max_req > 0) {
10701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		char *data;
10711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int len;
10721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		/*
10741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * Simulate EOF on interrupt: stop sending new requests and
10751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * allow outstanding requests to drain gracefully
10761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 */
10771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (interrupted) {
10781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (num_req == 0) /* If we haven't started yet... */
10791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				break;
10801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			max_req = 0;
10811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
10821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
10831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		/* Send some more requests */
10841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		while (num_req < max_req) {
10851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("Request range %llu -> %llu (%d/%d)",
10861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    (unsigned long long)offset,
10871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    (unsigned long long)offset + buflen - 1,
10881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    num_req, max_req);
10891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			req = xmalloc(sizeof(*req));
10901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			req->id = conn->msg_id++;
10911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			req->len = buflen;
10921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			req->offset = offset;
10931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			offset += buflen;
10941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			num_req++;
10951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			TAILQ_INSERT_TAIL(&requests, req, tq);
10961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			send_read_request(conn, req->id, req->offset,
10971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    req->len, handle, handle_len);
10981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
10991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_clear(&msg);
11011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		get_msg(conn, &msg);
11021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		type = buffer_get_char(&msg);
11031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		id = buffer_get_int(&msg);
11041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
11051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		/* Find the request in our queue */
11071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		for (req = TAILQ_FIRST(&requests);
11081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    req != NULL && req->id != id;
11091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    req = TAILQ_NEXT(req, tq))
11101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			;
11111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (req == NULL)
11121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Unexpected reply %u", id);
11131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		switch (type) {
11151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		case SSH2_FXP_STATUS:
11161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			status = buffer_get_int(&msg);
11171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (status != SSH2_FX_EOF)
11181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				read_error = 1;
11191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			max_req = 0;
11201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			TAILQ_REMOVE(&requests, req, tq);
11211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			xfree(req);
11221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			num_req--;
11231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			break;
11241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		case SSH2_FXP_DATA:
11251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			data = buffer_get_string(&msg, &len);
11261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("Received data %llu -> %llu",
11271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    (unsigned long long)req->offset,
11281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    (unsigned long long)req->offset + len - 1);
11291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (len > req->len)
11301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				fatal("Received more data than asked for "
11311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    "%u > %u", len, req->len);
11321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
11331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    atomicio(vwrite, local_fd, data, len) != len) &&
11341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    !write_error) {
11351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				write_errno = errno;
11361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				write_error = 1;
11371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				max_req = 0;
11381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
11391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			progress_counter += len;
11401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			xfree(data);
11411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (len == req->len) {
11431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				TAILQ_REMOVE(&requests, req, tq);
11441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				xfree(req);
11451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				num_req--;
11461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			} else {
11471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				/* Resend the request for the missing data */
11481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				debug3("Short data block, re-requesting "
11491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    "%llu -> %llu (%2d)",
11501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    (unsigned long long)req->offset + len,
11511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    (unsigned long long)req->offset +
11521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    req->len - 1, num_req);
11531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				req->id = conn->msg_id++;
11541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				req->len -= len;
11551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				req->offset += len;
11561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				send_read_request(conn, req->id,
11571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    req->offset, req->len, handle, handle_len);
11581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				/* Reduce the request size */
11591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				if (len < buflen)
11601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					buflen = MAX(MIN_READ_SIZE, len);
11611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
11621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (max_req > 0) { /* max_req = 0 iff EOF received */
11631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				if (size > 0 && offset > size) {
11641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					/* Only one request at a time
11651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					 * after the expected EOF */
11661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					debug3("Finish at %llu (%2d)",
11671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					    (unsigned long long)offset,
11681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					    num_req);
11691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					max_req = 1;
11701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				} else if (max_req <= conn->num_requests) {
11711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood					++max_req;
11721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				}
11731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
11741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			break;
11751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		default:
11761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
11771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    SSH2_FXP_DATA, type);
11781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
11791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
11801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (showprogress && size)
11821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		stop_progress_meter();
11831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Sanity check */
11851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (TAILQ_FIRST(&requests) != NULL)
11861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		fatal("Transfer complete, but requests still in queue");
11871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
11881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (read_error) {
11891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't read from remote file \"%s\" : %s",
11901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    remote_path, fx2txt(status));
11911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		do_close(conn, handle, handle_len);
11921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else if (write_error) {
11931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't write to \"%s\": %s", local_path,
11941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strerror(write_errno));
11951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = -1;
11961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		do_close(conn, handle, handle_len);
11971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	} else {
11981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = do_close(conn, handle, handle_len);
11991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		/* Override umask and utimes if asked */
12011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#ifdef HAVE_FCHMOD
12021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (pflag && fchmod(local_fd, mode) == -1)
12031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#else
12041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (pflag && chmod(local_path, mode) == -1)
12051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood#endif /* HAVE_FCHMOD */
12061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			error("Couldn't set mode on \"%s\": %s", local_path,
12071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    strerror(errno));
12081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
12091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			struct timeval tv[2];
12101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[0].tv_sec = a->atime;
12111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[1].tv_sec = a->mtime;
12121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[0].tv_usec = tv[1].tv_usec = 0;
12131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (utimes(local_path, tv) == -1)
12141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Can't set times on \"%s\": %s",
12151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    local_path, strerror(errno));
12161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
12171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	close(local_fd);
12191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
12201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(handle);
12211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(status);
12231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
12241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic int
12261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddownload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
12271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    Attrib *dirattrib, int pflag, int printflag, int depth)
12281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
12291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int i, ret = 0;
12301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	SFTP_DIRENT **dir_entries;
12311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *filename, *new_src, *new_dst;
12321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	mode_t mode = 0777;
12331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (depth >= MAX_DIR_DEPTH) {
12351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Maximum directory depth exceeded: %d levels", depth);
12361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
12371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (dirattrib == NULL &&
12401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    (dirattrib = do_stat(conn, src, 1)) == NULL) {
12411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Unable to stat remote directory \"%s\"", src);
12421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
12431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (!S_ISDIR(dirattrib->perm)) {
12451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("\"%s\" is not a directory", src);
12461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
12471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (printflag)
12491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		printf("Retrieving %s\n", src);
12501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
12521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		mode = dirattrib->perm & 01777;
12531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	else {
12541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		debug("Server did not send permissions for "
12551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    "directory \"%s\"", dst);
12561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (mkdir(dst, mode) == -1 && errno != EEXIST) {
12591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("mkdir %s: %s", dst, strerror(errno));
12601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
12611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (do_readdir(conn, src, &dir_entries) == -1) {
12641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("%s: Failed to get directory contents", src);
12651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
12661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
12691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		filename = dir_entries[i]->filename;
12701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		new_dst = path_append(dst, filename);
12721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		new_src = path_append(src, filename);
12731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (S_ISDIR(dir_entries[i]->a.perm)) {
12751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (strcmp(filename, ".") == 0 ||
12761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    strcmp(filename, "..") == 0)
12771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				continue;
12781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (download_dir_internal(conn, new_src, new_dst,
12791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    &(dir_entries[i]->a), pflag, printflag,
12801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    depth + 1) == -1)
12811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				ret = -1;
12821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (S_ISREG(dir_entries[i]->a.perm) ) {
12831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (do_download(conn, new_src, new_dst,
12841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    &(dir_entries[i]->a), pflag) == -1) {
12851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Download of file %s to %s failed",
12861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    new_src, new_dst);
12871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				ret = -1;
12881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
12891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else
12901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			logit("%s: not a regular file\n", new_src);
12911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(new_dst);
12931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(new_src);
12941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
12951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
12961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (pflag) {
12971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
12981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			struct timeval tv[2];
12991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[0].tv_sec = dirattrib->atime;
13001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[1].tv_sec = dirattrib->mtime;
13011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			tv[0].tv_usec = tv[1].tv_usec = 0;
13021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (utimes(dst, tv) == -1)
13031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Can't set times on \"%s\": %s",
13041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    dst, strerror(errno));
13051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else
13061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug("Server did not send times for directory "
13071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    "\"%s\"", dst);
13081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
13091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	free_sftp_dirents(dir_entries);
13111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return ret;
13131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
13141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
13161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddownload_dir(struct sftp_conn *conn, char *src, char *dst,
13171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    Attrib *dirattrib, int pflag, int printflag)
13181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
13191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *src_canon;
13201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int ret;
13211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((src_canon = do_realpath(conn, src)) == NULL) {
13231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Unable to canonicalise path \"%s\"", src);
13241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
13251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
13261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret = download_dir_internal(conn, src_canon, dst,
13281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    dirattrib, pflag, printflag, 0);
13291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(src_canon);
13301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return ret;
13311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
13321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
13341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwooddo_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
13351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    int pflag)
13361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
13371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int local_fd;
13381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int status = SSH2_FX_OK;
13391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int handle_len, id, type;
13401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	off_t offset;
13411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *handle, *data;
13421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Buffer msg;
13431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct stat sb;
13441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib a;
13451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int32_t startid;
13461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	u_int32_t ackid;
13471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct outstanding_ack {
13481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int id;
13491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		u_int len;
13501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		off_t offset;
13511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		TAILQ_ENTRY(outstanding_ack) tq;
13521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	};
13531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	TAILQ_HEAD(ackhead, outstanding_ack) acks;
13541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct outstanding_ack *ack = NULL;
13551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	TAILQ_INIT(&acks);
13571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
13591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't open local file \"%s\" for reading: %s",
13601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    local_path, strerror(errno));
13611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
13621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
13631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (fstat(local_fd, &sb) == -1) {
13641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't fstat local file \"%s\": %s",
13651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    local_path, strerror(errno));
13661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		close(local_fd);
13671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
13681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
13691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (!S_ISREG(sb.st_mode)) {
13701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("%s is not a regular file", local_path);
13711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		close(local_fd);
13721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return(-1);
13731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
13741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	stat_to_attrib(&sb, &a);
13751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
13771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
13781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.perm &= 0777;
13791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (!pflag)
13801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
13811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_init(&msg);
13831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Send open request */
13851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	id = conn->msg_id++;
13861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_char(&msg, SSH2_FXP_OPEN);
13871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, id);
13881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_cstring(&msg, remote_path);
13891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
13901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	encode_attrib(&msg, &a);
13911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	send_msg(conn, &msg);
13921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
13931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_clear(&msg);
13951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
13961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	handle = get_handle(conn, id, &handle_len,
13971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	    "remote open(\"%s\")", remote_path);
13981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (handle == NULL) {
13991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		close(local_fd);
14001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		buffer_free(&msg);
14011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
14021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
14031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	startid = ackid = id + 1;
14051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	data = xmalloc(conn->transfer_buflen);
14061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Read from local and write to remote */
14081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	offset = 0;
14091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (showprogress)
14101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		start_progress_meter(local_path, sb.st_size, &offset);
14111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	for (;;) {
14131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		int len;
14141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		/*
14161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * Can't use atomicio here because it returns 0 on EOF,
14171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * thus losing the last block of the file.
14181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * Simulate an EOF on interrupt, allowing ACKs from the
14191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 * server to drain.
14201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		 */
14211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (interrupted || status != SSH2_FX_OK)
14221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			len = 0;
14231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		else do
14241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			len = read(local_fd, data, conn->transfer_buflen);
14251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		while ((len == -1) &&
14261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
14271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (len == -1)
14291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Couldn't read from \"%s\": %s", local_path,
14301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    strerror(errno));
14311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (len != 0) {
14331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ack = xmalloc(sizeof(*ack));
14341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ack->id = ++id;
14351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ack->offset = offset;
14361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ack->len = len;
14371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			TAILQ_INSERT_TAIL(&acks, ack, tq);
14381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_clear(&msg);
14401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_put_char(&msg, SSH2_FXP_WRITE);
14411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_put_int(&msg, ack->id);
14421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_put_string(&msg, handle, handle_len);
14431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_put_int64(&msg, offset);
14441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_put_string(&msg, data, len);
14451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			send_msg(conn, &msg);
14461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
14471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    id, (unsigned long long)offset, len);
14481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (TAILQ_FIRST(&acks) == NULL)
14491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			break;
14501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (ack == NULL)
14521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("Unexpected ACK %u", id);
14531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (id == startid || len == 0 ||
14551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    id - ackid >= conn->num_requests) {
14561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			u_int r_id;
14571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			buffer_clear(&msg);
14591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			get_msg(conn, &msg);
14601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			type = buffer_get_char(&msg);
14611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			r_id = buffer_get_int(&msg);
14621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (type != SSH2_FXP_STATUS)
14641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
14651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    "got %d", SSH2_FXP_STATUS, type);
14661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			status = buffer_get_int(&msg);
14681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("SSH2_FXP_STATUS %d", status);
14691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			/* Find the request in our queue */
14711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			for (ack = TAILQ_FIRST(&acks);
14721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    ack != NULL && ack->id != r_id;
14731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    ack = TAILQ_NEXT(ack, tq))
14741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				;
14751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (ack == NULL)
14761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				fatal("Can't find request for ID %u", r_id);
14771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			TAILQ_REMOVE(&acks, ack, tq);
14781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			debug3("In write loop, ack for %u %u bytes at %lld",
14791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    ack->id, ack->len, (long long)ack->offset);
14801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			++ackid;
14811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			xfree(ack);
14821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		}
14831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		offset += len;
14841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (offset < 0)
14851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			fatal("%s: offset < 0", __func__);
14861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
14871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	buffer_free(&msg);
14881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (showprogress)
14901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		stop_progress_meter();
14911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(data);
14921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK) {
14941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't write to remote file \"%s\": %s",
14951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    remote_path, fx2txt(status));
14961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = -1;
14971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
14981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
14991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (close(local_fd) == -1) {
15001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't close local file \"%s\": %s", local_path,
15011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    strerror(errno));
15021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = -1;
15031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/* Override umask and utimes if asked */
15061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (pflag)
15071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		do_fsetstat(conn, handle, handle_len, &a);
15081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
15101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		status = -1;
15111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(handle);
15121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return status;
15141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
15151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodstatic int
15171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodupload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
15181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    int pflag, int printflag, int depth)
15191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
15201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int ret = 0, status;
15211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	DIR *dirp;
15221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct dirent *dp;
15231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *filename, *new_src, *new_dst;
15241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	struct stat sb;
15251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	Attrib a;
15261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (depth >= MAX_DIR_DEPTH) {
15281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Maximum directory depth exceeded: %d levels", depth);
15291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
15301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (stat(src, &sb) == -1) {
15331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Couldn't stat directory \"%s\": %s",
15341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		    src, strerror(errno));
15351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
15361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (!S_ISDIR(sb.st_mode)) {
15381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("\"%s\" is not a directory", src);
15391305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
15401305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15411305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (printflag)
15421305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		printf("Entering %s\n", src);
15431305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15441305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	attrib_clear(&a);
15451305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	stat_to_attrib(&sb, &a);
15461305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
15471305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
15481305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	a.perm &= 01777;
15491305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (!pflag)
15501305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
15511305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15521305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	status = do_mkdir(conn, dst, &a, 0);
15531305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	/*
15541305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	 * we lack a portable status for errno EEXIST,
15551305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	 * so if we get a SSH2_FX_FAILURE back we must check
15561305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	 * if it was created successfully.
15571305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	 */
15581305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (status != SSH2_FX_OK) {
15591305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (status != SSH2_FX_FAILURE)
15601305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			return -1;
15611305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (do_stat(conn, dst, 0) == NULL)
15621305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			return -1;
15631305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15641305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15651305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((dirp = opendir(src)) == NULL) {
15661305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Failed to open dir \"%s\": %s", src, strerror(errno));
15671305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
15681305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
15691305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15701305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	while (((dp = readdir(dirp)) != NULL) && !interrupted) {
15711305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (dp->d_ino == 0)
15721305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			continue;
15731305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		filename = dp->d_name;
15741305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		new_dst = path_append(dst, filename);
15751305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		new_src = path_append(src, filename);
15761305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15771305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		if (lstat(new_src, &sb) == -1) {
15781305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			logit("%s: lstat failed: %s", filename,
15791305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    strerror(errno));
15801305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			ret = -1;
15811305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (S_ISDIR(sb.st_mode)) {
15821305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (strcmp(filename, ".") == 0 ||
15831305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    strcmp(filename, "..") == 0)
15841305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				continue;
15851305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
15861305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (upload_dir_internal(conn, new_src, new_dst,
15871305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			    pflag, printflag, depth + 1) == -1)
15881305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				ret = -1;
15891305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else if (S_ISREG(sb.st_mode)) {
15901305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			if (do_upload(conn, new_src, new_dst, pflag) == -1) {
15911305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				error("Uploading of file %s to %s failed!",
15921305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				    new_src, new_dst);
15931305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood				ret = -1;
15941305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			}
15951305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		} else
15961305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood			logit("%s: not a regular file\n", filename);
15971305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(new_dst);
15981305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		xfree(new_src);
15991305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
16001305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16011305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	do_setstat(conn, dst, &a);
16021305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16031305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	(void) closedir(dirp);
16041305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return ret;
16051305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
16061305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16071305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodint
16081305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodupload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
16091305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood    int pflag)
16101305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
16111305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *dst_canon;
16121305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	int ret;
16131305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16141305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if ((dst_canon = do_realpath(conn, dst)) == NULL) {
16151305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		error("Unable to canonicalise path \"%s\"", dst);
16161305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		return -1;
16171305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	}
16181305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16191305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
16201305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	xfree(dst_canon);
16211305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return ret;
16221305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
16231305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16241305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodchar *
16251305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwoodpath_append(char *p1, char *p2)
16261305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood{
16271305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	char *ret;
16281305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	size_t len = strlen(p1) + strlen(p2) + 2;
16291305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16301305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	ret = xmalloc(len);
16311305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	strlcpy(ret, p1, len);
16321305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
16331305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood		strlcat(ret, "/", len);
16341305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	strlcat(ret, p2, len);
16351305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
16361305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood	return(ret);
16371305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood}
16381305e95ba6ff9fa202d0818caf10405df4b0f648Mike Lockwood
1639