sftp-client.c revision 157016
1/*
2 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* XXX: memleaks */
18/* XXX: signed vs unsigned */
19/* XXX: remove all logging, only return status codes */
20/* XXX: copy between two remote sites */
21
22#include "includes.h"
23RCSID("$OpenBSD: sftp-client.c,v 1.58 2006/01/02 01:20:31 djm Exp $");
24
25#include "openbsd-compat/sys-queue.h"
26
27#include "buffer.h"
28#include "bufaux.h"
29#include "getput.h"
30#include "xmalloc.h"
31#include "log.h"
32#include "atomicio.h"
33#include "progressmeter.h"
34
35#include "sftp.h"
36#include "sftp-common.h"
37#include "sftp-client.h"
38
39extern volatile sig_atomic_t interrupted;
40extern int showprogress;
41
42/* Minimum amount of data to read at at time */
43#define MIN_READ_SIZE	512
44
45struct sftp_conn {
46	int fd_in;
47	int fd_out;
48	u_int transfer_buflen;
49	u_int num_requests;
50	u_int version;
51	u_int msg_id;
52};
53
54static void
55send_msg(int fd, Buffer *m)
56{
57	u_char mlen[4];
58
59	if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
60		fatal("Outbound message too long %u", buffer_len(m));
61
62	/* Send length first */
63	PUT_32BIT(mlen, buffer_len(m));
64	if (atomicio(vwrite, fd, mlen, sizeof(mlen)) != sizeof(mlen))
65		fatal("Couldn't send packet: %s", strerror(errno));
66
67	if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) != buffer_len(m))
68		fatal("Couldn't send packet: %s", strerror(errno));
69
70	buffer_clear(m);
71}
72
73static void
74get_msg(int fd, Buffer *m)
75{
76	u_int msg_len;
77
78	buffer_append_space(m, 4);
79	if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
80		if (errno == EPIPE)
81			fatal("Connection closed");
82		else
83			fatal("Couldn't read packet: %s", strerror(errno));
84	}
85
86	msg_len = buffer_get_int(m);
87	if (msg_len > SFTP_MAX_MSG_LENGTH)
88		fatal("Received message too long %u", msg_len);
89
90	buffer_append_space(m, msg_len);
91	if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
92		if (errno == EPIPE)
93			fatal("Connection closed");
94		else
95			fatal("Read packet: %s", strerror(errno));
96	}
97}
98
99static void
100send_string_request(int fd, u_int id, u_int code, char *s,
101    u_int len)
102{
103	Buffer msg;
104
105	buffer_init(&msg);
106	buffer_put_char(&msg, code);
107	buffer_put_int(&msg, id);
108	buffer_put_string(&msg, s, len);
109	send_msg(fd, &msg);
110	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
111	buffer_free(&msg);
112}
113
114static void
115send_string_attrs_request(int fd, u_int id, u_int code, char *s,
116    u_int len, Attrib *a)
117{
118	Buffer msg;
119
120	buffer_init(&msg);
121	buffer_put_char(&msg, code);
122	buffer_put_int(&msg, id);
123	buffer_put_string(&msg, s, len);
124	encode_attrib(&msg, a);
125	send_msg(fd, &msg);
126	debug3("Sent message fd %d T:%u I:%u", fd, code, id);
127	buffer_free(&msg);
128}
129
130static u_int
131get_status(int fd, u_int expected_id)
132{
133	Buffer msg;
134	u_int type, id, status;
135
136	buffer_init(&msg);
137	get_msg(fd, &msg);
138	type = buffer_get_char(&msg);
139	id = buffer_get_int(&msg);
140
141	if (id != expected_id)
142		fatal("ID mismatch (%u != %u)", id, expected_id);
143	if (type != SSH2_FXP_STATUS)
144		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
145		    SSH2_FXP_STATUS, type);
146
147	status = buffer_get_int(&msg);
148	buffer_free(&msg);
149
150	debug3("SSH2_FXP_STATUS %u", status);
151
152	return(status);
153}
154
155static char *
156get_handle(int fd, u_int expected_id, u_int *len)
157{
158	Buffer msg;
159	u_int type, id;
160	char *handle;
161
162	buffer_init(&msg);
163	get_msg(fd, &msg);
164	type = buffer_get_char(&msg);
165	id = buffer_get_int(&msg);
166
167	if (id != expected_id)
168		fatal("ID mismatch (%u != %u)", id, expected_id);
169	if (type == SSH2_FXP_STATUS) {
170		int status = buffer_get_int(&msg);
171
172		error("Couldn't get handle: %s", fx2txt(status));
173		buffer_free(&msg);
174		return(NULL);
175	} else if (type != SSH2_FXP_HANDLE)
176		fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
177		    SSH2_FXP_HANDLE, type);
178
179	handle = buffer_get_string(&msg, len);
180	buffer_free(&msg);
181
182	return(handle);
183}
184
185static Attrib *
186get_decode_stat(int fd, u_int expected_id, int quiet)
187{
188	Buffer msg;
189	u_int type, id;
190	Attrib *a;
191
192	buffer_init(&msg);
193	get_msg(fd, &msg);
194
195	type = buffer_get_char(&msg);
196	id = buffer_get_int(&msg);
197
198	debug3("Received stat reply T:%u I:%u", type, id);
199	if (id != expected_id)
200		fatal("ID mismatch (%u != %u)", id, expected_id);
201	if (type == SSH2_FXP_STATUS) {
202		int status = buffer_get_int(&msg);
203
204		if (quiet)
205			debug("Couldn't stat remote file: %s", fx2txt(status));
206		else
207			error("Couldn't stat remote file: %s", fx2txt(status));
208		buffer_free(&msg);
209		return(NULL);
210	} else if (type != SSH2_FXP_ATTRS) {
211		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
212		    SSH2_FXP_ATTRS, type);
213	}
214	a = decode_attrib(&msg);
215	buffer_free(&msg);
216
217	return(a);
218}
219
220struct sftp_conn *
221do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
222{
223	u_int type;
224	int version;
225	Buffer msg;
226	struct sftp_conn *ret;
227
228	buffer_init(&msg);
229	buffer_put_char(&msg, SSH2_FXP_INIT);
230	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
231	send_msg(fd_out, &msg);
232
233	buffer_clear(&msg);
234
235	get_msg(fd_in, &msg);
236
237	/* Expecting a VERSION reply */
238	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
239		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
240		    type);
241		buffer_free(&msg);
242		return(NULL);
243	}
244	version = buffer_get_int(&msg);
245
246	debug2("Remote version: %d", version);
247
248	/* Check for extensions */
249	while (buffer_len(&msg) > 0) {
250		char *name = buffer_get_string(&msg, NULL);
251		char *value = buffer_get_string(&msg, NULL);
252
253		debug2("Init extension: \"%s\"", name);
254		xfree(name);
255		xfree(value);
256	}
257
258	buffer_free(&msg);
259
260	ret = xmalloc(sizeof(*ret));
261	ret->fd_in = fd_in;
262	ret->fd_out = fd_out;
263	ret->transfer_buflen = transfer_buflen;
264	ret->num_requests = num_requests;
265	ret->version = version;
266	ret->msg_id = 1;
267
268	/* Some filexfer v.0 servers don't support large packets */
269	if (version == 0)
270		ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
271
272	return(ret);
273}
274
275u_int
276sftp_proto_version(struct sftp_conn *conn)
277{
278	return(conn->version);
279}
280
281int
282do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
283{
284	u_int id, status;
285	Buffer msg;
286
287	buffer_init(&msg);
288
289	id = conn->msg_id++;
290	buffer_put_char(&msg, SSH2_FXP_CLOSE);
291	buffer_put_int(&msg, id);
292	buffer_put_string(&msg, handle, handle_len);
293	send_msg(conn->fd_out, &msg);
294	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
295
296	status = get_status(conn->fd_in, id);
297	if (status != SSH2_FX_OK)
298		error("Couldn't close file: %s", fx2txt(status));
299
300	buffer_free(&msg);
301
302	return(status);
303}
304
305
306static int
307do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
308    SFTP_DIRENT ***dir)
309{
310	Buffer msg;
311	u_int count, type, id, handle_len, i, expected_id, ents = 0;
312	char *handle;
313
314	id = conn->msg_id++;
315
316	buffer_init(&msg);
317	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
318	buffer_put_int(&msg, id);
319	buffer_put_cstring(&msg, path);
320	send_msg(conn->fd_out, &msg);
321
322	buffer_clear(&msg);
323
324	handle = get_handle(conn->fd_in, id, &handle_len);
325	if (handle == NULL)
326		return(-1);
327
328	if (dir) {
329		ents = 0;
330		*dir = xmalloc(sizeof(**dir));
331		(*dir)[0] = NULL;
332	}
333
334	for (; !interrupted;) {
335		id = expected_id = conn->msg_id++;
336
337		debug3("Sending SSH2_FXP_READDIR I:%u", id);
338
339		buffer_clear(&msg);
340		buffer_put_char(&msg, SSH2_FXP_READDIR);
341		buffer_put_int(&msg, id);
342		buffer_put_string(&msg, handle, handle_len);
343		send_msg(conn->fd_out, &msg);
344
345		buffer_clear(&msg);
346
347		get_msg(conn->fd_in, &msg);
348
349		type = buffer_get_char(&msg);
350		id = buffer_get_int(&msg);
351
352		debug3("Received reply T:%u I:%u", type, id);
353
354		if (id != expected_id)
355			fatal("ID mismatch (%u != %u)", id, expected_id);
356
357		if (type == SSH2_FXP_STATUS) {
358			int status = buffer_get_int(&msg);
359
360			debug3("Received SSH2_FXP_STATUS %d", status);
361
362			if (status == SSH2_FX_EOF) {
363				break;
364			} else {
365				error("Couldn't read directory: %s",
366				    fx2txt(status));
367				do_close(conn, handle, handle_len);
368				xfree(handle);
369				return(status);
370			}
371		} else if (type != SSH2_FXP_NAME)
372			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
373			    SSH2_FXP_NAME, type);
374
375		count = buffer_get_int(&msg);
376		if (count == 0)
377			break;
378		debug3("Received %d SSH2_FXP_NAME responses", count);
379		for (i = 0; i < count; i++) {
380			char *filename, *longname;
381			Attrib *a;
382
383			filename = buffer_get_string(&msg, NULL);
384			longname = buffer_get_string(&msg, NULL);
385			a = decode_attrib(&msg);
386
387			if (printflag)
388				printf("%s\n", longname);
389
390			if (dir) {
391				*dir = xrealloc(*dir, sizeof(**dir) *
392				    (ents + 2));
393				(*dir)[ents] = xmalloc(sizeof(***dir));
394				(*dir)[ents]->filename = xstrdup(filename);
395				(*dir)[ents]->longname = xstrdup(longname);
396				memcpy(&(*dir)[ents]->a, a, sizeof(*a));
397				(*dir)[++ents] = NULL;
398			}
399
400			xfree(filename);
401			xfree(longname);
402		}
403	}
404
405	buffer_free(&msg);
406	do_close(conn, handle, handle_len);
407	xfree(handle);
408
409	/* Don't return partial matches on interrupt */
410	if (interrupted && dir != NULL && *dir != NULL) {
411		free_sftp_dirents(*dir);
412		*dir = xmalloc(sizeof(**dir));
413		**dir = NULL;
414	}
415
416	return(0);
417}
418
419int
420do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
421{
422	return(do_lsreaddir(conn, path, 0, dir));
423}
424
425void free_sftp_dirents(SFTP_DIRENT **s)
426{
427	int i;
428
429	for (i = 0; s[i]; i++) {
430		xfree(s[i]->filename);
431		xfree(s[i]->longname);
432		xfree(s[i]);
433	}
434	xfree(s);
435}
436
437int
438do_rm(struct sftp_conn *conn, char *path)
439{
440	u_int status, id;
441
442	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
443
444	id = conn->msg_id++;
445	send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
446	    strlen(path));
447	status = get_status(conn->fd_in, id);
448	if (status != SSH2_FX_OK)
449		error("Couldn't delete file: %s", fx2txt(status));
450	return(status);
451}
452
453int
454do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
455{
456	u_int status, id;
457
458	id = conn->msg_id++;
459	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
460	    strlen(path), a);
461
462	status = get_status(conn->fd_in, id);
463	if (status != SSH2_FX_OK)
464		error("Couldn't create directory: %s", fx2txt(status));
465
466	return(status);
467}
468
469int
470do_rmdir(struct sftp_conn *conn, char *path)
471{
472	u_int status, id;
473
474	id = conn->msg_id++;
475	send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
476	    strlen(path));
477
478	status = get_status(conn->fd_in, id);
479	if (status != SSH2_FX_OK)
480		error("Couldn't remove directory: %s", fx2txt(status));
481
482	return(status);
483}
484
485Attrib *
486do_stat(struct sftp_conn *conn, char *path, int quiet)
487{
488	u_int id;
489
490	id = conn->msg_id++;
491
492	send_string_request(conn->fd_out, id,
493	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
494	    path, strlen(path));
495
496	return(get_decode_stat(conn->fd_in, id, quiet));
497}
498
499Attrib *
500do_lstat(struct sftp_conn *conn, char *path, int quiet)
501{
502	u_int id;
503
504	if (conn->version == 0) {
505		if (quiet)
506			debug("Server version does not support lstat operation");
507		else
508			logit("Server version does not support lstat operation");
509		return(do_stat(conn, path, quiet));
510	}
511
512	id = conn->msg_id++;
513	send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
514	    strlen(path));
515
516	return(get_decode_stat(conn->fd_in, id, quiet));
517}
518
519Attrib *
520do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
521{
522	u_int id;
523
524	id = conn->msg_id++;
525	send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
526	    handle_len);
527
528	return(get_decode_stat(conn->fd_in, id, quiet));
529}
530
531int
532do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
533{
534	u_int status, id;
535
536	id = conn->msg_id++;
537	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
538	    strlen(path), a);
539
540	status = get_status(conn->fd_in, id);
541	if (status != SSH2_FX_OK)
542		error("Couldn't setstat on \"%s\": %s", path,
543		    fx2txt(status));
544
545	return(status);
546}
547
548int
549do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
550    Attrib *a)
551{
552	u_int status, id;
553
554	id = conn->msg_id++;
555	send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
556	    handle_len, a);
557
558	status = get_status(conn->fd_in, id);
559	if (status != SSH2_FX_OK)
560		error("Couldn't fsetstat: %s", fx2txt(status));
561
562	return(status);
563}
564
565char *
566do_realpath(struct sftp_conn *conn, char *path)
567{
568	Buffer msg;
569	u_int type, expected_id, count, id;
570	char *filename, *longname;
571	Attrib *a;
572
573	expected_id = id = conn->msg_id++;
574	send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
575	    strlen(path));
576
577	buffer_init(&msg);
578
579	get_msg(conn->fd_in, &msg);
580	type = buffer_get_char(&msg);
581	id = buffer_get_int(&msg);
582
583	if (id != expected_id)
584		fatal("ID mismatch (%u != %u)", id, expected_id);
585
586	if (type == SSH2_FXP_STATUS) {
587		u_int status = buffer_get_int(&msg);
588
589		error("Couldn't canonicalise: %s", fx2txt(status));
590		return(NULL);
591	} else if (type != SSH2_FXP_NAME)
592		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
593		    SSH2_FXP_NAME, type);
594
595	count = buffer_get_int(&msg);
596	if (count != 1)
597		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
598
599	filename = buffer_get_string(&msg, NULL);
600	longname = buffer_get_string(&msg, NULL);
601	a = decode_attrib(&msg);
602
603	debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
604
605	xfree(longname);
606
607	buffer_free(&msg);
608
609	return(filename);
610}
611
612int
613do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
614{
615	Buffer msg;
616	u_int status, id;
617
618	buffer_init(&msg);
619
620	/* Send rename request */
621	id = conn->msg_id++;
622	buffer_put_char(&msg, SSH2_FXP_RENAME);
623	buffer_put_int(&msg, id);
624	buffer_put_cstring(&msg, oldpath);
625	buffer_put_cstring(&msg, newpath);
626	send_msg(conn->fd_out, &msg);
627	debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
628	    newpath);
629	buffer_free(&msg);
630
631	status = get_status(conn->fd_in, id);
632	if (status != SSH2_FX_OK)
633		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
634		    newpath, fx2txt(status));
635
636	return(status);
637}
638
639int
640do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
641{
642	Buffer msg;
643	u_int status, id;
644
645	if (conn->version < 3) {
646		error("This server does not support the symlink operation");
647		return(SSH2_FX_OP_UNSUPPORTED);
648	}
649
650	buffer_init(&msg);
651
652	/* Send symlink request */
653	id = conn->msg_id++;
654	buffer_put_char(&msg, SSH2_FXP_SYMLINK);
655	buffer_put_int(&msg, id);
656	buffer_put_cstring(&msg, oldpath);
657	buffer_put_cstring(&msg, newpath);
658	send_msg(conn->fd_out, &msg);
659	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
660	    newpath);
661	buffer_free(&msg);
662
663	status = get_status(conn->fd_in, id);
664	if (status != SSH2_FX_OK)
665		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
666		    newpath, fx2txt(status));
667
668	return(status);
669}
670
671char *
672do_readlink(struct sftp_conn *conn, char *path)
673{
674	Buffer msg;
675	u_int type, expected_id, count, id;
676	char *filename, *longname;
677	Attrib *a;
678
679	expected_id = id = conn->msg_id++;
680	send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
681	    strlen(path));
682
683	buffer_init(&msg);
684
685	get_msg(conn->fd_in, &msg);
686	type = buffer_get_char(&msg);
687	id = buffer_get_int(&msg);
688
689	if (id != expected_id)
690		fatal("ID mismatch (%u != %u)", id, expected_id);
691
692	if (type == SSH2_FXP_STATUS) {
693		u_int status = buffer_get_int(&msg);
694
695		error("Couldn't readlink: %s", fx2txt(status));
696		return(NULL);
697	} else if (type != SSH2_FXP_NAME)
698		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
699		    SSH2_FXP_NAME, type);
700
701	count = buffer_get_int(&msg);
702	if (count != 1)
703		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
704
705	filename = buffer_get_string(&msg, NULL);
706	longname = buffer_get_string(&msg, NULL);
707	a = decode_attrib(&msg);
708
709	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
710
711	xfree(longname);
712
713	buffer_free(&msg);
714
715	return(filename);
716}
717
718static void
719send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
720    char *handle, u_int handle_len)
721{
722	Buffer msg;
723
724	buffer_init(&msg);
725	buffer_clear(&msg);
726	buffer_put_char(&msg, SSH2_FXP_READ);
727	buffer_put_int(&msg, id);
728	buffer_put_string(&msg, handle, handle_len);
729	buffer_put_int64(&msg, offset);
730	buffer_put_int(&msg, len);
731	send_msg(fd_out, &msg);
732	buffer_free(&msg);
733}
734
735int
736do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
737    int pflag)
738{
739	Attrib junk, *a;
740	Buffer msg;
741	char *handle;
742	int local_fd, status = 0, write_error;
743	int read_error, write_errno;
744	u_int64_t offset, size;
745	u_int handle_len, mode, type, id, buflen, num_req, max_req;
746	off_t progress_counter;
747	struct request {
748		u_int id;
749		u_int len;
750		u_int64_t offset;
751		TAILQ_ENTRY(request) tq;
752	};
753	TAILQ_HEAD(reqhead, request) requests;
754	struct request *req;
755
756	TAILQ_INIT(&requests);
757
758	a = do_stat(conn, remote_path, 0);
759	if (a == NULL)
760		return(-1);
761
762	/* XXX: should we preserve set[ug]id? */
763	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
764		mode = a->perm & 0777;
765	else
766		mode = 0666;
767
768	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
769	    (!S_ISREG(a->perm))) {
770		error("Cannot download non-regular file: %s", remote_path);
771		return(-1);
772	}
773
774	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
775		size = a->size;
776	else
777		size = 0;
778
779	buflen = conn->transfer_buflen;
780	buffer_init(&msg);
781
782	/* Send open request */
783	id = conn->msg_id++;
784	buffer_put_char(&msg, SSH2_FXP_OPEN);
785	buffer_put_int(&msg, id);
786	buffer_put_cstring(&msg, remote_path);
787	buffer_put_int(&msg, SSH2_FXF_READ);
788	attrib_clear(&junk); /* Send empty attributes */
789	encode_attrib(&msg, &junk);
790	send_msg(conn->fd_out, &msg);
791	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
792
793	handle = get_handle(conn->fd_in, id, &handle_len);
794	if (handle == NULL) {
795		buffer_free(&msg);
796		return(-1);
797	}
798
799	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
800	    mode | S_IWRITE);
801	if (local_fd == -1) {
802		error("Couldn't open local file \"%s\" for writing: %s",
803		    local_path, strerror(errno));
804		buffer_free(&msg);
805		xfree(handle);
806		return(-1);
807	}
808
809	/* Read from remote and write to local */
810	write_error = read_error = write_errno = num_req = offset = 0;
811	max_req = 1;
812	progress_counter = 0;
813
814	if (showprogress && size != 0)
815		start_progress_meter(remote_path, size, &progress_counter);
816
817	while (num_req > 0 || max_req > 0) {
818		char *data;
819		u_int len;
820
821		/*
822		 * Simulate EOF on interrupt: stop sending new requests and
823		 * allow outstanding requests to drain gracefully
824		 */
825		if (interrupted) {
826			if (num_req == 0) /* If we haven't started yet... */
827				break;
828			max_req = 0;
829		}
830
831		/* Send some more requests */
832		while (num_req < max_req) {
833			debug3("Request range %llu -> %llu (%d/%d)",
834			    (unsigned long long)offset,
835			    (unsigned long long)offset + buflen - 1,
836			    num_req, max_req);
837			req = xmalloc(sizeof(*req));
838			req->id = conn->msg_id++;
839			req->len = buflen;
840			req->offset = offset;
841			offset += buflen;
842			num_req++;
843			TAILQ_INSERT_TAIL(&requests, req, tq);
844			send_read_request(conn->fd_out, req->id, req->offset,
845			    req->len, handle, handle_len);
846		}
847
848		buffer_clear(&msg);
849		get_msg(conn->fd_in, &msg);
850		type = buffer_get_char(&msg);
851		id = buffer_get_int(&msg);
852		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
853
854		/* Find the request in our queue */
855		for (req = TAILQ_FIRST(&requests);
856		    req != NULL && req->id != id;
857		    req = TAILQ_NEXT(req, tq))
858			;
859		if (req == NULL)
860			fatal("Unexpected reply %u", id);
861
862		switch (type) {
863		case SSH2_FXP_STATUS:
864			status = buffer_get_int(&msg);
865			if (status != SSH2_FX_EOF)
866				read_error = 1;
867			max_req = 0;
868			TAILQ_REMOVE(&requests, req, tq);
869			xfree(req);
870			num_req--;
871			break;
872		case SSH2_FXP_DATA:
873			data = buffer_get_string(&msg, &len);
874			debug3("Received data %llu -> %llu",
875			    (unsigned long long)req->offset,
876			    (unsigned long long)req->offset + len - 1);
877			if (len > req->len)
878				fatal("Received more data than asked for "
879				    "%u > %u", len, req->len);
880			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
881			    atomicio(vwrite, local_fd, data, len) != len) &&
882			    !write_error) {
883				write_errno = errno;
884				write_error = 1;
885				max_req = 0;
886			}
887			progress_counter += len;
888			xfree(data);
889
890			if (len == req->len) {
891				TAILQ_REMOVE(&requests, req, tq);
892				xfree(req);
893				num_req--;
894			} else {
895				/* Resend the request for the missing data */
896				debug3("Short data block, re-requesting "
897				    "%llu -> %llu (%2d)",
898				    (unsigned long long)req->offset + len,
899				    (unsigned long long)req->offset +
900				    req->len - 1, num_req);
901				req->id = conn->msg_id++;
902				req->len -= len;
903				req->offset += len;
904				send_read_request(conn->fd_out, req->id,
905				    req->offset, req->len, handle, handle_len);
906				/* Reduce the request size */
907				if (len < buflen)
908					buflen = MAX(MIN_READ_SIZE, len);
909			}
910			if (max_req > 0) { /* max_req = 0 iff EOF received */
911				if (size > 0 && offset > size) {
912					/* Only one request at a time
913					 * after the expected EOF */
914					debug3("Finish at %llu (%2d)",
915					    (unsigned long long)offset,
916					    num_req);
917					max_req = 1;
918				} else if (max_req <= conn->num_requests) {
919					++max_req;
920				}
921			}
922			break;
923		default:
924			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
925			    SSH2_FXP_DATA, type);
926		}
927	}
928
929	if (showprogress && size)
930		stop_progress_meter();
931
932	/* Sanity check */
933	if (TAILQ_FIRST(&requests) != NULL)
934		fatal("Transfer complete, but requests still in queue");
935
936	if (read_error) {
937		error("Couldn't read from remote file \"%s\" : %s",
938		    remote_path, fx2txt(status));
939		do_close(conn, handle, handle_len);
940	} else if (write_error) {
941		error("Couldn't write to \"%s\": %s", local_path,
942		    strerror(write_errno));
943		status = -1;
944		do_close(conn, handle, handle_len);
945	} else {
946		status = do_close(conn, handle, handle_len);
947
948		/* Override umask and utimes if asked */
949#ifdef HAVE_FCHMOD
950		if (pflag && fchmod(local_fd, mode) == -1)
951#else
952		if (pflag && chmod(local_path, mode) == -1)
953#endif /* HAVE_FCHMOD */
954			error("Couldn't set mode on \"%s\": %s", local_path,
955			    strerror(errno));
956		if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
957			struct timeval tv[2];
958			tv[0].tv_sec = a->atime;
959			tv[1].tv_sec = a->mtime;
960			tv[0].tv_usec = tv[1].tv_usec = 0;
961			if (utimes(local_path, tv) == -1)
962				error("Can't set times on \"%s\": %s",
963				    local_path, strerror(errno));
964		}
965	}
966	close(local_fd);
967	buffer_free(&msg);
968	xfree(handle);
969
970	return(status);
971}
972
973int
974do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
975    int pflag)
976{
977	int local_fd, status;
978	u_int handle_len, id, type;
979	u_int64_t offset;
980	char *handle, *data;
981	Buffer msg;
982	struct stat sb;
983	Attrib a;
984	u_int32_t startid;
985	u_int32_t ackid;
986	struct outstanding_ack {
987		u_int id;
988		u_int len;
989		u_int64_t offset;
990		TAILQ_ENTRY(outstanding_ack) tq;
991	};
992	TAILQ_HEAD(ackhead, outstanding_ack) acks;
993	struct outstanding_ack *ack = NULL;
994
995	TAILQ_INIT(&acks);
996
997	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
998		error("Couldn't open local file \"%s\" for reading: %s",
999		    local_path, strerror(errno));
1000		return(-1);
1001	}
1002	if (fstat(local_fd, &sb) == -1) {
1003		error("Couldn't fstat local file \"%s\": %s",
1004		    local_path, strerror(errno));
1005		close(local_fd);
1006		return(-1);
1007	}
1008	if (!S_ISREG(sb.st_mode)) {
1009		error("%s is not a regular file", local_path);
1010		close(local_fd);
1011		return(-1);
1012	}
1013	stat_to_attrib(&sb, &a);
1014
1015	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1016	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1017	a.perm &= 0777;
1018	if (!pflag)
1019		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1020
1021	buffer_init(&msg);
1022
1023	/* Send open request */
1024	id = conn->msg_id++;
1025	buffer_put_char(&msg, SSH2_FXP_OPEN);
1026	buffer_put_int(&msg, id);
1027	buffer_put_cstring(&msg, remote_path);
1028	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1029	encode_attrib(&msg, &a);
1030	send_msg(conn->fd_out, &msg);
1031	debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1032
1033	buffer_clear(&msg);
1034
1035	handle = get_handle(conn->fd_in, id, &handle_len);
1036	if (handle == NULL) {
1037		close(local_fd);
1038		buffer_free(&msg);
1039		return(-1);
1040	}
1041
1042	startid = ackid = id + 1;
1043	data = xmalloc(conn->transfer_buflen);
1044
1045	/* Read from local and write to remote */
1046	offset = 0;
1047	if (showprogress)
1048		start_progress_meter(local_path, sb.st_size, &offset);
1049
1050	for (;;) {
1051		int len;
1052
1053		/*
1054		 * Can't use atomicio here because it returns 0 on EOF,
1055		 * thus losing the last block of the file.
1056		 * Simulate an EOF on interrupt, allowing ACKs from the
1057		 * server to drain.
1058		 */
1059		if (interrupted)
1060			len = 0;
1061		else do
1062			len = read(local_fd, data, conn->transfer_buflen);
1063		while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1064
1065		if (len == -1)
1066			fatal("Couldn't read from \"%s\": %s", local_path,
1067			    strerror(errno));
1068
1069		if (len != 0) {
1070			ack = xmalloc(sizeof(*ack));
1071			ack->id = ++id;
1072			ack->offset = offset;
1073			ack->len = len;
1074			TAILQ_INSERT_TAIL(&acks, ack, tq);
1075
1076			buffer_clear(&msg);
1077			buffer_put_char(&msg, SSH2_FXP_WRITE);
1078			buffer_put_int(&msg, ack->id);
1079			buffer_put_string(&msg, handle, handle_len);
1080			buffer_put_int64(&msg, offset);
1081			buffer_put_string(&msg, data, len);
1082			send_msg(conn->fd_out, &msg);
1083			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1084			    id, (unsigned long long)offset, len);
1085		} else if (TAILQ_FIRST(&acks) == NULL)
1086			break;
1087
1088		if (ack == NULL)
1089			fatal("Unexpected ACK %u", id);
1090
1091		if (id == startid || len == 0 ||
1092		    id - ackid >= conn->num_requests) {
1093			u_int r_id;
1094
1095			buffer_clear(&msg);
1096			get_msg(conn->fd_in, &msg);
1097			type = buffer_get_char(&msg);
1098			r_id = buffer_get_int(&msg);
1099
1100			if (type != SSH2_FXP_STATUS)
1101				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1102				    "got %d", SSH2_FXP_STATUS, type);
1103
1104			status = buffer_get_int(&msg);
1105			debug3("SSH2_FXP_STATUS %d", status);
1106
1107			/* Find the request in our queue */
1108			for (ack = TAILQ_FIRST(&acks);
1109			    ack != NULL && ack->id != r_id;
1110			    ack = TAILQ_NEXT(ack, tq))
1111				;
1112			if (ack == NULL)
1113				fatal("Can't find request for ID %u", r_id);
1114			TAILQ_REMOVE(&acks, ack, tq);
1115
1116			if (status != SSH2_FX_OK) {
1117				error("Couldn't write to remote file \"%s\": %s",
1118				    remote_path, fx2txt(status));
1119				do_close(conn, handle, handle_len);
1120				close(local_fd);
1121				xfree(data);
1122				xfree(ack);
1123				goto done;
1124			}
1125			debug3("In write loop, ack for %u %u bytes at %llu",
1126			    ack->id, ack->len, (unsigned long long)ack->offset);
1127			++ackid;
1128			xfree(ack);
1129		}
1130		offset += len;
1131	}
1132	if (showprogress)
1133		stop_progress_meter();
1134	xfree(data);
1135
1136	if (close(local_fd) == -1) {
1137		error("Couldn't close local file \"%s\": %s", local_path,
1138		    strerror(errno));
1139		do_close(conn, handle, handle_len);
1140		status = -1;
1141		goto done;
1142	}
1143
1144	/* Override umask and utimes if asked */
1145	if (pflag)
1146		do_fsetstat(conn, handle, handle_len, &a);
1147
1148	status = do_close(conn, handle, handle_len);
1149
1150done:
1151	xfree(handle);
1152	buffer_free(&msg);
1153	return(status);
1154}
1155