sftp-client.c revision 1.29
1/*	$NetBSD: sftp-client.c,v 1.29 2021/09/27 17:03:13 christos Exp $	*/
2/* $OpenBSD: sftp-client.c,v 1.155 2021/09/03 05:12:25 dtucker Exp $ */
3
4/*
5 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* XXX: memleaks */
21/* XXX: signed vs unsigned */
22/* XXX: remove all logging, only return status codes */
23/* XXX: copy between two remote sites */
24
25#include "includes.h"
26__RCSID("$NetBSD: sftp-client.c,v 1.29 2021/09/27 17:03:13 christos Exp $");
27
28#include <sys/param.h>	/* MIN MAX */
29#include <sys/types.h>
30#include <sys/poll.h>
31#include <sys/queue.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/statvfs.h>
35#include <sys/uio.h>
36
37#include <dirent.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <poll.h>
41#include <signal.h>
42#include <stdarg.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "xmalloc.h"
49#include "ssherr.h"
50#include "sshbuf.h"
51#include "log.h"
52#include "atomicio.h"
53#include "progressmeter.h"
54#include "misc.h"
55#include "utf8.h"
56
57#include "sftp.h"
58#include "sftp-common.h"
59#include "sftp-client.h"
60
61extern volatile sig_atomic_t interrupted;
62extern int showprogress;
63
64/* Default size of buffer for up/download */
65#define DEFAULT_COPY_BUFLEN	32768
66
67/* Default number of concurrent outstanding requests */
68#define DEFAULT_NUM_REQUESTS	64
69
70/* Minimum amount of data to read at a time */
71#define MIN_READ_SIZE	512
72
73/* Maximum depth to descend in directory trees */
74#define MAX_DIR_DEPTH 64
75
76struct sftp_conn {
77	int fd_in;
78	int fd_out;
79	u_int download_buflen;
80	u_int upload_buflen;
81	u_int num_requests;
82	u_int version;
83	u_int msg_id;
84#define SFTP_EXT_POSIX_RENAME	0x00000001
85#define SFTP_EXT_STATVFS	0x00000002
86#define SFTP_EXT_FSTATVFS	0x00000004
87#define SFTP_EXT_HARDLINK	0x00000008
88#define SFTP_EXT_FSYNC		0x00000010
89#define SFTP_EXT_LSETSTAT	0x00000020
90#define SFTP_EXT_LIMITS		0x00000040
91#define SFTP_EXT_PATH_EXPAND	0x00000080
92	u_int exts;
93	u_int64_t limit_kbps;
94	struct bwlimit bwlimit_in, bwlimit_out;
95};
96
97/* Tracks in-progress requests during file transfers */
98struct request {
99	u_int id;
100	size_t len;
101	u_int64_t offset;
102	TAILQ_ENTRY(request) tq;
103};
104TAILQ_HEAD(requests, request);
105
106static u_char *
107get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
108    const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
109
110static struct request *
111request_enqueue(struct requests *requests, u_int id, size_t len,
112    uint64_t offset)
113{
114	struct request *req;
115
116	req = xcalloc(1, sizeof(*req));
117	req->id = id;
118	req->len = len;
119	req->offset = offset;
120	TAILQ_INSERT_TAIL(requests, req, tq);
121	return req;
122}
123
124static struct request *
125request_find(struct requests *requests, u_int id)
126{
127	struct request *req;
128
129	for (req = TAILQ_FIRST(requests);
130	    req != NULL && req->id != id;
131	    req = TAILQ_NEXT(req, tq))
132		;
133	return req;
134}
135
136/* ARGSUSED */
137static int
138sftpio(void *_bwlimit, size_t amount)
139{
140	struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
141
142	refresh_progress_meter(0);
143	if (bwlimit != NULL)
144		bandwidth_limit(bwlimit, amount);
145	return 0;
146}
147
148static void
149send_msg(struct sftp_conn *conn, struct sshbuf *m)
150{
151	u_char mlen[4];
152	struct iovec iov[2];
153
154	if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH)
155		fatal("Outbound message too long %zu", sshbuf_len(m));
156
157	/* Send length first */
158	put_u32(mlen, sshbuf_len(m));
159	iov[0].iov_base = mlen;
160	iov[0].iov_len = sizeof(mlen);
161	iov[1].iov_base = __UNCONST(sshbuf_ptr(m));
162	iov[1].iov_len = sshbuf_len(m);
163
164	if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio,
165	    conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) !=
166	    sshbuf_len(m) + sizeof(mlen))
167		fatal("Couldn't send packet: %s", strerror(errno));
168
169	sshbuf_reset(m);
170}
171
172static void
173get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial)
174{
175	u_int msg_len;
176	u_char *p;
177	int r;
178
179	sshbuf_reset(m);
180	if ((r = sshbuf_reserve(m, 4, &p)) != 0)
181		fatal_fr(r, "reserve");
182	if (atomicio6(read, conn->fd_in, p, 4, sftpio,
183	    conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) {
184		if (errno == EPIPE || errno == ECONNRESET)
185			fatal("Connection closed");
186		else
187			fatal("Couldn't read packet: %s", strerror(errno));
188	}
189
190	if ((r = sshbuf_get_u32(m, &msg_len)) != 0)
191		fatal_fr(r, "sshbuf_get_u32");
192	if (msg_len > SFTP_MAX_MSG_LENGTH) {
193		do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL,
194		    "Received message too long %u", msg_len);
195		fatal("Ensure the remote shell produces no output "
196		    "for non-interactive sessions.");
197	}
198
199	if ((r = sshbuf_reserve(m, msg_len, &p)) != 0)
200		fatal_fr(r, "reserve");
201	if (atomicio6(read, conn->fd_in, p, msg_len, sftpio,
202	    conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL)
203	    != msg_len) {
204		if (errno == EPIPE)
205			fatal("Connection closed");
206		else
207			fatal("Read packet: %s", strerror(errno));
208	}
209}
210
211static void
212get_msg(struct sftp_conn *conn, struct sshbuf *m)
213{
214	get_msg_extended(conn, m, 0);
215}
216
217static void
218send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s,
219    u_int len)
220{
221	struct sshbuf *msg;
222	int r;
223
224	if ((msg = sshbuf_new()) == NULL)
225		fatal_f("sshbuf_new failed");
226	if ((r = sshbuf_put_u8(msg, code)) != 0 ||
227	    (r = sshbuf_put_u32(msg, id)) != 0 ||
228	    (r = sshbuf_put_string(msg, s, len)) != 0)
229		fatal_fr(r, "compose");
230	send_msg(conn, msg);
231	debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
232	sshbuf_free(msg);
233}
234
235static void
236send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
237    const void *s, u_int len, Attrib *a)
238{
239	struct sshbuf *msg;
240	int r;
241
242	if ((msg = sshbuf_new()) == NULL)
243		fatal_f("sshbuf_new failed");
244	if ((r = sshbuf_put_u8(msg, code)) != 0 ||
245	    (r = sshbuf_put_u32(msg, id)) != 0 ||
246	    (r = sshbuf_put_string(msg, s, len)) != 0 ||
247	    (r = encode_attrib(msg, a)) != 0)
248		fatal_fr(r, "compose");
249	send_msg(conn, msg);
250	debug3("Sent message fd %d T:%u I:%u F:0x%04x M:%05o",
251	    conn->fd_out, code, id, a->flags, a->perm);
252	sshbuf_free(msg);
253}
254
255static u_int
256get_status(struct sftp_conn *conn, u_int expected_id)
257{
258	struct sshbuf *msg;
259	u_char type;
260	u_int id, status;
261	int r;
262
263	if ((msg = sshbuf_new()) == NULL)
264		fatal_f("sshbuf_new failed");
265	get_msg(conn, msg);
266	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
267	    (r = sshbuf_get_u32(msg, &id)) != 0)
268		fatal_fr(r, "compose");
269
270	if (id != expected_id)
271		fatal("ID mismatch (%u != %u)", id, expected_id);
272	if (type != SSH2_FXP_STATUS)
273		fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
274		    SSH2_FXP_STATUS, type);
275
276	if ((r = sshbuf_get_u32(msg, &status)) != 0)
277		fatal_fr(r, "parse");
278	sshbuf_free(msg);
279
280	debug3("SSH2_FXP_STATUS %u", status);
281
282	return status;
283}
284
285static u_char *
286get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
287    const char *errfmt, ...)
288{
289	struct sshbuf *msg;
290	u_int id, status;
291	u_char type;
292	u_char *handle;
293	char errmsg[256];
294	va_list args;
295	int r;
296
297	va_start(args, errfmt);
298	if (errfmt != NULL)
299		vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
300	va_end(args);
301
302	if ((msg = sshbuf_new()) == NULL)
303		fatal_f("sshbuf_new failed");
304	get_msg(conn, msg);
305	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
306	    (r = sshbuf_get_u32(msg, &id)) != 0)
307		fatal_fr(r, "parse");
308
309	if (id != expected_id)
310		fatal("%s: ID mismatch (%u != %u)",
311		    errfmt == NULL ? __func__ : errmsg, id, expected_id);
312	if (type == SSH2_FXP_STATUS) {
313		if ((r = sshbuf_get_u32(msg, &status)) != 0)
314			fatal_fr(r, "parse status");
315		if (errfmt != NULL)
316			error("%s: %s", errmsg, fx2txt(status));
317		sshbuf_free(msg);
318		return(NULL);
319	} else if (type != SSH2_FXP_HANDLE)
320		fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
321		    errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
322
323	if ((r = sshbuf_get_string(msg, &handle, len)) != 0)
324		fatal_fr(r, "parse handle");
325	sshbuf_free(msg);
326
327	return handle;
328}
329
330/* XXX returing &static is error-prone. Refactor to fill *Attrib argument */
331static Attrib *
332get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
333{
334	struct sshbuf *msg;
335	u_int id;
336	u_char type;
337	int r;
338	static Attrib a;
339
340	if ((msg = sshbuf_new()) == NULL)
341		fatal_f("sshbuf_new failed");
342	get_msg(conn, msg);
343
344	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
345	    (r = sshbuf_get_u32(msg, &id)) != 0)
346		fatal_fr(r, "parse");
347
348	if (id != expected_id)
349		fatal("ID mismatch (%u != %u)", id, expected_id);
350	if (type == SSH2_FXP_STATUS) {
351		u_int status;
352
353		if ((r = sshbuf_get_u32(msg, &status)) != 0)
354			fatal_fr(r, "parse status");
355		if (quiet)
356			debug("Couldn't stat remote file: %s", fx2txt(status));
357		else
358			error("Couldn't stat remote file: %s", fx2txt(status));
359		sshbuf_free(msg);
360		return(NULL);
361	} else if (type != SSH2_FXP_ATTRS) {
362		fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
363		    SSH2_FXP_ATTRS, type);
364	}
365	if ((r = decode_attrib(msg, &a)) != 0) {
366		error_fr(r, "decode_attrib");
367		sshbuf_free(msg);
368		return NULL;
369	}
370	debug3("Recevied stat reply T:%u I:%u F:0x%04x M:%05o",
371	    type, id, a.flags, a.perm);
372	sshbuf_free(msg);
373
374	return &a;
375}
376
377static int
378get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
379    u_int expected_id, int quiet)
380{
381	struct sshbuf *msg;
382	u_char type;
383	u_int id;
384	u_int64_t flag;
385	int r;
386
387	if ((msg = sshbuf_new()) == NULL)
388		fatal_f("sshbuf_new failed");
389	get_msg(conn, msg);
390
391	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
392	    (r = sshbuf_get_u32(msg, &id)) != 0)
393		fatal_fr(r, "parse");
394
395	debug3("Received statvfs reply T:%u I:%u", type, id);
396	if (id != expected_id)
397		fatal("ID mismatch (%u != %u)", id, expected_id);
398	if (type == SSH2_FXP_STATUS) {
399		u_int status;
400
401		if ((r = sshbuf_get_u32(msg, &status)) != 0)
402			fatal_fr(r, "parse status");
403		if (quiet)
404			debug("Couldn't statvfs: %s", fx2txt(status));
405		else
406			error("Couldn't statvfs: %s", fx2txt(status));
407		sshbuf_free(msg);
408		return -1;
409	} else if (type != SSH2_FXP_EXTENDED_REPLY) {
410		fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
411		    SSH2_FXP_EXTENDED_REPLY, type);
412	}
413
414	memset(st, 0, sizeof(*st));
415	if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 ||
416	    (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 ||
417	    (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 ||
418	    (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 ||
419	    (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 ||
420	    (r = sshbuf_get_u64(msg, &st->f_files)) != 0 ||
421	    (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 ||
422	    (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 ||
423	    (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 ||
424	    (r = sshbuf_get_u64(msg, &flag)) != 0 ||
425	    (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0)
426		fatal_fr(r, "parse statvfs");
427
428	st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
429	st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
430
431	sshbuf_free(msg);
432
433	return 0;
434}
435
436struct sftp_conn *
437do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
438    u_int64_t limit_kbps)
439{
440	u_char type;
441	struct sshbuf *msg;
442	struct sftp_conn *ret;
443	int r;
444
445	ret = xcalloc(1, sizeof(*ret));
446	ret->msg_id = 1;
447	ret->fd_in = fd_in;
448	ret->fd_out = fd_out;
449	ret->download_buflen = ret->upload_buflen =
450	    transfer_buflen ? transfer_buflen : DEFAULT_COPY_BUFLEN;
451	ret->num_requests =
452	    num_requests ? num_requests : DEFAULT_NUM_REQUESTS;
453	ret->exts = 0;
454	ret->limit_kbps = 0;
455
456	if ((msg = sshbuf_new()) == NULL)
457		fatal_f("sshbuf_new failed");
458	if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 ||
459	    (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0)
460		fatal_fr(r, "parse");
461
462	send_msg(ret, msg);
463
464	get_msg_extended(ret, msg, 1);
465
466	/* Expecting a VERSION reply */
467	if ((r = sshbuf_get_u8(msg, &type)) != 0)
468		fatal_fr(r, "parse type");
469	if (type != SSH2_FXP_VERSION) {
470		error("Invalid packet back from SSH2_FXP_INIT (type %u)",
471		    type);
472		sshbuf_free(msg);
473		free(ret);
474		return(NULL);
475	}
476	if ((r = sshbuf_get_u32(msg, &ret->version)) != 0)
477		fatal_fr(r, "parse version");
478
479	debug2("Remote version: %u", ret->version);
480
481	/* Check for extensions */
482	while (sshbuf_len(msg) > 0) {
483		char *name;
484		u_char *value;
485		size_t vlen;
486		int known = 0;
487
488		if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 ||
489		    (r = sshbuf_get_string(msg, &value, &vlen)) != 0)
490			fatal_fr(r, "parse extension");
491		if (strcmp(name, "posix-rename@openssh.com") == 0 &&
492		    strcmp((char *)value, "1") == 0) {
493			ret->exts |= SFTP_EXT_POSIX_RENAME;
494			known = 1;
495		} else if (strcmp(name, "statvfs@openssh.com") == 0 &&
496		    strcmp((char *)value, "2") == 0) {
497			ret->exts |= SFTP_EXT_STATVFS;
498			known = 1;
499		} else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
500		    strcmp((char *)value, "2") == 0) {
501			ret->exts |= SFTP_EXT_FSTATVFS;
502			known = 1;
503		} else if (strcmp(name, "hardlink@openssh.com") == 0 &&
504		    strcmp((char *)value, "1") == 0) {
505			ret->exts |= SFTP_EXT_HARDLINK;
506			known = 1;
507		} else if (strcmp(name, "fsync@openssh.com") == 0 &&
508		    strcmp((char *)value, "1") == 0) {
509			ret->exts |= SFTP_EXT_FSYNC;
510			known = 1;
511		} else if (strcmp(name, "lsetstat@openssh.com") == 0 &&
512		    strcmp((char *)value, "1") == 0) {
513			ret->exts |= SFTP_EXT_LSETSTAT;
514			known = 1;
515		} else if (strcmp(name, "limits@openssh.com") == 0 &&
516		    strcmp((char *)value, "1") == 0) {
517			ret->exts |= SFTP_EXT_LIMITS;
518			known = 1;
519		} else if (strcmp(name, "expand-path@openssh.com") == 0 &&
520		    strcmp((char *)value, "1") == 0) {
521			ret->exts |= SFTP_EXT_PATH_EXPAND;
522			known = 1;
523		}
524		if (known) {
525			debug2("Server supports extension \"%s\" revision %s",
526			    name, value);
527		} else {
528			debug2("Unrecognised server extension \"%s\"", name);
529		}
530		free(name);
531		free(value);
532	}
533
534	sshbuf_free(msg);
535
536	/* Query the server for its limits */
537	if (ret->exts & SFTP_EXT_LIMITS) {
538		struct sftp_limits limits;
539		if (do_limits(ret, &limits) != 0)
540			fatal_f("limits failed");
541
542		/* If the caller did not specify, find a good value */
543		if (transfer_buflen == 0) {
544			ret->download_buflen = limits.read_length;
545			ret->upload_buflen = limits.write_length;
546			debug("Using server download size %u", ret->download_buflen);
547			debug("Using server upload size %u", ret->upload_buflen);
548		}
549
550		/* Use the server limit to scale down our value only */
551		if (num_requests == 0 && limits.open_handles) {
552			ret->num_requests =
553			    MINIMUM(DEFAULT_NUM_REQUESTS, limits.open_handles);
554			debug("Server handle limit %llu; using %u",
555			    (unsigned long long)limits.open_handles,
556			    ret->num_requests);
557		}
558	}
559
560	/* Some filexfer v.0 servers don't support large packets */
561	if (ret->version == 0) {
562		ret->download_buflen = MINIMUM(ret->download_buflen, 20480);
563		ret->upload_buflen = MINIMUM(ret->upload_buflen, 20480);
564	}
565
566	ret->limit_kbps = limit_kbps;
567	if (ret->limit_kbps > 0) {
568		bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
569		    ret->download_buflen);
570		bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
571		    ret->upload_buflen);
572	}
573
574	return ret;
575}
576
577u_int
578sftp_proto_version(struct sftp_conn *conn)
579{
580	return conn->version;
581}
582
583int
584do_limits(struct sftp_conn *conn, struct sftp_limits *limits)
585{
586	u_int id, msg_id;
587	u_char type;
588	struct sshbuf *msg;
589	int r;
590
591	if ((conn->exts & SFTP_EXT_LIMITS) == 0) {
592		error("Server does not support limits@openssh.com extension");
593		return -1;
594	}
595
596	if ((msg = sshbuf_new()) == NULL)
597		fatal_f("sshbuf_new failed");
598
599	id = conn->msg_id++;
600	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
601	    (r = sshbuf_put_u32(msg, id)) != 0 ||
602	    (r = sshbuf_put_cstring(msg, "limits@openssh.com")) != 0)
603		fatal_fr(r, "compose");
604	send_msg(conn, msg);
605	debug3("Sent message limits@openssh.com I:%u", id);
606
607	get_msg(conn, msg);
608
609	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
610	    (r = sshbuf_get_u32(msg, &msg_id)) != 0)
611		fatal_fr(r, "parse");
612
613	debug3("Received limits reply T:%u I:%u", type, msg_id);
614	if (id != msg_id)
615		fatal("ID mismatch (%u != %u)", msg_id, id);
616	if (type != SSH2_FXP_EXTENDED_REPLY) {
617		debug_f("expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
618		    SSH2_FXP_EXTENDED_REPLY, type);
619		/* Disable the limits extension */
620		conn->exts &= ~SFTP_EXT_LIMITS;
621		sshbuf_free(msg);
622		return 0;
623	}
624
625	memset(limits, 0, sizeof(*limits));
626	if ((r = sshbuf_get_u64(msg, &limits->packet_length)) != 0 ||
627	    (r = sshbuf_get_u64(msg, &limits->read_length)) != 0 ||
628	    (r = sshbuf_get_u64(msg, &limits->write_length)) != 0 ||
629	    (r = sshbuf_get_u64(msg, &limits->open_handles)) != 0)
630		fatal_fr(r, "parse limits");
631
632	sshbuf_free(msg);
633
634	return 0;
635}
636
637int
638do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
639{
640	u_int id, status;
641	struct sshbuf *msg;
642	int r;
643
644	if ((msg = sshbuf_new()) == NULL)
645		fatal_f("sshbuf_new failed");
646
647	id = conn->msg_id++;
648	if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 ||
649	    (r = sshbuf_put_u32(msg, id)) != 0 ||
650	    (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
651		fatal_fr(r, "parse");
652	send_msg(conn, msg);
653	debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
654
655	status = get_status(conn, id);
656	if (status != SSH2_FX_OK)
657		error("Couldn't close file: %s", fx2txt(status));
658
659	sshbuf_free(msg);
660
661	return status == SSH2_FX_OK ? 0 : -1;
662}
663
664
665static int
666do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
667    SFTP_DIRENT ***dir)
668{
669	struct sshbuf *msg;
670	u_int count, id, i, expected_id, ents = 0;
671	size_t handle_len;
672	u_char type, *handle;
673	int status = SSH2_FX_FAILURE;
674	int r;
675
676	if (dir)
677		*dir = NULL;
678
679	id = conn->msg_id++;
680
681	if ((msg = sshbuf_new()) == NULL)
682		fatal_f("sshbuf_new failed");
683	if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 ||
684	    (r = sshbuf_put_u32(msg, id)) != 0 ||
685	    (r = sshbuf_put_cstring(msg, path)) != 0)
686		fatal_fr(r, "compose OPENDIR");
687	send_msg(conn, msg);
688
689	handle = get_handle(conn, id, &handle_len,
690	    "remote readdir(\"%s\")", path);
691	if (handle == NULL) {
692		sshbuf_free(msg);
693		return -1;
694	}
695
696	if (dir) {
697		ents = 0;
698		*dir = xcalloc(1, sizeof(**dir));
699		(*dir)[0] = NULL;
700	}
701
702	for (; !interrupted;) {
703		id = expected_id = conn->msg_id++;
704
705		debug3("Sending SSH2_FXP_READDIR I:%u", id);
706
707		sshbuf_reset(msg);
708		if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 ||
709		    (r = sshbuf_put_u32(msg, id)) != 0 ||
710		    (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
711			fatal_fr(r, "compose READDIR");
712		send_msg(conn, msg);
713
714		sshbuf_reset(msg);
715
716		get_msg(conn, msg);
717
718		if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
719		    (r = sshbuf_get_u32(msg, &id)) != 0)
720			fatal_fr(r, "parse");
721
722		debug3("Received reply T:%u I:%u", type, id);
723
724		if (id != expected_id)
725			fatal("ID mismatch (%u != %u)", id, expected_id);
726
727		if (type == SSH2_FXP_STATUS) {
728			u_int rstatus;
729
730			if ((r = sshbuf_get_u32(msg, &rstatus)) != 0)
731				fatal_fr(r, "parse status");
732			debug3("Received SSH2_FXP_STATUS %d", rstatus);
733			if (rstatus == SSH2_FX_EOF)
734				break;
735			error("Couldn't read directory: %s", fx2txt(rstatus));
736			goto out;
737		} else if (type != SSH2_FXP_NAME)
738			fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
739			    SSH2_FXP_NAME, type);
740
741		if ((r = sshbuf_get_u32(msg, &count)) != 0)
742			fatal_fr(r, "parse count");
743		if (count > SSHBUF_SIZE_MAX)
744			fatal_f("nonsensical number of entries");
745		if (count == 0)
746			break;
747		debug3("Received %d SSH2_FXP_NAME responses", count);
748		for (i = 0; i < count; i++) {
749			char *filename, *longname;
750			Attrib a;
751
752			if ((r = sshbuf_get_cstring(msg, &filename,
753			    NULL)) != 0 ||
754			    (r = sshbuf_get_cstring(msg, &longname,
755			    NULL)) != 0)
756				fatal_fr(r, "parse filenames");
757			if ((r = decode_attrib(msg, &a)) != 0) {
758				error_fr(r, "couldn't decode attrib");
759				free(filename);
760				free(longname);
761				goto out;
762			}
763
764			if (print_flag)
765				mprintf("%s\n", longname);
766
767			/*
768			 * Directory entries should never contain '/'
769			 * These can be used to attack recursive ops
770			 * (e.g. send '../../../../etc/passwd')
771			 */
772			if (strchr(filename, '/') != NULL) {
773				error("Server sent suspect path \"%s\" "
774				    "during readdir of \"%s\"", filename, path);
775			} else if (dir) {
776				*dir = xreallocarray(*dir, ents + 2, sizeof(**dir));
777				(*dir)[ents] = xcalloc(1, sizeof(***dir));
778				(*dir)[ents]->filename = xstrdup(filename);
779				(*dir)[ents]->longname = xstrdup(longname);
780				memcpy(&(*dir)[ents]->a, &a, sizeof(a));
781				(*dir)[++ents] = NULL;
782			}
783			free(filename);
784			free(longname);
785		}
786	}
787	status = 0;
788
789 out:
790	sshbuf_free(msg);
791	do_close(conn, handle, handle_len);
792	free(handle);
793
794	if (status != 0 && dir != NULL) {
795		/* Don't return results on error */
796		free_sftp_dirents(*dir);
797		*dir = NULL;
798	} else if (interrupted && dir != NULL && *dir != NULL) {
799		/* Don't return partial matches on interrupt */
800		free_sftp_dirents(*dir);
801		*dir = xcalloc(1, sizeof(**dir));
802		**dir = NULL;
803	}
804
805	return status == SSH2_FX_OK ? 0 : -1;
806}
807
808int
809do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir)
810{
811	return(do_lsreaddir(conn, path, 0, dir));
812}
813
814void free_sftp_dirents(SFTP_DIRENT **s)
815{
816	int i;
817
818	if (s == NULL)
819		return;
820	for (i = 0; s[i]; i++) {
821		free(s[i]->filename);
822		free(s[i]->longname);
823		free(s[i]);
824	}
825	free(s);
826}
827
828int
829do_rm(struct sftp_conn *conn, const char *path)
830{
831	u_int status, id;
832
833	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
834
835	id = conn->msg_id++;
836	send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
837	status = get_status(conn, id);
838	if (status != SSH2_FX_OK)
839		error("Couldn't delete file: %s", fx2txt(status));
840	return status == SSH2_FX_OK ? 0 : -1;
841}
842
843int
844do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
845{
846	u_int status, id;
847
848	id = conn->msg_id++;
849	send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
850	    strlen(path), a);
851
852	status = get_status(conn, id);
853	if (status != SSH2_FX_OK && print_flag)
854		error("Couldn't create directory: %s", fx2txt(status));
855
856	return status == SSH2_FX_OK ? 0 : -1;
857}
858
859int
860do_rmdir(struct sftp_conn *conn, const char *path)
861{
862	u_int status, id;
863
864	id = conn->msg_id++;
865	send_string_request(conn, id, SSH2_FXP_RMDIR, path,
866	    strlen(path));
867
868	status = get_status(conn, id);
869	if (status != SSH2_FX_OK)
870		error("Couldn't remove directory: %s", fx2txt(status));
871
872	return status == SSH2_FX_OK ? 0 : -1;
873}
874
875Attrib *
876do_stat(struct sftp_conn *conn, const char *path, int quiet)
877{
878	u_int id;
879
880	id = conn->msg_id++;
881
882	send_string_request(conn, id,
883	    conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
884	    path, strlen(path));
885
886	return(get_decode_stat(conn, id, quiet));
887}
888
889Attrib *
890do_lstat(struct sftp_conn *conn, const char *path, int quiet)
891{
892	u_int id;
893
894	if (conn->version == 0) {
895		if (quiet)
896			debug("Server version does not support lstat operation");
897		else
898			logit("Server version does not support lstat operation");
899		return(do_stat(conn, path, quiet));
900	}
901
902	id = conn->msg_id++;
903	send_string_request(conn, id, SSH2_FXP_LSTAT, path,
904	    strlen(path));
905
906	return(get_decode_stat(conn, id, quiet));
907}
908
909#ifdef notyet
910Attrib *
911do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
912    int quiet)
913{
914	u_int id;
915
916	id = conn->msg_id++;
917	send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
918	    handle_len);
919
920	return(get_decode_stat(conn, id, quiet));
921}
922#endif
923
924int
925do_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
926{
927	u_int status, id;
928
929	id = conn->msg_id++;
930	send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
931	    strlen(path), a);
932
933	status = get_status(conn, id);
934	if (status != SSH2_FX_OK)
935		error("Couldn't setstat on \"%s\": %s", path,
936		    fx2txt(status));
937
938	return status == SSH2_FX_OK ? 0 : -1;
939}
940
941int
942do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
943    Attrib *a)
944{
945	u_int status, id;
946
947	id = conn->msg_id++;
948	send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
949	    handle_len, a);
950
951	status = get_status(conn, id);
952	if (status != SSH2_FX_OK)
953		error("Couldn't fsetstat: %s", fx2txt(status));
954
955	return status == SSH2_FX_OK ? 0 : -1;
956}
957
958/* Implements both the realpath and expand-path operations */
959static char *
960do_realpath_expand(struct sftp_conn *conn, const char *path, int expand)
961{
962	struct sshbuf *msg;
963	u_int expected_id, count, id;
964	char *filename, *longname;
965	Attrib a;
966	u_char type;
967	int r;
968	const char *what = "SSH2_FXP_REALPATH";
969
970	if (expand)
971		what = "expand-path@openssh.com";
972	if ((msg = sshbuf_new()) == NULL)
973		fatal_f("sshbuf_new failed");
974
975	expected_id = id = conn->msg_id++;
976	if (expand) {
977		if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
978		    (r = sshbuf_put_u32(msg, id)) != 0 ||
979		    (r = sshbuf_put_cstring(msg,
980		    "expand-path@openssh.com")) != 0 ||
981		    (r = sshbuf_put_cstring(msg, path)) != 0)
982			fatal_fr(r, "compose %s", what);
983		send_msg(conn, msg);
984	} else {
985		send_string_request(conn, id, SSH2_FXP_REALPATH,
986		    path, strlen(path));
987	}
988	get_msg(conn, msg);
989	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
990	    (r = sshbuf_get_u32(msg, &id)) != 0)
991		fatal_fr(r, "parse");
992
993	if (id != expected_id)
994		fatal("ID mismatch (%u != %u)", id, expected_id);
995
996	if (type == SSH2_FXP_STATUS) {
997		u_int status;
998
999		if ((r = sshbuf_get_u32(msg, &status)) != 0)
1000			fatal_fr(r, "parse status");
1001		error("Couldn't canonicalize: %s", fx2txt(status));
1002		sshbuf_free(msg);
1003		return NULL;
1004	} else if (type != SSH2_FXP_NAME)
1005		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1006		    SSH2_FXP_NAME, type);
1007
1008	if ((r = sshbuf_get_u32(msg, &count)) != 0)
1009		fatal_fr(r, "parse count");
1010	if (count != 1)
1011		fatal("Got multiple names (%d) from %s", count, what);
1012
1013	if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
1014	    (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
1015	    (r = decode_attrib(msg, &a)) != 0)
1016		fatal_fr(r, "parse filename/attrib");
1017
1018	debug3("%s %s -> %s", what, path, filename);
1019
1020	free(longname);
1021
1022	sshbuf_free(msg);
1023
1024	return(filename);
1025}
1026
1027char *
1028do_realpath(struct sftp_conn *conn, const char *path)
1029{
1030	return do_realpath_expand(conn, path, 0);
1031}
1032
1033int
1034can_expand_path(struct sftp_conn *conn)
1035{
1036	return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0;
1037}
1038
1039char *
1040do_expand_path(struct sftp_conn *conn, const char *path)
1041{
1042	if (!can_expand_path(conn)) {
1043		debug3_f("no server support, fallback to realpath");
1044		return do_realpath_expand(conn, path, 0);
1045	}
1046	return do_realpath_expand(conn, path, 1);
1047}
1048
1049int
1050do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
1051    int force_legacy)
1052{
1053	struct sshbuf *msg;
1054	u_int status, id;
1055	int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
1056
1057	if ((msg = sshbuf_new()) == NULL)
1058		fatal_f("sshbuf_new failed");
1059
1060	/* Send rename request */
1061	id = conn->msg_id++;
1062	if (use_ext) {
1063		if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1064		    (r = sshbuf_put_u32(msg, id)) != 0 ||
1065		    (r = sshbuf_put_cstring(msg,
1066		    "posix-rename@openssh.com")) != 0)
1067			fatal_fr(r, "compose posix-rename");
1068	} else {
1069		if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 ||
1070		    (r = sshbuf_put_u32(msg, id)) != 0)
1071			fatal_fr(r, "compose rename");
1072	}
1073	if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1074	    (r = sshbuf_put_cstring(msg, newpath)) != 0)
1075		fatal_fr(r, "compose paths");
1076	send_msg(conn, msg);
1077	debug3("Sent message %s \"%s\" -> \"%s\"",
1078	    use_ext ? "posix-rename@openssh.com" :
1079	    "SSH2_FXP_RENAME", oldpath, newpath);
1080	sshbuf_free(msg);
1081
1082	status = get_status(conn, id);
1083	if (status != SSH2_FX_OK)
1084		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
1085		    newpath, fx2txt(status));
1086
1087	return status == SSH2_FX_OK ? 0 : -1;
1088}
1089
1090int
1091do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
1092{
1093	struct sshbuf *msg;
1094	u_int status, id;
1095	int r;
1096
1097	if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
1098		error("Server does not support hardlink@openssh.com extension");
1099		return -1;
1100	}
1101
1102	if ((msg = sshbuf_new()) == NULL)
1103		fatal_f("sshbuf_new failed");
1104
1105	/* Send link request */
1106	id = conn->msg_id++;
1107	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1108	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1109	    (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 ||
1110	    (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1111	    (r = sshbuf_put_cstring(msg, newpath)) != 0)
1112		fatal_fr(r, "compose");
1113	send_msg(conn, msg);
1114	debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
1115	    oldpath, newpath);
1116	sshbuf_free(msg);
1117
1118	status = get_status(conn, id);
1119	if (status != SSH2_FX_OK)
1120		error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
1121		    newpath, fx2txt(status));
1122
1123	return status == SSH2_FX_OK ? 0 : -1;
1124}
1125
1126int
1127do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
1128{
1129	struct sshbuf *msg;
1130	u_int status, id;
1131	int r;
1132
1133	if (conn->version < 3) {
1134		error("This server does not support the symlink operation");
1135		return(SSH2_FX_OP_UNSUPPORTED);
1136	}
1137
1138	if ((msg = sshbuf_new()) == NULL)
1139		fatal_f("sshbuf_new failed");
1140
1141	/* Send symlink request */
1142	id = conn->msg_id++;
1143	if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 ||
1144	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1145	    (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1146	    (r = sshbuf_put_cstring(msg, newpath)) != 0)
1147		fatal_fr(r, "compose");
1148	send_msg(conn, msg);
1149	debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
1150	    newpath);
1151	sshbuf_free(msg);
1152
1153	status = get_status(conn, id);
1154	if (status != SSH2_FX_OK)
1155		error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
1156		    newpath, fx2txt(status));
1157
1158	return status == SSH2_FX_OK ? 0 : -1;
1159}
1160
1161int
1162do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
1163{
1164	struct sshbuf *msg;
1165	u_int status, id;
1166	int r;
1167
1168	/* Silently return if the extension is not supported */
1169	if ((conn->exts & SFTP_EXT_FSYNC) == 0)
1170		return -1;
1171
1172	/* Send fsync request */
1173	if ((msg = sshbuf_new()) == NULL)
1174		fatal_f("sshbuf_new failed");
1175	id = conn->msg_id++;
1176	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1177	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1178	    (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
1179	    (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1180		fatal_fr(r, "compose");
1181	send_msg(conn, msg);
1182	debug3("Sent message fsync@openssh.com I:%u", id);
1183	sshbuf_free(msg);
1184
1185	status = get_status(conn, id);
1186	if (status != SSH2_FX_OK)
1187		error("Couldn't sync file: %s", fx2txt(status));
1188
1189	return status == SSH2_FX_OK ? 0 : -1;
1190}
1191
1192#ifdef notyet
1193char *
1194do_readlink(struct sftp_conn *conn, const char *path)
1195{
1196	struct sshbuf *msg;
1197	u_int expected_id, count, id;
1198	char *filename, *longname;
1199	Attrib a;
1200	u_char type;
1201	int r;
1202
1203	expected_id = id = conn->msg_id++;
1204	send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
1205
1206	if ((msg = sshbuf_new()) == NULL)
1207		fatal_f("sshbuf_new failed");
1208
1209	get_msg(conn, msg);
1210	if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1211	    (r = sshbuf_get_u32(msg, &id)) != 0)
1212		fatal_fr(r, "parse");
1213
1214	if (id != expected_id)
1215		fatal("ID mismatch (%u != %u)", id, expected_id);
1216
1217	if (type == SSH2_FXP_STATUS) {
1218		u_int status;
1219
1220		if ((r = sshbuf_get_u32(msg, &status)) != 0)
1221			fatal_fr(r, "parse status");
1222		error("Couldn't readlink: %s", fx2txt(status));
1223		sshbuf_free(msg);
1224		return(NULL);
1225	} else if (type != SSH2_FXP_NAME)
1226		fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1227		    SSH2_FXP_NAME, type);
1228
1229	if ((r = sshbuf_get_u32(msg, &count)) != 0)
1230		fatal_fr(r, "parse count");
1231	if (count != 1)
1232		fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
1233
1234	if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
1235	    (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
1236	    (r = decode_attrib(msg, &a)) != 0)
1237		fatal_fr(r, "parse filenames/attrib");
1238
1239	debug3("SSH_FXP_READLINK %s -> %s", path, filename);
1240
1241	free(longname);
1242
1243	sshbuf_free(msg);
1244
1245	return filename;
1246}
1247#endif
1248
1249int
1250do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
1251    int quiet)
1252{
1253	struct sshbuf *msg;
1254	u_int id;
1255	int r;
1256
1257	if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
1258		error("Server does not support statvfs@openssh.com extension");
1259		return -1;
1260	}
1261
1262	id = conn->msg_id++;
1263
1264	if ((msg = sshbuf_new()) == NULL)
1265		fatal_f("sshbuf_new failed");
1266	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1267	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1268	    (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
1269	    (r = sshbuf_put_cstring(msg, path)) != 0)
1270		fatal_fr(r, "compose");
1271	send_msg(conn, msg);
1272	sshbuf_free(msg);
1273
1274	return get_decode_statvfs(conn, st, id, quiet);
1275}
1276
1277#ifdef notyet
1278int
1279do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
1280    struct sftp_statvfs *st, int quiet)
1281{
1282	struct sshbuf *msg;
1283	u_int id;
1284
1285	if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
1286		error("Server does not support fstatvfs@openssh.com extension");
1287		return -1;
1288	}
1289
1290	id = conn->msg_id++;
1291
1292	if ((msg = sshbuf_new()) == NULL)
1293		fatal_f("sshbuf_new failed");
1294	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1295	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1296	    (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
1297	    (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1298		fatal_fr(r, "compose");
1299	send_msg(conn, msg);
1300	sshbuf_free(msg);
1301
1302	return get_decode_statvfs(conn, st, id, quiet);
1303}
1304#endif
1305
1306int
1307do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a)
1308{
1309	struct sshbuf *msg;
1310	u_int status, id;
1311	int r;
1312
1313	if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) {
1314		error("Server does not support lsetstat@openssh.com extension");
1315		return -1;
1316	}
1317
1318	id = conn->msg_id++;
1319	if ((msg = sshbuf_new()) == NULL)
1320		fatal_f("sshbuf_new failed");
1321	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1322	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1323	    (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 ||
1324	    (r = sshbuf_put_cstring(msg, path)) != 0 ||
1325	    (r = encode_attrib(msg, a)) != 0)
1326		fatal_fr(r, "compose");
1327	send_msg(conn, msg);
1328	sshbuf_free(msg);
1329
1330	status = get_status(conn, id);
1331	if (status != SSH2_FX_OK)
1332		error("Couldn't setstat on \"%s\": %s", path,
1333		    fx2txt(status));
1334
1335	return status == SSH2_FX_OK ? 0 : -1;
1336}
1337
1338static void
1339send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1340    u_int len, const u_char *handle, u_int handle_len)
1341{
1342	struct sshbuf *msg;
1343	int r;
1344
1345	if ((msg = sshbuf_new()) == NULL)
1346		fatal_f("sshbuf_new failed");
1347	if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 ||
1348	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1349	    (r = sshbuf_put_string(msg, handle, handle_len)) != 0 ||
1350	    (r = sshbuf_put_u64(msg, offset)) != 0 ||
1351	    (r = sshbuf_put_u32(msg, len)) != 0)
1352		fatal_fr(r, "compose");
1353	send_msg(conn, msg);
1354	sshbuf_free(msg);
1355}
1356
1357static int
1358send_open(struct sftp_conn *conn, const char *path, const char *tag,
1359    u_int openmode, Attrib *a, u_char **handlep, size_t *handle_lenp)
1360{
1361	Attrib junk;
1362	u_char *handle;
1363	size_t handle_len;
1364	struct sshbuf *msg;
1365	int r;
1366	u_int id;
1367
1368	*handlep = NULL;
1369	*handle_lenp = 0;
1370
1371	if (a == NULL) {
1372		attrib_clear(&junk); /* Send empty attributes */
1373		a = &junk;
1374	}
1375	/* Send open request */
1376	if ((msg = sshbuf_new()) == NULL)
1377		fatal_f("sshbuf_new failed");
1378	id = conn->msg_id++;
1379	if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
1380	    (r = sshbuf_put_u32(msg, id)) != 0 ||
1381	    (r = sshbuf_put_cstring(msg, path)) != 0 ||
1382	    (r = sshbuf_put_u32(msg, openmode)) != 0 ||
1383	    (r = encode_attrib(msg, a)) != 0)
1384		fatal_fr(r, "compose %s open", tag);
1385	send_msg(conn, msg);
1386	sshbuf_free(msg);
1387	debug3("Sent %s message SSH2_FXP_OPEN I:%u P:%s M:0x%04x",
1388	    tag, id, path, openmode);
1389	if ((handle = get_handle(conn, id, &handle_len,
1390	    "%s open(\"%s\")", tag, path)) == NULL)
1391		return -1;
1392	/* success */
1393	*handlep = handle;
1394	*handle_lenp = handle_len;
1395	return 0;
1396}
1397
1398static const char *
1399progress_meter_path(const char *path)
1400{
1401	const char *progresspath;
1402
1403	if ((progresspath = strrchr(path, '/')) == NULL)
1404		return path;
1405	progresspath++;
1406	if (*progresspath == '\0')
1407		return path;
1408	return progresspath;
1409}
1410
1411int
1412do_download(struct sftp_conn *conn, const char *remote_path,
1413    const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
1414    int fsync_flag)
1415{
1416	struct sshbuf *msg;
1417	u_char *handle;
1418	int local_fd = -1, write_error;
1419	int read_error, write_errno, lmodified = 0, reordered = 0, r;
1420	u_int64_t offset = 0, size, highwater;
1421	u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK;
1422	off_t progress_counter;
1423	size_t handle_len;
1424	struct stat st;
1425	struct requests requests;
1426	struct request *req;
1427	u_char type;
1428
1429	status = -1;
1430	TAILQ_INIT(&requests);
1431
1432	if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1433		return -1;
1434
1435	/* Do not preserve set[ug]id here, as we do not preserve ownership */
1436	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1437		mode = a->perm & 0777;
1438	else
1439		mode = 0666;
1440
1441	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1442	    (!S_ISREG(a->perm))) {
1443		error("Cannot download non-regular file: %s", remote_path);
1444		return(-1);
1445	}
1446
1447	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1448		size = a->size;
1449	else
1450		size = 0;
1451
1452	buflen = conn->download_buflen;
1453
1454	/* Send open request */
1455	if (send_open(conn, remote_path, "remote", SSH2_FXF_READ, NULL,
1456	    &handle, &handle_len) != 0)
1457		return -1;
1458
1459	local_fd = open(local_path,
1460	    O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
1461	if (local_fd == -1) {
1462		error("Couldn't open local file \"%s\" for writing: %s",
1463		    local_path, strerror(errno));
1464		goto fail;
1465	}
1466	offset = highwater = 0;
1467	if (resume_flag) {
1468		if (fstat(local_fd, &st) == -1) {
1469			error("Unable to stat local file \"%s\": %s",
1470			    local_path, strerror(errno));
1471			goto fail;
1472		}
1473		if (st.st_size < 0) {
1474			error("\"%s\" has negative size", local_path);
1475			goto fail;
1476		}
1477		if ((u_int64_t)st.st_size > size) {
1478			error("Unable to resume download of \"%s\": "
1479			    "local file is larger than remote", local_path);
1480 fail:
1481			do_close(conn, handle, handle_len);
1482			free(handle);
1483			if (local_fd != -1)
1484				close(local_fd);
1485			return -1;
1486		}
1487		offset = highwater = st.st_size;
1488	}
1489
1490	/* Read from remote and write to local */
1491	write_error = read_error = write_errno = num_req = 0;
1492	max_req = 1;
1493	progress_counter = offset;
1494
1495	if (showprogress && size != 0) {
1496		start_progress_meter(progress_meter_path(remote_path),
1497		    size, &progress_counter);
1498	}
1499
1500	if ((msg = sshbuf_new()) == NULL)
1501		fatal_f("sshbuf_new failed");
1502
1503	while (num_req > 0 || max_req > 0) {
1504		u_char *data;
1505		size_t len;
1506
1507		/*
1508		 * Simulate EOF on interrupt: stop sending new requests and
1509		 * allow outstanding requests to drain gracefully
1510		 */
1511		if (interrupted) {
1512			if (num_req == 0) /* If we haven't started yet... */
1513				break;
1514			max_req = 0;
1515		}
1516
1517		/* Send some more requests */
1518		while (num_req < max_req) {
1519			debug3("Request range %llu -> %llu (%d/%d)",
1520			    (unsigned long long)offset,
1521			    (unsigned long long)offset + buflen - 1,
1522			    num_req, max_req);
1523			req = request_enqueue(&requests, conn->msg_id++,
1524			    buflen, offset);
1525			offset += buflen;
1526			num_req++;
1527			send_read_request(conn, req->id, req->offset,
1528			    req->len, handle, handle_len);
1529		}
1530
1531		sshbuf_reset(msg);
1532		get_msg(conn, msg);
1533		if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1534		    (r = sshbuf_get_u32(msg, &id)) != 0)
1535			fatal_fr(r, "parse");
1536		debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1537
1538		/* Find the request in our queue */
1539		if ((req = request_find(&requests, id)) == NULL)
1540			fatal("Unexpected reply %u", id);
1541
1542		switch (type) {
1543		case SSH2_FXP_STATUS:
1544			if ((r = sshbuf_get_u32(msg, &status)) != 0)
1545				fatal_fr(r, "parse status");
1546			if (status != SSH2_FX_EOF)
1547				read_error = 1;
1548			max_req = 0;
1549			TAILQ_REMOVE(&requests, req, tq);
1550			free(req);
1551			num_req--;
1552			break;
1553		case SSH2_FXP_DATA:
1554			if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
1555				fatal_fr(r, "parse data");
1556			debug3("Received data %llu -> %llu",
1557			    (unsigned long long)req->offset,
1558			    (unsigned long long)req->offset + len - 1);
1559			if (len > req->len)
1560				fatal("Received more data than asked for "
1561				    "%zu > %zu", len, req->len);
1562			lmodified = 1;
1563			if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1564			    atomicio(vwrite, local_fd, data, len) != len) &&
1565			    !write_error) {
1566				write_errno = errno;
1567				write_error = 1;
1568				max_req = 0;
1569			}
1570			else if (!reordered && req->offset <= highwater)
1571				highwater = req->offset + len;
1572			else if (!reordered && req->offset > highwater)
1573				reordered = 1;
1574			progress_counter += len;
1575			free(data);
1576
1577			if (len == req->len) {
1578				TAILQ_REMOVE(&requests, req, tq);
1579				free(req);
1580				num_req--;
1581			} else {
1582				/* Resend the request for the missing data */
1583				debug3("Short data block, re-requesting "
1584				    "%llu -> %llu (%2d)",
1585				    (unsigned long long)req->offset + len,
1586				    (unsigned long long)req->offset +
1587				    req->len - 1, num_req);
1588				req->id = conn->msg_id++;
1589				req->len -= len;
1590				req->offset += len;
1591				send_read_request(conn, req->id,
1592				    req->offset, req->len, handle, handle_len);
1593				/* Reduce the request size */
1594				if (len < buflen)
1595					buflen = MAXIMUM(MIN_READ_SIZE, len);
1596			}
1597			if (max_req > 0) { /* max_req = 0 iff EOF received */
1598				if (size > 0 && offset > size) {
1599					/* Only one request at a time
1600					 * after the expected EOF */
1601					debug3("Finish at %llu (%2d)",
1602					    (unsigned long long)offset,
1603					    num_req);
1604					max_req = 1;
1605				} else if (max_req < conn->num_requests) {
1606					++max_req;
1607				}
1608			}
1609			break;
1610		default:
1611			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1612			    SSH2_FXP_DATA, type);
1613		}
1614	}
1615
1616	if (showprogress && size)
1617		stop_progress_meter();
1618
1619	/* Sanity check */
1620	if (TAILQ_FIRST(&requests) != NULL)
1621		fatal("Transfer complete, but requests still in queue");
1622	/* Truncate at highest contiguous point to avoid holes on interrupt */
1623	if (read_error || write_error || interrupted) {
1624		if (reordered && resume_flag) {
1625			error("Unable to resume download of \"%s\": "
1626			    "server reordered requests", local_path);
1627		}
1628		debug("truncating at %llu", (unsigned long long)highwater);
1629		if (ftruncate(local_fd, highwater) == -1)
1630			error("ftruncate \"%s\": %s", local_path,
1631			    strerror(errno));
1632	}
1633	if (read_error) {
1634		error("Couldn't read from remote file \"%s\" : %s",
1635		    remote_path, fx2txt(status));
1636		status = -1;
1637		do_close(conn, handle, handle_len);
1638	} else if (write_error) {
1639		error("Couldn't write to \"%s\": %s", local_path,
1640		    strerror(write_errno));
1641		status = SSH2_FX_FAILURE;
1642		do_close(conn, handle, handle_len);
1643	} else {
1644		if (do_close(conn, handle, handle_len) != 0 || interrupted)
1645			status = SSH2_FX_FAILURE;
1646		else
1647			status = SSH2_FX_OK;
1648		/* Override umask and utimes if asked */
1649		if (preserve_flag && fchmod(local_fd, mode) == -1)
1650			error("Couldn't set mode on \"%s\": %s", local_path,
1651			    strerror(errno));
1652		if (preserve_flag &&
1653		    (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1654			struct timeval tv[2];
1655			tv[0].tv_sec = a->atime;
1656			tv[1].tv_sec = a->mtime;
1657			tv[0].tv_usec = tv[1].tv_usec = 0;
1658			if (utimes(local_path, tv) == -1)
1659				error("Can't set times on \"%s\": %s",
1660				    local_path, strerror(errno));
1661		}
1662		if (resume_flag && !lmodified)
1663			logit("File \"%s\" was not modified", local_path);
1664		else if (fsync_flag) {
1665			debug("syncing \"%s\"", local_path);
1666			if (fsync(local_fd) == -1)
1667				error("Couldn't sync file \"%s\": %s",
1668				    local_path, strerror(errno));
1669		}
1670	}
1671	close(local_fd);
1672	sshbuf_free(msg);
1673	free(handle);
1674
1675	return status == SSH2_FX_OK ? 0 : -1;
1676}
1677
1678static int
1679download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1680    int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
1681    int resume_flag, int fsync_flag, int follow_link_flag)
1682{
1683	int i, ret = 0;
1684	SFTP_DIRENT **dir_entries;
1685	char *filename, *new_src = NULL, *new_dst = NULL;
1686	mode_t mode = 0777, tmpmode = mode;
1687
1688	if (depth >= MAX_DIR_DEPTH) {
1689		error("Maximum directory depth exceeded: %d levels", depth);
1690		return -1;
1691	}
1692
1693	if (dirattrib == NULL &&
1694	    (dirattrib = do_stat(conn, src, 1)) == NULL) {
1695		error("Unable to stat remote directory \"%s\"", src);
1696		return -1;
1697	}
1698	if (!S_ISDIR(dirattrib->perm)) {
1699		error("\"%s\" is not a directory", src);
1700		return -1;
1701	}
1702	if (print_flag && print_flag != SFTP_PROGRESS_ONLY)
1703		mprintf("Retrieving %s\n", src);
1704
1705	if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1706		mode = dirattrib->perm & 01777;
1707		tmpmode = mode | (S_IWUSR|S_IXUSR);
1708	} else {
1709		debug("Server did not send permissions for "
1710		    "directory \"%s\"", dst);
1711	}
1712
1713	if (mkdir(dst, tmpmode) == -1 && errno != EEXIST) {
1714		error("mkdir %s: %s", dst, strerror(errno));
1715		return -1;
1716	}
1717
1718	if (do_readdir(conn, src, &dir_entries) == -1) {
1719		error("%s: Failed to get directory contents", src);
1720		return -1;
1721	}
1722
1723	for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1724		free(new_dst);
1725		free(new_src);
1726
1727		filename = dir_entries[i]->filename;
1728		new_dst = path_append(dst, filename);
1729		new_src = path_append(src, filename);
1730
1731		if (S_ISDIR(dir_entries[i]->a.perm)) {
1732			if (strcmp(filename, ".") == 0 ||
1733			    strcmp(filename, "..") == 0)
1734				continue;
1735			if (download_dir_internal(conn, new_src, new_dst,
1736			    depth + 1, &(dir_entries[i]->a), preserve_flag,
1737			    print_flag, resume_flag,
1738			    fsync_flag, follow_link_flag) == -1)
1739				ret = -1;
1740		} else if (S_ISREG(dir_entries[i]->a.perm) ||
1741		    (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
1742			/*
1743			 * If this is a symlink then don't send the link's
1744			 * Attrib. do_download() will do a FXP_STAT operation
1745			 * and get the link target's attributes.
1746			 */
1747			if (do_download(conn, new_src, new_dst,
1748			    S_ISLNK(dir_entries[i]->a.perm) ? NULL :
1749			    &(dir_entries[i]->a),
1750			    preserve_flag, resume_flag, fsync_flag) == -1) {
1751				error("Download of file %s to %s failed",
1752				    new_src, new_dst);
1753				ret = -1;
1754			}
1755		} else
1756			logit("%s: not a regular file\n", new_src);
1757
1758	}
1759	free(new_dst);
1760	free(new_src);
1761
1762	if (preserve_flag) {
1763		if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1764			struct timeval tv[2];
1765			tv[0].tv_sec = dirattrib->atime;
1766			tv[1].tv_sec = dirattrib->mtime;
1767			tv[0].tv_usec = tv[1].tv_usec = 0;
1768			if (utimes(dst, tv) == -1)
1769				error("Can't set times on \"%s\": %s",
1770				    dst, strerror(errno));
1771		} else
1772			debug("Server did not send times for directory "
1773			    "\"%s\"", dst);
1774	}
1775
1776	if (mode != tmpmode && chmod(dst, mode) == -1)
1777		error("Can't set final mode on \"%s\": %s", dst,
1778		    strerror(errno));
1779
1780	free_sftp_dirents(dir_entries);
1781
1782	return ret;
1783}
1784
1785int
1786download_dir(struct sftp_conn *conn, const char *src, const char *dst,
1787    Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1788    int fsync_flag, int follow_link_flag)
1789{
1790	char *src_canon;
1791	int ret;
1792
1793	if ((src_canon = do_realpath(conn, src)) == NULL) {
1794		error("Unable to canonicalize path \"%s\"", src);
1795		return -1;
1796	}
1797
1798	ret = download_dir_internal(conn, src_canon, dst, 0,
1799	    dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag,
1800	    follow_link_flag);
1801	free(src_canon);
1802	return ret;
1803}
1804
1805int
1806do_upload(struct sftp_conn *conn, const char *local_path,
1807    const char *remote_path, int preserve_flag, int resume, int fsync_flag)
1808{
1809	int r, local_fd;
1810	u_int status = SSH2_FX_OK;
1811	u_int id;
1812	u_char type;
1813	off_t offset, progress_counter;
1814	u_char *handle, *data;
1815	struct sshbuf *msg;
1816	struct stat sb;
1817	Attrib a, *c = NULL;
1818	u_int32_t startid;
1819	u_int32_t ackid;
1820	struct request *ack = NULL;
1821	struct requests acks;
1822	size_t handle_len;
1823
1824	TAILQ_INIT(&acks);
1825
1826	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1827		error("Couldn't open local file \"%s\" for reading: %s",
1828		    local_path, strerror(errno));
1829		return(-1);
1830	}
1831	if (fstat(local_fd, &sb) == -1) {
1832		error("Couldn't fstat local file \"%s\": %s",
1833		    local_path, strerror(errno));
1834		close(local_fd);
1835		return(-1);
1836	}
1837	if (!S_ISREG(sb.st_mode)) {
1838		error("%s is not a regular file", local_path);
1839		close(local_fd);
1840		return(-1);
1841	}
1842	stat_to_attrib(&sb, &a);
1843
1844	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1845	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1846	a.perm &= 0777;
1847	if (!preserve_flag)
1848		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1849
1850	if (resume) {
1851		/* Get remote file size if it exists */
1852		if ((c = do_stat(conn, remote_path, 0)) == NULL) {
1853			close(local_fd);
1854			return -1;
1855		}
1856
1857		if ((off_t)c->size >= sb.st_size) {
1858			error("destination file bigger or same size as "
1859			    "source file");
1860			close(local_fd);
1861			return -1;
1862		}
1863
1864		if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) {
1865			close(local_fd);
1866			return -1;
1867		}
1868	}
1869
1870	/* Send open request */
1871	if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|
1872	    (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC),
1873	    &a, &handle, &handle_len) != 0) {
1874		close(local_fd);
1875		return -1;
1876	}
1877
1878	id = conn->msg_id;
1879	startid = ackid = id + 1;
1880	data = xmalloc(conn->upload_buflen);
1881
1882	/* Read from local and write to remote */
1883	offset = progress_counter = (resume ? c->size : 0);
1884	if (showprogress) {
1885		start_progress_meter(progress_meter_path(local_path),
1886		    sb.st_size, &progress_counter);
1887	}
1888
1889	if ((msg = sshbuf_new()) == NULL)
1890		fatal_f("sshbuf_new failed");
1891	for (;;) {
1892		int len;
1893
1894		/*
1895		 * Can't use atomicio here because it returns 0 on EOF,
1896		 * thus losing the last block of the file.
1897		 * Simulate an EOF on interrupt, allowing ACKs from the
1898		 * server to drain.
1899		 */
1900		if (interrupted || status != SSH2_FX_OK)
1901			len = 0;
1902		else do
1903			len = read(local_fd, data, conn->upload_buflen);
1904		while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1905
1906		if (len == -1)
1907			fatal("Couldn't read from \"%s\": %s", local_path,
1908			    strerror(errno));
1909
1910		if (len != 0) {
1911			ack = request_enqueue(&acks, ++id, len, offset);
1912			sshbuf_reset(msg);
1913			if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
1914			    (r = sshbuf_put_u32(msg, ack->id)) != 0 ||
1915			    (r = sshbuf_put_string(msg, handle,
1916			    handle_len)) != 0 ||
1917			    (r = sshbuf_put_u64(msg, offset)) != 0 ||
1918			    (r = sshbuf_put_string(msg, data, len)) != 0)
1919				fatal_fr(r, "compose");
1920			send_msg(conn, msg);
1921			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1922			    id, (unsigned long long)offset, len);
1923		} else if (TAILQ_FIRST(&acks) == NULL)
1924			break;
1925
1926		if (ack == NULL)
1927			fatal("Unexpected ACK %u", id);
1928
1929		if (id == startid || len == 0 ||
1930		    id - ackid >= conn->num_requests) {
1931			u_int rid;
1932
1933			sshbuf_reset(msg);
1934			get_msg(conn, msg);
1935			if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1936			    (r = sshbuf_get_u32(msg, &rid)) != 0)
1937				fatal_fr(r, "parse");
1938
1939			if (type != SSH2_FXP_STATUS)
1940				fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1941				    "got %d", SSH2_FXP_STATUS, type);
1942
1943			if ((r = sshbuf_get_u32(msg, &status)) != 0)
1944				fatal_fr(r, "parse status");
1945			debug3("SSH2_FXP_STATUS %u", status);
1946
1947			/* Find the request in our queue */
1948			if ((ack = request_find(&acks, rid)) == NULL)
1949				fatal("Can't find request for ID %u", rid);
1950			TAILQ_REMOVE(&acks, ack, tq);
1951			debug3("In write loop, ack for %u %zu bytes at %lld",
1952			    ack->id, ack->len, (unsigned long long)ack->offset);
1953			++ackid;
1954			progress_counter += ack->len;
1955			free(ack);
1956		}
1957		offset += len;
1958		if (offset < 0)
1959			fatal_f("offset < 0");
1960	}
1961	sshbuf_free(msg);
1962
1963	if (showprogress)
1964		stop_progress_meter();
1965	free(data);
1966
1967	if (status != SSH2_FX_OK) {
1968		error("Couldn't write to remote file \"%s\": %s",
1969		    remote_path, fx2txt(status));
1970		status = SSH2_FX_FAILURE;
1971	}
1972
1973	if (close(local_fd) == -1) {
1974		error("Couldn't close local file \"%s\": %s", local_path,
1975		    strerror(errno));
1976		status = SSH2_FX_FAILURE;
1977	}
1978
1979	/* Override umask and utimes if asked */
1980	if (preserve_flag)
1981		do_fsetstat(conn, handle, handle_len, &a);
1982
1983	if (fsync_flag)
1984		(void)do_fsync(conn, handle, handle_len);
1985
1986	if (do_close(conn, handle, handle_len) != 0)
1987		status = SSH2_FX_FAILURE;
1988
1989	free(handle);
1990
1991	return status == SSH2_FX_OK ? 0 : -1;
1992}
1993
1994static int
1995upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1996    int depth, int preserve_flag, int print_flag, int resume, int fsync_flag,
1997    int follow_link_flag)
1998{
1999	int ret = 0;
2000	DIR *dirp;
2001	struct dirent *dp;
2002	char *filename, *new_src = NULL, *new_dst = NULL;
2003	struct stat sb;
2004	Attrib a, *dirattrib;
2005	u_int32_t saved_perm;
2006
2007	if (depth >= MAX_DIR_DEPTH) {
2008		error("Maximum directory depth exceeded: %d levels", depth);
2009		return -1;
2010	}
2011
2012	if (stat(src, &sb) == -1) {
2013		error("Couldn't stat directory \"%s\": %s",
2014		    src, strerror(errno));
2015		return -1;
2016	}
2017	if (!S_ISDIR(sb.st_mode)) {
2018		error("\"%s\" is not a directory", src);
2019		return -1;
2020	}
2021	if (print_flag && print_flag != SFTP_PROGRESS_ONLY)
2022		mprintf("Entering %s\n", src);
2023
2024	stat_to_attrib(&sb, &a);
2025	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
2026	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
2027	a.perm &= 01777;
2028	if (!preserve_flag)
2029		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
2030
2031	/*
2032	 * sftp lacks a portable status value to match errno EEXIST,
2033	 * so if we get a failure back then we must check whether
2034	 * the path already existed and is a directory.  Ensure we can
2035	 * write to the directory we create for the duration of the transfer.
2036	 */
2037	saved_perm = a.perm;
2038	a.perm |= (S_IWUSR|S_IXUSR);
2039	if (do_mkdir(conn, dst, &a, 0) != 0) {
2040		if ((dirattrib = do_stat(conn, dst, 0)) == NULL)
2041			return -1;
2042		if (!S_ISDIR(dirattrib->perm)) {
2043			error("\"%s\" exists but is not a directory", dst);
2044			return -1;
2045		}
2046	}
2047	a.perm = saved_perm;
2048
2049	if ((dirp = opendir(src)) == NULL) {
2050		error("Failed to open dir \"%s\": %s", src, strerror(errno));
2051		return -1;
2052	}
2053
2054	while (((dp = readdir(dirp)) != NULL) && !interrupted) {
2055		if (dp->d_ino == 0)
2056			continue;
2057		free(new_dst);
2058		free(new_src);
2059		filename = dp->d_name;
2060		new_dst = path_append(dst, filename);
2061		new_src = path_append(src, filename);
2062
2063		if (lstat(new_src, &sb) == -1) {
2064			logit("%s: lstat failed: %s", filename,
2065			    strerror(errno));
2066			ret = -1;
2067		} else if (S_ISDIR(sb.st_mode)) {
2068			if (strcmp(filename, ".") == 0 ||
2069			    strcmp(filename, "..") == 0)
2070				continue;
2071
2072			if (upload_dir_internal(conn, new_src, new_dst,
2073			    depth + 1, preserve_flag, print_flag, resume,
2074			    fsync_flag, follow_link_flag) == -1)
2075				ret = -1;
2076		} else if (S_ISREG(sb.st_mode) ||
2077		    (follow_link_flag && S_ISLNK(sb.st_mode))) {
2078			if (do_upload(conn, new_src, new_dst,
2079			    preserve_flag, resume, fsync_flag) == -1) {
2080				error("Uploading of file %s to %s failed!",
2081				    new_src, new_dst);
2082				ret = -1;
2083			}
2084		} else
2085			logit("%s: not a regular file\n", filename);
2086	}
2087	free(new_dst);
2088	free(new_src);
2089
2090	do_setstat(conn, dst, &a);
2091
2092	(void) closedir(dirp);
2093	return ret;
2094}
2095
2096int
2097upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
2098    int preserve_flag, int print_flag, int resume, int fsync_flag,
2099    int follow_link_flag)
2100{
2101	char *dst_canon;
2102	int ret;
2103
2104	if ((dst_canon = do_realpath(conn, dst)) == NULL) {
2105		error("Unable to canonicalize path \"%s\"", dst);
2106		return -1;
2107	}
2108
2109	ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
2110	    print_flag, resume, fsync_flag, follow_link_flag);
2111
2112	free(dst_canon);
2113	return ret;
2114}
2115
2116static void
2117handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous,
2118    u_int *nreqsp, int *write_errorp)
2119{
2120	struct sshbuf *msg;
2121	u_char type;
2122	u_int id, status;
2123	int r;
2124	struct pollfd pfd;
2125
2126	if ((msg = sshbuf_new()) == NULL)
2127		fatal_f("sshbuf_new failed");
2128
2129	/* Try to eat replies from the upload side */
2130	while (*nreqsp > 0) {
2131		debug3_f("%u outstanding replies", *nreqsp);
2132		if (!synchronous) {
2133			/* Bail out if no data is ready to be read */
2134			pfd.fd = to->fd_in;
2135			pfd.events = POLLIN;
2136			if ((r = poll(&pfd, 1, 0)) == -1) {
2137				if (errno == EINTR)
2138					break;
2139				fatal_f("poll: %s", strerror(errno));
2140			} else if (r == 0)
2141				break; /* fd not ready */
2142		}
2143		sshbuf_reset(msg);
2144		get_msg(to, msg);
2145
2146		if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
2147		    (r = sshbuf_get_u32(msg, &id)) != 0)
2148			fatal_fr(r, "dest parse");
2149		debug3("Received dest reply T:%u I:%u R:%u", type, id, *nreqsp);
2150		if (type != SSH2_FXP_STATUS) {
2151			fatal_f("Expected SSH2_FXP_STATUS(%d) packet, got %d",
2152			    SSH2_FXP_STATUS, type);
2153		}
2154		if ((r = sshbuf_get_u32(msg, &status)) != 0)
2155			fatal_fr(r, "parse dest status");
2156		debug3("dest SSH2_FXP_STATUS %u", status);
2157		if (status != SSH2_FX_OK) {
2158			/* record first error */
2159			if (*write_errorp == 0)
2160				*write_errorp = status;
2161		}
2162		/*
2163		 * XXX this doesn't do full reply matching like do_upload and
2164		 * so cannot gracefully truncate terminated uploads at a
2165		 * high-water mark. ATM the only caller of this function (scp)
2166		 * doesn't support transfer resumption, so this doesn't matter
2167		 * a whole lot.
2168		 *
2169		 * To be safe, do_crossload truncates the destination file to
2170		 * zero length on upload failure, since we can't trust the
2171		 * server not to have reordered replies that could have
2172		 * inserted holes where none existed in the source file.
2173		 *
2174		 * XXX we could get a more accutate progress bar if we updated
2175		 * the counter based on the reply from the destination...
2176		 */
2177		(*nreqsp)--;
2178	}
2179	debug3_f("done: %u outstanding replies", *nreqsp);
2180}
2181
2182int
2183do_crossload(struct sftp_conn *from, struct sftp_conn *to,
2184    const char *from_path, const char *to_path,
2185    Attrib *a, int preserve_flag)
2186{
2187	struct sshbuf *msg;
2188	int write_error, read_error, r;
2189	u_int64_t offset = 0, size;
2190	u_int id, buflen, num_req, max_req, status = SSH2_FX_OK;
2191	u_int num_upload_req;
2192	off_t progress_counter;
2193	u_char *from_handle, *to_handle;
2194	size_t from_handle_len, to_handle_len;
2195	struct requests requests;
2196	struct request *req;
2197	u_char type;
2198
2199	TAILQ_INIT(&requests);
2200
2201	if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL)
2202		return -1;
2203
2204	if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
2205	    (!S_ISREG(a->perm))) {
2206		error("Cannot download non-regular file: %s", from_path);
2207		return(-1);
2208	}
2209	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
2210		size = a->size;
2211	else
2212		size = 0;
2213
2214	buflen = from->download_buflen;
2215	if (buflen > to->upload_buflen)
2216		buflen = to->upload_buflen;
2217
2218	/* Send open request to read side */
2219	if (send_open(from, from_path, "origin", SSH2_FXF_READ, NULL,
2220	    &from_handle, &from_handle_len) != 0)
2221		return -1;
2222
2223	/* Send open request to write side */
2224	a->flags &= ~SSH2_FILEXFER_ATTR_SIZE;
2225	a->flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
2226	a->perm &= 0777;
2227	if (!preserve_flag)
2228		a->flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
2229	if (send_open(to, to_path, "dest",
2230	    SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
2231	    &to_handle, &to_handle_len) != 0) {
2232		do_close(from, from_handle, from_handle_len);
2233		return -1;
2234	}
2235
2236	/* Read from remote "from" and write to remote "to" */
2237	offset = 0;
2238	write_error = read_error = num_req = num_upload_req = 0;
2239	max_req = 1;
2240	progress_counter = 0;
2241
2242	if (showprogress && size != 0) {
2243		start_progress_meter(progress_meter_path(from_path),
2244		    size, &progress_counter);
2245	}
2246	if ((msg = sshbuf_new()) == NULL)
2247		fatal_f("sshbuf_new failed");
2248	while (num_req > 0 || max_req > 0) {
2249		u_char *data;
2250		size_t len;
2251
2252		/*
2253		 * Simulate EOF on interrupt: stop sending new requests and
2254		 * allow outstanding requests to drain gracefully
2255		 */
2256		if (interrupted) {
2257			if (num_req == 0) /* If we haven't started yet... */
2258				break;
2259			max_req = 0;
2260		}
2261
2262		/* Send some more requests */
2263		while (num_req < max_req) {
2264			debug3("Request range %llu -> %llu (%d/%d)",
2265			    (unsigned long long)offset,
2266			    (unsigned long long)offset + buflen - 1,
2267			    num_req, max_req);
2268			req = request_enqueue(&requests, from->msg_id++,
2269			    buflen, offset);
2270			offset += buflen;
2271			num_req++;
2272			send_read_request(from, req->id, req->offset,
2273			    req->len, from_handle, from_handle_len);
2274		}
2275
2276		/* Try to eat replies from the upload side (nonblocking) */
2277		handle_dest_replies(to, to_path, 0,
2278		    &num_upload_req, &write_error);
2279
2280		sshbuf_reset(msg);
2281		get_msg(from, msg);
2282		if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
2283		    (r = sshbuf_get_u32(msg, &id)) != 0)
2284			fatal_fr(r, "parse");
2285		debug3("Received origin reply T:%u I:%u R:%d",
2286		    type, id, max_req);
2287
2288		/* Find the request in our queue */
2289		if ((req = request_find(&requests, id)) == NULL)
2290			fatal("Unexpected reply %u", id);
2291
2292		switch (type) {
2293		case SSH2_FXP_STATUS:
2294			if ((r = sshbuf_get_u32(msg, &status)) != 0)
2295				fatal_fr(r, "parse status");
2296			if (status != SSH2_FX_EOF)
2297				read_error = 1;
2298			max_req = 0;
2299			TAILQ_REMOVE(&requests, req, tq);
2300			free(req);
2301			num_req--;
2302			break;
2303		case SSH2_FXP_DATA:
2304			if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
2305				fatal_fr(r, "parse data");
2306			debug3("Received data %llu -> %llu",
2307			    (unsigned long long)req->offset,
2308			    (unsigned long long)req->offset + len - 1);
2309			if (len > req->len)
2310				fatal("Received more data than asked for "
2311				    "%zu > %zu", len, req->len);
2312
2313			/* Write this chunk out to the destination */
2314			sshbuf_reset(msg);
2315			if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
2316			    (r = sshbuf_put_u32(msg, to->msg_id++)) != 0 ||
2317			    (r = sshbuf_put_string(msg, to_handle,
2318			    to_handle_len)) != 0 ||
2319			    (r = sshbuf_put_u64(msg, req->offset)) != 0 ||
2320			    (r = sshbuf_put_string(msg, data, len)) != 0)
2321				fatal_fr(r, "compose write");
2322			send_msg(to, msg);
2323			debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%zu",
2324			    id, (unsigned long long)offset, len);
2325			num_upload_req++;
2326			progress_counter += len;
2327			free(data);
2328
2329			if (len == req->len) {
2330				TAILQ_REMOVE(&requests, req, tq);
2331				free(req);
2332				num_req--;
2333			} else {
2334				/* Resend the request for the missing data */
2335				debug3("Short data block, re-requesting "
2336				    "%llu -> %llu (%2d)",
2337				    (unsigned long long)req->offset + len,
2338				    (unsigned long long)req->offset +
2339				    req->len - 1, num_req);
2340				req->id = from->msg_id++;
2341				req->len -= len;
2342				req->offset += len;
2343				send_read_request(from, req->id,
2344				    req->offset, req->len,
2345				    from_handle, from_handle_len);
2346				/* Reduce the request size */
2347				if (len < buflen)
2348					buflen = MAXIMUM(MIN_READ_SIZE, len);
2349			}
2350			if (max_req > 0) { /* max_req = 0 iff EOF received */
2351				if (size > 0 && offset > size) {
2352					/* Only one request at a time
2353					 * after the expected EOF */
2354					debug3("Finish at %llu (%2d)",
2355					    (unsigned long long)offset,
2356					    num_req);
2357					max_req = 1;
2358				} else if (max_req < from->num_requests) {
2359					++max_req;
2360				}
2361			}
2362			break;
2363		default:
2364			fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
2365			    SSH2_FXP_DATA, type);
2366		}
2367	}
2368
2369	if (showprogress && size)
2370		stop_progress_meter();
2371
2372	/* Drain replies from the server (blocking) */
2373	debug3_f("waiting for %u replies from destination", num_upload_req);
2374	handle_dest_replies(to, to_path, 1, &num_upload_req, &write_error);
2375
2376	/* Sanity check */
2377	if (TAILQ_FIRST(&requests) != NULL)
2378		fatal("Transfer complete, but requests still in queue");
2379	/* Truncate at 0 length on interrupt or error to avoid holes at dest */
2380	if (read_error || write_error || interrupted) {
2381		debug("truncating \"%s\" at 0", to_path);
2382		do_close(to, to_handle, to_handle_len);
2383		free(to_handle);
2384		if (send_open(to, to_path, "dest",
2385		    SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
2386		    &to_handle, &to_handle_len) != 0) {
2387			error("truncation failed for \"%s\"", to_path);
2388			to_handle = NULL;
2389		}
2390	}
2391	if (read_error) {
2392		error("Couldn't read from origin file \"%s\" : %s",
2393		    from_path, fx2txt(status));
2394		status = -1;
2395		do_close(from, from_handle, from_handle_len);
2396		if (to_handle != NULL)
2397			do_close(to, to_handle, to_handle_len);
2398	} else if (write_error) {
2399		error("Couldn't write to \"%s\": %s",
2400		    to_path, fx2txt(write_error));
2401		status = SSH2_FX_FAILURE;
2402		do_close(from, from_handle, from_handle_len);
2403		if (to_handle != NULL)
2404			do_close(to, to_handle, to_handle_len);
2405	} else {
2406		if (do_close(from, from_handle, from_handle_len) != 0 ||
2407		    interrupted)
2408			status = -1;
2409		else
2410			status = SSH2_FX_OK;
2411		if (to_handle != NULL) {
2412			/* Need to resend utimes after write */
2413			if (preserve_flag)
2414				do_fsetstat(to, to_handle, to_handle_len, a);
2415			do_close(to, to_handle, to_handle_len);
2416		}
2417	}
2418	sshbuf_free(msg);
2419	free(from_handle);
2420	free(to_handle);
2421
2422	return status == SSH2_FX_OK ? 0 : -1;
2423}
2424
2425static int
2426crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
2427    const char *from_path, const char *to_path,
2428    int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
2429    int follow_link_flag)
2430{
2431	int i, ret = 0;
2432	SFTP_DIRENT **dir_entries;
2433	char *filename, *new_from_path = NULL, *new_to_path = NULL;
2434	mode_t mode = 0777;
2435	Attrib curdir;
2436
2437	if (depth >= MAX_DIR_DEPTH) {
2438		error("Maximum directory depth exceeded: %d levels", depth);
2439		return -1;
2440	}
2441
2442	if (dirattrib == NULL &&
2443	    (dirattrib = do_stat(from, from_path, 1)) == NULL) {
2444		error("Unable to stat remote directory \"%s\"", from_path);
2445		return -1;
2446	}
2447	if (!S_ISDIR(dirattrib->perm)) {
2448		error("\"%s\" is not a directory", from_path);
2449		return -1;
2450	}
2451	if (print_flag && print_flag != SFTP_PROGRESS_ONLY)
2452		mprintf("Retrieving %s\n", from_path);
2453
2454	curdir = *dirattrib; /* dirattrib will be clobbered */
2455	curdir.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
2456	curdir.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
2457	if ((curdir.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) == 0) {
2458		debug("Origin did not send permissions for "
2459		    "directory \"%s\"", to_path);
2460		curdir.perm = S_IWUSR|S_IXUSR;
2461		curdir.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
2462	}
2463	/* We need to be able to write to the directory while we transfer it */
2464	mode = curdir.perm & 01777;
2465	curdir.perm = mode | (S_IWUSR|S_IXUSR);
2466
2467	/*
2468	 * sftp lacks a portable status value to match errno EEXIST,
2469	 * so if we get a failure back then we must check whether
2470	 * the path already existed and is a directory.  Ensure we can
2471	 * write to the directory we create for the duration of the transfer.
2472	 */
2473	if (do_mkdir(to, to_path, &curdir, 0) != 0) {
2474		if ((dirattrib = do_stat(to, to_path, 0)) == NULL)
2475			return -1;
2476		if (!S_ISDIR(dirattrib->perm)) {
2477			error("\"%s\" exists but is not a directory", to_path);
2478			return -1;
2479		}
2480	}
2481	curdir.perm = mode;
2482
2483	if (do_readdir(from, from_path, &dir_entries) == -1) {
2484		error("%s: Failed to get directory contents", from_path);
2485		return -1;
2486	}
2487
2488	for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
2489		free(new_from_path);
2490		free(new_to_path);
2491
2492		filename = dir_entries[i]->filename;
2493		new_from_path = path_append(from_path, filename);
2494		new_to_path = path_append(to_path, filename);
2495
2496		if (S_ISDIR(dir_entries[i]->a.perm)) {
2497			if (strcmp(filename, ".") == 0 ||
2498			    strcmp(filename, "..") == 0)
2499				continue;
2500			if (crossload_dir_internal(from, to,
2501			    new_from_path, new_to_path,
2502			    depth + 1, &(dir_entries[i]->a), preserve_flag,
2503			    print_flag, follow_link_flag) == -1)
2504				ret = -1;
2505		} else if (S_ISREG(dir_entries[i]->a.perm) ||
2506		    (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
2507			/*
2508			 * If this is a symlink then don't send the link's
2509			 * Attrib. do_download() will do a FXP_STAT operation
2510			 * and get the link target's attributes.
2511			 */
2512			if (do_crossload(from, to, new_from_path, new_to_path,
2513			    S_ISLNK(dir_entries[i]->a.perm) ? NULL :
2514			    &(dir_entries[i]->a), preserve_flag) == -1) {
2515				error("Transfer of file %s to %s failed",
2516				    new_from_path, new_to_path);
2517				ret = -1;
2518			}
2519		} else
2520			logit("%s: not a regular file\n", new_from_path);
2521
2522	}
2523	free(new_to_path);
2524	free(new_from_path);
2525
2526	do_setstat(to, to_path, &curdir);
2527
2528	free_sftp_dirents(dir_entries);
2529
2530	return ret;
2531}
2532
2533int
2534crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
2535    const char *from_path, const char *to_path,
2536    Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag)
2537{
2538	char *from_path_canon;
2539	int ret;
2540
2541	if ((from_path_canon = do_realpath(from, from_path)) == NULL) {
2542		error("Unable to canonicalize path \"%s\"", from_path);
2543		return -1;
2544	}
2545
2546	ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0,
2547	    dirattrib, preserve_flag, print_flag, follow_link_flag);
2548	free(from_path_canon);
2549	return ret;
2550}
2551
2552char *
2553path_append(const char *p1, const char *p2)
2554{
2555	char *ret;
2556	size_t len = strlen(p1) + strlen(p2) + 2;
2557
2558	ret = xmalloc(len);
2559	strlcpy(ret, p1, len);
2560	if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
2561		strlcat(ret, "/", len);
2562	strlcat(ret, p2, len);
2563
2564	return(ret);
2565}
2566
2567char *
2568make_absolute(char *p, const char *pwd)
2569{
2570	char *abs_str;
2571
2572	/* Derelativise */
2573	if (p && !path_absolute(p)) {
2574		abs_str = path_append(pwd, p);
2575		free(p);
2576		return(abs_str);
2577	} else
2578		return(p);
2579}
2580
2581int
2582remote_is_dir(struct sftp_conn *conn, const char *path)
2583{
2584	Attrib *a;
2585
2586	/* XXX: report errors? */
2587	if ((a = do_stat(conn, path, 1)) == NULL)
2588		return(0);
2589	if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
2590		return(0);
2591	return(S_ISDIR(a->perm));
2592}
2593
2594
2595int
2596local_is_dir(const char *path)
2597{
2598	struct stat sb;
2599
2600	/* XXX: report errors? */
2601	if (stat(path, &sb) == -1)
2602		return(0);
2603
2604	return(S_ISDIR(sb.st_mode));
2605}
2606
2607/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
2608int
2609globpath_is_dir(const char *pathname)
2610{
2611	size_t l = strlen(pathname);
2612
2613	return l > 0 && pathname[l - 1] == '/';
2614}
2615
2616