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