xref: /freebsd/crypto/openssh/sftp-server.c (revision 021d409f5beb1827f72d24f171e3c3ed233ed62a)
1b66f2d16SKris Kennaway /*
2efcad6b7SDag-Erling Smørgrav  * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
3b66f2d16SKris Kennaway  *
4efcad6b7SDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
5efcad6b7SDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
6efcad6b7SDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
7b66f2d16SKris Kennaway  *
8efcad6b7SDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9efcad6b7SDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10efcad6b7SDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11efcad6b7SDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12efcad6b7SDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13efcad6b7SDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14efcad6b7SDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15b66f2d16SKris Kennaway  */
16b66f2d16SKris Kennaway #include "includes.h"
17021d409fSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.50 2006/01/02 01:20:31 djm Exp $");
18b66f2d16SKris Kennaway 
19b66f2d16SKris Kennaway #include "buffer.h"
20b66f2d16SKris Kennaway #include "bufaux.h"
21b66f2d16SKris Kennaway #include "getput.h"
221e8db6e2SBrian Feldman #include "log.h"
23b66f2d16SKris Kennaway #include "xmalloc.h"
24021d409fSDag-Erling Smørgrav #include "misc.h"
25b66f2d16SKris Kennaway 
261e8db6e2SBrian Feldman #include "sftp.h"
271e8db6e2SBrian Feldman #include "sftp-common.h"
28b66f2d16SKris Kennaway 
29b66f2d16SKris Kennaway /* helper */
301e8db6e2SBrian Feldman #define get_int64()			buffer_get_int64(&iqueue);
31b66f2d16SKris Kennaway #define get_int()			buffer_get_int(&iqueue);
32b66f2d16SKris Kennaway #define get_string(lenp)		buffer_get_string(&iqueue, lenp);
331e8db6e2SBrian Feldman #define TRACE				debug
34b66f2d16SKris Kennaway 
3583d2307dSDag-Erling Smørgrav extern char *__progname;
3683d2307dSDag-Erling Smørgrav 
37b66f2d16SKris Kennaway /* input and output queue */
38b66f2d16SKris Kennaway Buffer iqueue;
39b66f2d16SKris Kennaway Buffer oqueue;
40b66f2d16SKris Kennaway 
411e8db6e2SBrian Feldman /* Version of client */
421e8db6e2SBrian Feldman int version;
431e8db6e2SBrian Feldman 
44d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */
45b66f2d16SKris Kennaway 
46b66f2d16SKris Kennaway typedef struct Stat Stat;
47b66f2d16SKris Kennaway 
481e8db6e2SBrian Feldman struct Stat {
49b66f2d16SKris Kennaway 	char *name;
50b66f2d16SKris Kennaway 	char *long_name;
51b66f2d16SKris Kennaway 	Attrib attrib;
52b66f2d16SKris Kennaway };
53b66f2d16SKris Kennaway 
54ae1f160dSDag-Erling Smørgrav static int
55b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
56b66f2d16SKris Kennaway {
57b66f2d16SKris Kennaway 	int ret = 0;
581e8db6e2SBrian Feldman 
59b66f2d16SKris Kennaway 	switch (unixerrno) {
60b66f2d16SKris Kennaway 	case 0:
611e8db6e2SBrian Feldman 		ret = SSH2_FX_OK;
62b66f2d16SKris Kennaway 		break;
63b66f2d16SKris Kennaway 	case ENOENT:
64b66f2d16SKris Kennaway 	case ENOTDIR:
65b66f2d16SKris Kennaway 	case EBADF:
66b66f2d16SKris Kennaway 	case ELOOP:
671e8db6e2SBrian Feldman 		ret = SSH2_FX_NO_SUCH_FILE;
68b66f2d16SKris Kennaway 		break;
69b66f2d16SKris Kennaway 	case EPERM:
70b66f2d16SKris Kennaway 	case EACCES:
71b66f2d16SKris Kennaway 	case EFAULT:
721e8db6e2SBrian Feldman 		ret = SSH2_FX_PERMISSION_DENIED;
73b66f2d16SKris Kennaway 		break;
74b66f2d16SKris Kennaway 	case ENAMETOOLONG:
75b66f2d16SKris Kennaway 	case EINVAL:
761e8db6e2SBrian Feldman 		ret = SSH2_FX_BAD_MESSAGE;
77b66f2d16SKris Kennaway 		break;
78b66f2d16SKris Kennaway 	default:
791e8db6e2SBrian Feldman 		ret = SSH2_FX_FAILURE;
80b66f2d16SKris Kennaway 		break;
81b66f2d16SKris Kennaway 	}
82b66f2d16SKris Kennaway 	return ret;
83b66f2d16SKris Kennaway }
84b66f2d16SKris Kennaway 
85ae1f160dSDag-Erling Smørgrav static int
86b66f2d16SKris Kennaway flags_from_portable(int pflags)
87b66f2d16SKris Kennaway {
88b66f2d16SKris Kennaway 	int flags = 0;
891e8db6e2SBrian Feldman 
901e8db6e2SBrian Feldman 	if ((pflags & SSH2_FXF_READ) &&
911e8db6e2SBrian Feldman 	    (pflags & SSH2_FXF_WRITE)) {
92b66f2d16SKris Kennaway 		flags = O_RDWR;
931e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_READ) {
94b66f2d16SKris Kennaway 		flags = O_RDONLY;
951e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_WRITE) {
96b66f2d16SKris Kennaway 		flags = O_WRONLY;
97b66f2d16SKris Kennaway 	}
981e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_CREAT)
99b66f2d16SKris Kennaway 		flags |= O_CREAT;
1001e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_TRUNC)
101b66f2d16SKris Kennaway 		flags |= O_TRUNC;
1021e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_EXCL)
103b66f2d16SKris Kennaway 		flags |= O_EXCL;
104b66f2d16SKris Kennaway 	return flags;
105b66f2d16SKris Kennaway }
106b66f2d16SKris Kennaway 
107ae1f160dSDag-Erling Smørgrav static Attrib *
108b66f2d16SKris Kennaway get_attrib(void)
109b66f2d16SKris Kennaway {
110b66f2d16SKris Kennaway 	return decode_attrib(&iqueue);
111b66f2d16SKris Kennaway }
112b66f2d16SKris Kennaway 
113b66f2d16SKris Kennaway /* handle handles */
114b66f2d16SKris Kennaway 
115b66f2d16SKris Kennaway typedef struct Handle Handle;
116b66f2d16SKris Kennaway struct Handle {
117b66f2d16SKris Kennaway 	int use;
118b66f2d16SKris Kennaway 	DIR *dirp;
119b66f2d16SKris Kennaway 	int fd;
120b66f2d16SKris Kennaway 	char *name;
121b66f2d16SKris Kennaway };
1221e8db6e2SBrian Feldman 
123b66f2d16SKris Kennaway enum {
124b66f2d16SKris Kennaway 	HANDLE_UNUSED,
125b66f2d16SKris Kennaway 	HANDLE_DIR,
126b66f2d16SKris Kennaway 	HANDLE_FILE
127b66f2d16SKris Kennaway };
1281e8db6e2SBrian Feldman 
129b66f2d16SKris Kennaway Handle	handles[100];
130b66f2d16SKris Kennaway 
131ae1f160dSDag-Erling Smørgrav static void
132b66f2d16SKris Kennaway handle_init(void)
133b66f2d16SKris Kennaway {
134043840dfSDag-Erling Smørgrav 	u_int i;
1351e8db6e2SBrian Feldman 
136b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
137b66f2d16SKris Kennaway 		handles[i].use = HANDLE_UNUSED;
138b66f2d16SKris Kennaway }
139b66f2d16SKris Kennaway 
140ae1f160dSDag-Erling Smørgrav static int
141efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp)
142b66f2d16SKris Kennaway {
143043840dfSDag-Erling Smørgrav 	u_int i;
1441e8db6e2SBrian Feldman 
145b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
146b66f2d16SKris Kennaway 		if (handles[i].use == HANDLE_UNUSED) {
147b66f2d16SKris Kennaway 			handles[i].use = use;
148b66f2d16SKris Kennaway 			handles[i].dirp = dirp;
149b66f2d16SKris Kennaway 			handles[i].fd = fd;
150d0c8c0bcSDag-Erling Smørgrav 			handles[i].name = xstrdup(name);
151b66f2d16SKris Kennaway 			return i;
152b66f2d16SKris Kennaway 		}
153b66f2d16SKris Kennaway 	}
154b66f2d16SKris Kennaway 	return -1;
155b66f2d16SKris Kennaway }
156b66f2d16SKris Kennaway 
157ae1f160dSDag-Erling Smørgrav static int
158b66f2d16SKris Kennaway handle_is_ok(int i, int type)
159b66f2d16SKris Kennaway {
160043840dfSDag-Erling Smørgrav 	return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
1611e8db6e2SBrian Feldman 	    handles[i].use == type;
162b66f2d16SKris Kennaway }
163b66f2d16SKris Kennaway 
164ae1f160dSDag-Erling Smørgrav static int
165b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp)
166b66f2d16SKris Kennaway {
167b66f2d16SKris Kennaway 	if (stringp == NULL || hlenp == NULL)
168b66f2d16SKris Kennaway 		return -1;
1691e8db6e2SBrian Feldman 	*stringp = xmalloc(sizeof(int32_t));
1701e8db6e2SBrian Feldman 	PUT_32BIT(*stringp, handle);
1711e8db6e2SBrian Feldman 	*hlenp = sizeof(int32_t);
172b66f2d16SKris Kennaway 	return 0;
173b66f2d16SKris Kennaway }
174b66f2d16SKris Kennaway 
175ae1f160dSDag-Erling Smørgrav static int
176efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen)
177b66f2d16SKris Kennaway {
1781e8db6e2SBrian Feldman 	int val;
1791e8db6e2SBrian Feldman 
1801e8db6e2SBrian Feldman 	if (hlen != sizeof(int32_t))
181b66f2d16SKris Kennaway 		return -1;
1821e8db6e2SBrian Feldman 	val = GET_32BIT(handle);
183b66f2d16SKris Kennaway 	if (handle_is_ok(val, HANDLE_FILE) ||
184b66f2d16SKris Kennaway 	    handle_is_ok(val, HANDLE_DIR))
185b66f2d16SKris Kennaway 		return val;
186b66f2d16SKris Kennaway 	return -1;
187b66f2d16SKris Kennaway }
188b66f2d16SKris Kennaway 
189ae1f160dSDag-Erling Smørgrav static char *
190b66f2d16SKris Kennaway handle_to_name(int handle)
191b66f2d16SKris Kennaway {
192b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR)||
193b66f2d16SKris Kennaway 	    handle_is_ok(handle, HANDLE_FILE))
194b66f2d16SKris Kennaway 		return handles[handle].name;
195b66f2d16SKris Kennaway 	return NULL;
196b66f2d16SKris Kennaway }
197b66f2d16SKris Kennaway 
198ae1f160dSDag-Erling Smørgrav static DIR *
199b66f2d16SKris Kennaway handle_to_dir(int handle)
200b66f2d16SKris Kennaway {
201b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR))
202b66f2d16SKris Kennaway 		return handles[handle].dirp;
203b66f2d16SKris Kennaway 	return NULL;
204b66f2d16SKris Kennaway }
205b66f2d16SKris Kennaway 
206ae1f160dSDag-Erling Smørgrav static int
207b66f2d16SKris Kennaway handle_to_fd(int handle)
208b66f2d16SKris Kennaway {
209b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE))
210b66f2d16SKris Kennaway 		return handles[handle].fd;
211b66f2d16SKris Kennaway 	return -1;
212b66f2d16SKris Kennaway }
213b66f2d16SKris Kennaway 
214ae1f160dSDag-Erling Smørgrav static int
215b66f2d16SKris Kennaway handle_close(int handle)
216b66f2d16SKris Kennaway {
217b66f2d16SKris Kennaway 	int ret = -1;
2181e8db6e2SBrian Feldman 
219b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE)) {
220b66f2d16SKris Kennaway 		ret = close(handles[handle].fd);
221b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
222d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
223b66f2d16SKris Kennaway 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
224b66f2d16SKris Kennaway 		ret = closedir(handles[handle].dirp);
225b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
226d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
227b66f2d16SKris Kennaway 	} else {
228b66f2d16SKris Kennaway 		errno = ENOENT;
229b66f2d16SKris Kennaway 	}
230b66f2d16SKris Kennaway 	return ret;
231b66f2d16SKris Kennaway }
232b66f2d16SKris Kennaway 
233ae1f160dSDag-Erling Smørgrav static int
234b66f2d16SKris Kennaway get_handle(void)
235b66f2d16SKris Kennaway {
236b66f2d16SKris Kennaway 	char *handle;
2371e8db6e2SBrian Feldman 	int val = -1;
238b66f2d16SKris Kennaway 	u_int hlen;
2391e8db6e2SBrian Feldman 
240b66f2d16SKris Kennaway 	handle = get_string(&hlen);
2411e8db6e2SBrian Feldman 	if (hlen < 256)
242b66f2d16SKris Kennaway 		val = handle_from_string(handle, hlen);
243b66f2d16SKris Kennaway 	xfree(handle);
244b66f2d16SKris Kennaway 	return val;
245b66f2d16SKris Kennaway }
246b66f2d16SKris Kennaway 
247b66f2d16SKris Kennaway /* send replies */
248b66f2d16SKris Kennaway 
249ae1f160dSDag-Erling Smørgrav static void
250b66f2d16SKris Kennaway send_msg(Buffer *m)
251b66f2d16SKris Kennaway {
252b66f2d16SKris Kennaway 	int mlen = buffer_len(m);
2531e8db6e2SBrian Feldman 
254b66f2d16SKris Kennaway 	buffer_put_int(&oqueue, mlen);
255b66f2d16SKris Kennaway 	buffer_append(&oqueue, buffer_ptr(m), mlen);
256b66f2d16SKris Kennaway 	buffer_consume(m, mlen);
257b66f2d16SKris Kennaway }
258b66f2d16SKris Kennaway 
259ae1f160dSDag-Erling Smørgrav static void
260d74d50a8SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status)
261b66f2d16SKris Kennaway {
262b66f2d16SKris Kennaway 	Buffer msg;
2631e8db6e2SBrian Feldman 	const char *status_messages[] = {
2641e8db6e2SBrian Feldman 		"Success",			/* SSH_FX_OK */
2651e8db6e2SBrian Feldman 		"End of file",			/* SSH_FX_EOF */
2661e8db6e2SBrian Feldman 		"No such file",			/* SSH_FX_NO_SUCH_FILE */
2671e8db6e2SBrian Feldman 		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
2681e8db6e2SBrian Feldman 		"Failure",			/* SSH_FX_FAILURE */
2691e8db6e2SBrian Feldman 		"Bad message",			/* SSH_FX_BAD_MESSAGE */
2701e8db6e2SBrian Feldman 		"No connection",		/* SSH_FX_NO_CONNECTION */
2711e8db6e2SBrian Feldman 		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
2721e8db6e2SBrian Feldman 		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
2731e8db6e2SBrian Feldman 		"Unknown error"			/* Others */
2741e8db6e2SBrian Feldman 	};
2751e8db6e2SBrian Feldman 
276d74d50a8SDag-Erling Smørgrav 	TRACE("sent status id %u error %u", id, status);
277b66f2d16SKris Kennaway 	buffer_init(&msg);
2781e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_STATUS);
279b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
280d74d50a8SDag-Erling Smørgrav 	buffer_put_int(&msg, status);
2811e8db6e2SBrian Feldman 	if (version >= 3) {
2821e8db6e2SBrian Feldman 		buffer_put_cstring(&msg,
283d74d50a8SDag-Erling Smørgrav 		    status_messages[MIN(status,SSH2_FX_MAX)]);
2841e8db6e2SBrian Feldman 		buffer_put_cstring(&msg, "");
2851e8db6e2SBrian Feldman 	}
286b66f2d16SKris Kennaway 	send_msg(&msg);
287b66f2d16SKris Kennaway 	buffer_free(&msg);
288b66f2d16SKris Kennaway }
289ae1f160dSDag-Erling Smørgrav static void
290efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
291b66f2d16SKris Kennaway {
292b66f2d16SKris Kennaway 	Buffer msg;
2931e8db6e2SBrian Feldman 
294b66f2d16SKris Kennaway 	buffer_init(&msg);
295b66f2d16SKris Kennaway 	buffer_put_char(&msg, type);
296b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
297b66f2d16SKris Kennaway 	buffer_put_string(&msg, data, dlen);
298b66f2d16SKris Kennaway 	send_msg(&msg);
299b66f2d16SKris Kennaway 	buffer_free(&msg);
300b66f2d16SKris Kennaway }
301b66f2d16SKris Kennaway 
302ae1f160dSDag-Erling Smørgrav static void
303efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen)
304b66f2d16SKris Kennaway {
305ee21a45fSDag-Erling Smørgrav 	TRACE("sent data id %u len %d", id, dlen);
3061e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
307b66f2d16SKris Kennaway }
308b66f2d16SKris Kennaway 
309ae1f160dSDag-Erling Smørgrav static void
310b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
311b66f2d16SKris Kennaway {
312b66f2d16SKris Kennaway 	char *string;
313b66f2d16SKris Kennaway 	int hlen;
3141e8db6e2SBrian Feldman 
315b66f2d16SKris Kennaway 	handle_to_string(handle, &string, &hlen);
316ee21a45fSDag-Erling Smørgrav 	TRACE("sent handle id %u handle %d", id, handle);
3171e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
318b66f2d16SKris Kennaway 	xfree(string);
319b66f2d16SKris Kennaway }
320b66f2d16SKris Kennaway 
321ae1f160dSDag-Erling Smørgrav static void
322efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats)
323b66f2d16SKris Kennaway {
324b66f2d16SKris Kennaway 	Buffer msg;
325b66f2d16SKris Kennaway 	int i;
3261e8db6e2SBrian Feldman 
327b66f2d16SKris Kennaway 	buffer_init(&msg);
3281e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_NAME);
329b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
330b66f2d16SKris Kennaway 	buffer_put_int(&msg, count);
331ee21a45fSDag-Erling Smørgrav 	TRACE("sent names id %u count %d", id, count);
332b66f2d16SKris Kennaway 	for (i = 0; i < count; i++) {
333b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].name);
334b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].long_name);
335b66f2d16SKris Kennaway 		encode_attrib(&msg, &stats[i].attrib);
336b66f2d16SKris Kennaway 	}
337b66f2d16SKris Kennaway 	send_msg(&msg);
338b66f2d16SKris Kennaway 	buffer_free(&msg);
339b66f2d16SKris Kennaway }
340b66f2d16SKris Kennaway 
341ae1f160dSDag-Erling Smørgrav static void
342efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a)
343b66f2d16SKris Kennaway {
344b66f2d16SKris Kennaway 	Buffer msg;
3451e8db6e2SBrian Feldman 
346ee21a45fSDag-Erling Smørgrav 	TRACE("sent attrib id %u have 0x%x", id, a->flags);
347b66f2d16SKris Kennaway 	buffer_init(&msg);
3481e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_ATTRS);
349b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
350b66f2d16SKris Kennaway 	encode_attrib(&msg, a);
351b66f2d16SKris Kennaway 	send_msg(&msg);
352b66f2d16SKris Kennaway 	buffer_free(&msg);
353b66f2d16SKris Kennaway }
354b66f2d16SKris Kennaway 
355b66f2d16SKris Kennaway /* parse incoming */
356b66f2d16SKris Kennaway 
357ae1f160dSDag-Erling Smørgrav static void
358b66f2d16SKris Kennaway process_init(void)
359b66f2d16SKris Kennaway {
360b66f2d16SKris Kennaway 	Buffer msg;
361b66f2d16SKris Kennaway 
362545d5ecaSDag-Erling Smørgrav 	version = get_int();
363b66f2d16SKris Kennaway 	TRACE("client version %d", version);
364b66f2d16SKris Kennaway 	buffer_init(&msg);
3651e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_VERSION);
3661e8db6e2SBrian Feldman 	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
367b66f2d16SKris Kennaway 	send_msg(&msg);
368b66f2d16SKris Kennaway 	buffer_free(&msg);
369b66f2d16SKris Kennaway }
370b66f2d16SKris Kennaway 
371ae1f160dSDag-Erling Smørgrav static void
372b66f2d16SKris Kennaway process_open(void)
373b66f2d16SKris Kennaway {
374b66f2d16SKris Kennaway 	u_int32_t id, pflags;
375b66f2d16SKris Kennaway 	Attrib *a;
376b66f2d16SKris Kennaway 	char *name;
3771e8db6e2SBrian Feldman 	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
378b66f2d16SKris Kennaway 
379b66f2d16SKris Kennaway 	id = get_int();
380b66f2d16SKris Kennaway 	name = get_string(NULL);
3811e8db6e2SBrian Feldman 	pflags = get_int();		/* portable flags */
382b66f2d16SKris Kennaway 	a = get_attrib();
383b66f2d16SKris Kennaway 	flags = flags_from_portable(pflags);
3841e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
385ee21a45fSDag-Erling Smørgrav 	TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
386b66f2d16SKris Kennaway 	fd = open(name, flags, mode);
387b66f2d16SKris Kennaway 	if (fd < 0) {
388b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
389b66f2d16SKris Kennaway 	} else {
390d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_FILE, name, fd, NULL);
391b66f2d16SKris Kennaway 		if (handle < 0) {
392b66f2d16SKris Kennaway 			close(fd);
393b66f2d16SKris Kennaway 		} else {
394b66f2d16SKris Kennaway 			send_handle(id, handle);
3951e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
396b66f2d16SKris Kennaway 		}
397b66f2d16SKris Kennaway 	}
3981e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
399b66f2d16SKris Kennaway 		send_status(id, status);
400b66f2d16SKris Kennaway 	xfree(name);
401b66f2d16SKris Kennaway }
402b66f2d16SKris Kennaway 
403ae1f160dSDag-Erling Smørgrav static void
404b66f2d16SKris Kennaway process_close(void)
405b66f2d16SKris Kennaway {
406b66f2d16SKris Kennaway 	u_int32_t id;
4071e8db6e2SBrian Feldman 	int handle, ret, status = SSH2_FX_FAILURE;
408b66f2d16SKris Kennaway 
409b66f2d16SKris Kennaway 	id = get_int();
410b66f2d16SKris Kennaway 	handle = get_handle();
411ee21a45fSDag-Erling Smørgrav 	TRACE("close id %u handle %d", id, handle);
412b66f2d16SKris Kennaway 	ret = handle_close(handle);
4131e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
414b66f2d16SKris Kennaway 	send_status(id, status);
415b66f2d16SKris Kennaway }
416b66f2d16SKris Kennaway 
417ae1f160dSDag-Erling Smørgrav static void
418b66f2d16SKris Kennaway process_read(void)
419b66f2d16SKris Kennaway {
420b66f2d16SKris Kennaway 	char buf[64*1024];
4211e8db6e2SBrian Feldman 	u_int32_t id, len;
4221e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
423b66f2d16SKris Kennaway 	u_int64_t off;
424b66f2d16SKris Kennaway 
425b66f2d16SKris Kennaway 	id = get_int();
426b66f2d16SKris Kennaway 	handle = get_handle();
4271e8db6e2SBrian Feldman 	off = get_int64();
428b66f2d16SKris Kennaway 	len = get_int();
429b66f2d16SKris Kennaway 
430ee21a45fSDag-Erling Smørgrav 	TRACE("read id %u handle %d off %llu len %d", id, handle,
431021d409fSDag-Erling Smørgrav 	    (unsigned long long)off, len);
432b66f2d16SKris Kennaway 	if (len > sizeof buf) {
433b66f2d16SKris Kennaway 		len = sizeof buf;
434d95e11bfSDag-Erling Smørgrav 		logit("read change len %d", len);
435b66f2d16SKris Kennaway 	}
436b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
437b66f2d16SKris Kennaway 	if (fd >= 0) {
438b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
439b66f2d16SKris Kennaway 			error("process_read: seek failed");
440b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
441b66f2d16SKris Kennaway 		} else {
442b66f2d16SKris Kennaway 			ret = read(fd, buf, len);
443b66f2d16SKris Kennaway 			if (ret < 0) {
444b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
445b66f2d16SKris Kennaway 			} else if (ret == 0) {
4461e8db6e2SBrian Feldman 				status = SSH2_FX_EOF;
447b66f2d16SKris Kennaway 			} else {
448b66f2d16SKris Kennaway 				send_data(id, buf, ret);
4491e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
450b66f2d16SKris Kennaway 			}
451b66f2d16SKris Kennaway 		}
452b66f2d16SKris Kennaway 	}
4531e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
454b66f2d16SKris Kennaway 		send_status(id, status);
455b66f2d16SKris Kennaway }
456b66f2d16SKris Kennaway 
457ae1f160dSDag-Erling Smørgrav static void
458b66f2d16SKris Kennaway process_write(void)
459b66f2d16SKris Kennaway {
4601e8db6e2SBrian Feldman 	u_int32_t id;
461b66f2d16SKris Kennaway 	u_int64_t off;
462b66f2d16SKris Kennaway 	u_int len;
4631e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
464b66f2d16SKris Kennaway 	char *data;
465b66f2d16SKris Kennaway 
466b66f2d16SKris Kennaway 	id = get_int();
467b66f2d16SKris Kennaway 	handle = get_handle();
4681e8db6e2SBrian Feldman 	off = get_int64();
469b66f2d16SKris Kennaway 	data = get_string(&len);
470b66f2d16SKris Kennaway 
471ee21a45fSDag-Erling Smørgrav 	TRACE("write id %u handle %d off %llu len %d", id, handle,
472021d409fSDag-Erling Smørgrav 	    (unsigned long long)off, len);
473b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
474b66f2d16SKris Kennaway 	if (fd >= 0) {
475b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
476b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
477b66f2d16SKris Kennaway 			error("process_write: seek failed");
478b66f2d16SKris Kennaway 		} else {
479b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
480b66f2d16SKris Kennaway 			ret = write(fd, data, len);
481043840dfSDag-Erling Smørgrav 			if (ret < 0) {
482b66f2d16SKris Kennaway 				error("process_write: write failed");
483b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
484043840dfSDag-Erling Smørgrav 			} else if ((size_t)ret == len) {
4851e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
486b66f2d16SKris Kennaway 			} else {
487d95e11bfSDag-Erling Smørgrav 				logit("nothing at all written");
488b66f2d16SKris Kennaway 			}
489b66f2d16SKris Kennaway 		}
490b66f2d16SKris Kennaway 	}
491b66f2d16SKris Kennaway 	send_status(id, status);
492b66f2d16SKris Kennaway 	xfree(data);
493b66f2d16SKris Kennaway }
494b66f2d16SKris Kennaway 
495ae1f160dSDag-Erling Smørgrav static void
496b66f2d16SKris Kennaway process_do_stat(int do_lstat)
497b66f2d16SKris Kennaway {
4981e8db6e2SBrian Feldman 	Attrib a;
499b66f2d16SKris Kennaway 	struct stat st;
500b66f2d16SKris Kennaway 	u_int32_t id;
501b66f2d16SKris Kennaway 	char *name;
5021e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
503b66f2d16SKris Kennaway 
504b66f2d16SKris Kennaway 	id = get_int();
505b66f2d16SKris Kennaway 	name = get_string(NULL);
506ee21a45fSDag-Erling Smørgrav 	TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
507b66f2d16SKris Kennaway 	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
508b66f2d16SKris Kennaway 	if (ret < 0) {
509b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
510b66f2d16SKris Kennaway 	} else {
5111e8db6e2SBrian Feldman 		stat_to_attrib(&st, &a);
5121e8db6e2SBrian Feldman 		send_attrib(id, &a);
5131e8db6e2SBrian Feldman 		status = SSH2_FX_OK;
514b66f2d16SKris Kennaway 	}
5151e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
516b66f2d16SKris Kennaway 		send_status(id, status);
517b66f2d16SKris Kennaway 	xfree(name);
518b66f2d16SKris Kennaway }
519b66f2d16SKris Kennaway 
520ae1f160dSDag-Erling Smørgrav static void
521b66f2d16SKris Kennaway process_stat(void)
522b66f2d16SKris Kennaway {
523b66f2d16SKris Kennaway 	process_do_stat(0);
524b66f2d16SKris Kennaway }
525b66f2d16SKris Kennaway 
526ae1f160dSDag-Erling Smørgrav static void
527b66f2d16SKris Kennaway process_lstat(void)
528b66f2d16SKris Kennaway {
529b66f2d16SKris Kennaway 	process_do_stat(1);
530b66f2d16SKris Kennaway }
531b66f2d16SKris Kennaway 
532ae1f160dSDag-Erling Smørgrav static void
533b66f2d16SKris Kennaway process_fstat(void)
534b66f2d16SKris Kennaway {
5351e8db6e2SBrian Feldman 	Attrib a;
536b66f2d16SKris Kennaway 	struct stat st;
537b66f2d16SKris Kennaway 	u_int32_t id;
5381e8db6e2SBrian Feldman 	int fd, ret, handle, status = SSH2_FX_FAILURE;
539b66f2d16SKris Kennaway 
540b66f2d16SKris Kennaway 	id = get_int();
541b66f2d16SKris Kennaway 	handle = get_handle();
542ee21a45fSDag-Erling Smørgrav 	TRACE("fstat id %u handle %d", id, handle);
543b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
544b66f2d16SKris Kennaway 	if (fd  >= 0) {
545b66f2d16SKris Kennaway 		ret = fstat(fd, &st);
546b66f2d16SKris Kennaway 		if (ret < 0) {
547b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
548b66f2d16SKris Kennaway 		} else {
5491e8db6e2SBrian Feldman 			stat_to_attrib(&st, &a);
5501e8db6e2SBrian Feldman 			send_attrib(id, &a);
5511e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
552b66f2d16SKris Kennaway 		}
553b66f2d16SKris Kennaway 	}
5541e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
555b66f2d16SKris Kennaway 		send_status(id, status);
556b66f2d16SKris Kennaway }
557b66f2d16SKris Kennaway 
558ae1f160dSDag-Erling Smørgrav static struct timeval *
559efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a)
560b66f2d16SKris Kennaway {
561b66f2d16SKris Kennaway 	static struct timeval tv[2];
5621e8db6e2SBrian Feldman 
563b66f2d16SKris Kennaway 	tv[0].tv_sec = a->atime;
564b66f2d16SKris Kennaway 	tv[0].tv_usec = 0;
565b66f2d16SKris Kennaway 	tv[1].tv_sec = a->mtime;
566b66f2d16SKris Kennaway 	tv[1].tv_usec = 0;
567b66f2d16SKris Kennaway 	return tv;
568b66f2d16SKris Kennaway }
569b66f2d16SKris Kennaway 
570ae1f160dSDag-Erling Smørgrav static void
571b66f2d16SKris Kennaway process_setstat(void)
572b66f2d16SKris Kennaway {
573b66f2d16SKris Kennaway 	Attrib *a;
574b66f2d16SKris Kennaway 	u_int32_t id;
575b66f2d16SKris Kennaway 	char *name;
576ee21a45fSDag-Erling Smørgrav 	int status = SSH2_FX_OK, ret;
577b66f2d16SKris Kennaway 
578b66f2d16SKris Kennaway 	id = get_int();
579b66f2d16SKris Kennaway 	name = get_string(NULL);
580b66f2d16SKris Kennaway 	a = get_attrib();
581ee21a45fSDag-Erling Smørgrav 	TRACE("setstat id %u name %s", id, name);
582ae1f160dSDag-Erling Smørgrav 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
583ae1f160dSDag-Erling Smørgrav 		ret = truncate(name, a->size);
584ae1f160dSDag-Erling Smørgrav 		if (ret == -1)
585ae1f160dSDag-Erling Smørgrav 			status = errno_to_portable(errno);
586ae1f160dSDag-Erling Smørgrav 	}
5871e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
588b66f2d16SKris Kennaway 		ret = chmod(name, a->perm & 0777);
589b66f2d16SKris Kennaway 		if (ret == -1)
590b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
591b66f2d16SKris Kennaway 	}
5921e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
593b66f2d16SKris Kennaway 		ret = utimes(name, attrib_to_tv(a));
594b66f2d16SKris Kennaway 		if (ret == -1)
595b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
596b66f2d16SKris Kennaway 	}
5971e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
5981e8db6e2SBrian Feldman 		ret = chown(name, a->uid, a->gid);
5991e8db6e2SBrian Feldman 		if (ret == -1)
6001e8db6e2SBrian Feldman 			status = errno_to_portable(errno);
6011e8db6e2SBrian Feldman 	}
602b66f2d16SKris Kennaway 	send_status(id, status);
603b66f2d16SKris Kennaway 	xfree(name);
604b66f2d16SKris Kennaway }
605b66f2d16SKris Kennaway 
606ae1f160dSDag-Erling Smørgrav static void
607b66f2d16SKris Kennaway process_fsetstat(void)
608b66f2d16SKris Kennaway {
609b66f2d16SKris Kennaway 	Attrib *a;
610b66f2d16SKris Kennaway 	u_int32_t id;
611b66f2d16SKris Kennaway 	int handle, fd, ret;
6121e8db6e2SBrian Feldman 	int status = SSH2_FX_OK;
61383d2307dSDag-Erling Smørgrav 	char *name;
614b66f2d16SKris Kennaway 
615b66f2d16SKris Kennaway 	id = get_int();
616b66f2d16SKris Kennaway 	handle = get_handle();
617b66f2d16SKris Kennaway 	a = get_attrib();
618ee21a45fSDag-Erling Smørgrav 	TRACE("fsetstat id %u handle %d", id, handle);
619b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
62083d2307dSDag-Erling Smørgrav 	name = handle_to_name(handle);
62183d2307dSDag-Erling Smørgrav 	if (fd < 0 || name == NULL) {
6221e8db6e2SBrian Feldman 		status = SSH2_FX_FAILURE;
623b66f2d16SKris Kennaway 	} else {
624ae1f160dSDag-Erling Smørgrav 		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
625ae1f160dSDag-Erling Smørgrav 			ret = ftruncate(fd, a->size);
626ae1f160dSDag-Erling Smørgrav 			if (ret == -1)
627ae1f160dSDag-Erling Smørgrav 				status = errno_to_portable(errno);
628ae1f160dSDag-Erling Smørgrav 		}
6291e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
63083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD
631b66f2d16SKris Kennaway 			ret = fchmod(fd, a->perm & 0777);
63283d2307dSDag-Erling Smørgrav #else
63383d2307dSDag-Erling Smørgrav 			ret = chmod(name, a->perm & 0777);
63483d2307dSDag-Erling Smørgrav #endif
635b66f2d16SKris Kennaway 			if (ret == -1)
636b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
637b66f2d16SKris Kennaway 		}
6381e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
63983d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES
640b66f2d16SKris Kennaway 			ret = futimes(fd, attrib_to_tv(a));
64183d2307dSDag-Erling Smørgrav #else
64283d2307dSDag-Erling Smørgrav 			ret = utimes(name, attrib_to_tv(a));
64383d2307dSDag-Erling Smørgrav #endif
644b66f2d16SKris Kennaway 			if (ret == -1)
645b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
646b66f2d16SKris Kennaway 		}
6471e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
64883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN
6491e8db6e2SBrian Feldman 			ret = fchown(fd, a->uid, a->gid);
65083d2307dSDag-Erling Smørgrav #else
65183d2307dSDag-Erling Smørgrav 			ret = chown(name, a->uid, a->gid);
65283d2307dSDag-Erling Smørgrav #endif
6531e8db6e2SBrian Feldman 			if (ret == -1)
6541e8db6e2SBrian Feldman 				status = errno_to_portable(errno);
6551e8db6e2SBrian Feldman 		}
656b66f2d16SKris Kennaway 	}
657b66f2d16SKris Kennaway 	send_status(id, status);
658b66f2d16SKris Kennaway }
659b66f2d16SKris Kennaway 
660ae1f160dSDag-Erling Smørgrav static void
661b66f2d16SKris Kennaway process_opendir(void)
662b66f2d16SKris Kennaway {
663b66f2d16SKris Kennaway 	DIR *dirp = NULL;
664b66f2d16SKris Kennaway 	char *path;
6651e8db6e2SBrian Feldman 	int handle, status = SSH2_FX_FAILURE;
666b66f2d16SKris Kennaway 	u_int32_t id;
667b66f2d16SKris Kennaway 
668b66f2d16SKris Kennaway 	id = get_int();
669b66f2d16SKris Kennaway 	path = get_string(NULL);
670ee21a45fSDag-Erling Smørgrav 	TRACE("opendir id %u path %s", id, path);
671b66f2d16SKris Kennaway 	dirp = opendir(path);
672b66f2d16SKris Kennaway 	if (dirp == NULL) {
673b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
674b66f2d16SKris Kennaway 	} else {
675d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_DIR, path, 0, dirp);
676b66f2d16SKris Kennaway 		if (handle < 0) {
677b66f2d16SKris Kennaway 			closedir(dirp);
678b66f2d16SKris Kennaway 		} else {
679b66f2d16SKris Kennaway 			send_handle(id, handle);
6801e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
681b66f2d16SKris Kennaway 		}
682b66f2d16SKris Kennaway 
683b66f2d16SKris Kennaway 	}
6841e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
685b66f2d16SKris Kennaway 		send_status(id, status);
686b66f2d16SKris Kennaway 	xfree(path);
687b66f2d16SKris Kennaway }
688b66f2d16SKris Kennaway 
689ae1f160dSDag-Erling Smørgrav static void
690b66f2d16SKris Kennaway process_readdir(void)
691b66f2d16SKris Kennaway {
692b66f2d16SKris Kennaway 	DIR *dirp;
693b66f2d16SKris Kennaway 	struct dirent *dp;
694b66f2d16SKris Kennaway 	char *path;
695b66f2d16SKris Kennaway 	int handle;
696b66f2d16SKris Kennaway 	u_int32_t id;
697b66f2d16SKris Kennaway 
698b66f2d16SKris Kennaway 	id = get_int();
699b66f2d16SKris Kennaway 	handle = get_handle();
700ee21a45fSDag-Erling Smørgrav 	TRACE("readdir id %u handle %d", id, handle);
701b66f2d16SKris Kennaway 	dirp = handle_to_dir(handle);
702b66f2d16SKris Kennaway 	path = handle_to_name(handle);
703b66f2d16SKris Kennaway 	if (dirp == NULL || path == NULL) {
7041e8db6e2SBrian Feldman 		send_status(id, SSH2_FX_FAILURE);
705b66f2d16SKris Kennaway 	} else {
706b66f2d16SKris Kennaway 		struct stat st;
707b66f2d16SKris Kennaway 		char pathname[1024];
708b66f2d16SKris Kennaway 		Stat *stats;
709b66f2d16SKris Kennaway 		int nstats = 10, count = 0, i;
710ee21a45fSDag-Erling Smørgrav 
711b66f2d16SKris Kennaway 		stats = xmalloc(nstats * sizeof(Stat));
712b66f2d16SKris Kennaway 		while ((dp = readdir(dirp)) != NULL) {
713b66f2d16SKris Kennaway 			if (count >= nstats) {
714b66f2d16SKris Kennaway 				nstats *= 2;
715b66f2d16SKris Kennaway 				stats = xrealloc(stats, nstats * sizeof(Stat));
716b66f2d16SKris Kennaway 			}
717b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
718ae1f160dSDag-Erling Smørgrav 			snprintf(pathname, sizeof pathname, "%s%s%s", path,
719ae1f160dSDag-Erling Smørgrav 			    strcmp(path, "/") ? "/" : "", dp->d_name);
720b66f2d16SKris Kennaway 			if (lstat(pathname, &st) < 0)
721b66f2d16SKris Kennaway 				continue;
7221e8db6e2SBrian Feldman 			stat_to_attrib(&st, &(stats[count].attrib));
723b66f2d16SKris Kennaway 			stats[count].name = xstrdup(dp->d_name);
7244b17dab0SDag-Erling Smørgrav 			stats[count].long_name = ls_file(dp->d_name, &st, 0);
725b66f2d16SKris Kennaway 			count++;
726b66f2d16SKris Kennaway 			/* send up to 100 entries in one message */
7271e8db6e2SBrian Feldman 			/* XXX check packet size instead */
728b66f2d16SKris Kennaway 			if (count == 100)
729b66f2d16SKris Kennaway 				break;
730b66f2d16SKris Kennaway 		}
7311e8db6e2SBrian Feldman 		if (count > 0) {
732b66f2d16SKris Kennaway 			send_names(id, count, stats);
733b66f2d16SKris Kennaway 			for (i = 0; i < count; i++) {
734b66f2d16SKris Kennaway 				xfree(stats[i].name);
735b66f2d16SKris Kennaway 				xfree(stats[i].long_name);
736b66f2d16SKris Kennaway 			}
7371e8db6e2SBrian Feldman 		} else {
7381e8db6e2SBrian Feldman 			send_status(id, SSH2_FX_EOF);
7391e8db6e2SBrian Feldman 		}
740b66f2d16SKris Kennaway 		xfree(stats);
741b66f2d16SKris Kennaway 	}
742b66f2d16SKris Kennaway }
743b66f2d16SKris Kennaway 
744ae1f160dSDag-Erling Smørgrav static void
745b66f2d16SKris Kennaway process_remove(void)
746b66f2d16SKris Kennaway {
747b66f2d16SKris Kennaway 	char *name;
748b66f2d16SKris Kennaway 	u_int32_t id;
7491e8db6e2SBrian Feldman 	int status = SSH2_FX_FAILURE;
750b66f2d16SKris Kennaway 	int ret;
751b66f2d16SKris Kennaway 
752b66f2d16SKris Kennaway 	id = get_int();
753b66f2d16SKris Kennaway 	name = get_string(NULL);
754ee21a45fSDag-Erling Smørgrav 	TRACE("remove id %u name %s", id, name);
7551e8db6e2SBrian Feldman 	ret = unlink(name);
7561e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
757b66f2d16SKris Kennaway 	send_status(id, status);
758b66f2d16SKris Kennaway 	xfree(name);
759b66f2d16SKris Kennaway }
760b66f2d16SKris Kennaway 
761ae1f160dSDag-Erling Smørgrav static void
762b66f2d16SKris Kennaway process_mkdir(void)
763b66f2d16SKris Kennaway {
764b66f2d16SKris Kennaway 	Attrib *a;
765b66f2d16SKris Kennaway 	u_int32_t id;
766b66f2d16SKris Kennaway 	char *name;
7671e8db6e2SBrian Feldman 	int ret, mode, status = SSH2_FX_FAILURE;
768b66f2d16SKris Kennaway 
769b66f2d16SKris Kennaway 	id = get_int();
770b66f2d16SKris Kennaway 	name = get_string(NULL);
771b66f2d16SKris Kennaway 	a = get_attrib();
7721e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
7731e8db6e2SBrian Feldman 	    a->perm & 0777 : 0777;
774ee21a45fSDag-Erling Smørgrav 	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
775b66f2d16SKris Kennaway 	ret = mkdir(name, mode);
7761e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
777b66f2d16SKris Kennaway 	send_status(id, status);
778b66f2d16SKris Kennaway 	xfree(name);
779b66f2d16SKris Kennaway }
780b66f2d16SKris Kennaway 
781ae1f160dSDag-Erling Smørgrav static void
782b66f2d16SKris Kennaway process_rmdir(void)
783b66f2d16SKris Kennaway {
784b66f2d16SKris Kennaway 	u_int32_t id;
785b66f2d16SKris Kennaway 	char *name;
786b66f2d16SKris Kennaway 	int ret, status;
787b66f2d16SKris Kennaway 
788b66f2d16SKris Kennaway 	id = get_int();
789b66f2d16SKris Kennaway 	name = get_string(NULL);
790ee21a45fSDag-Erling Smørgrav 	TRACE("rmdir id %u name %s", id, name);
791b66f2d16SKris Kennaway 	ret = rmdir(name);
7921e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
793b66f2d16SKris Kennaway 	send_status(id, status);
794b66f2d16SKris Kennaway 	xfree(name);
795b66f2d16SKris Kennaway }
796b66f2d16SKris Kennaway 
797ae1f160dSDag-Erling Smørgrav static void
798b66f2d16SKris Kennaway process_realpath(void)
799b66f2d16SKris Kennaway {
800b66f2d16SKris Kennaway 	char resolvedname[MAXPATHLEN];
801b66f2d16SKris Kennaway 	u_int32_t id;
802b66f2d16SKris Kennaway 	char *path;
803b66f2d16SKris Kennaway 
804b66f2d16SKris Kennaway 	id = get_int();
805b66f2d16SKris Kennaway 	path = get_string(NULL);
8061e8db6e2SBrian Feldman 	if (path[0] == '\0') {
8071e8db6e2SBrian Feldman 		xfree(path);
8081e8db6e2SBrian Feldman 		path = xstrdup(".");
8091e8db6e2SBrian Feldman 	}
810ee21a45fSDag-Erling Smørgrav 	TRACE("realpath id %u path %s", id, path);
811b66f2d16SKris Kennaway 	if (realpath(path, resolvedname) == NULL) {
812b66f2d16SKris Kennaway 		send_status(id, errno_to_portable(errno));
813b66f2d16SKris Kennaway 	} else {
814b66f2d16SKris Kennaway 		Stat s;
815b66f2d16SKris Kennaway 		attrib_clear(&s.attrib);
816b66f2d16SKris Kennaway 		s.name = s.long_name = resolvedname;
817b66f2d16SKris Kennaway 		send_names(id, 1, &s);
818b66f2d16SKris Kennaway 	}
819b66f2d16SKris Kennaway 	xfree(path);
820b66f2d16SKris Kennaway }
821b66f2d16SKris Kennaway 
822ae1f160dSDag-Erling Smørgrav static void
823b66f2d16SKris Kennaway process_rename(void)
824b66f2d16SKris Kennaway {
825b66f2d16SKris Kennaway 	u_int32_t id;
826b66f2d16SKris Kennaway 	char *oldpath, *newpath;
827d0c8c0bcSDag-Erling Smørgrav 	int status;
828d0c8c0bcSDag-Erling Smørgrav 	struct stat sb;
829b66f2d16SKris Kennaway 
830b66f2d16SKris Kennaway 	id = get_int();
831b66f2d16SKris Kennaway 	oldpath = get_string(NULL);
832b66f2d16SKris Kennaway 	newpath = get_string(NULL);
833ee21a45fSDag-Erling Smørgrav 	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
834d0c8c0bcSDag-Erling Smørgrav 	status = SSH2_FX_FAILURE;
835d0c8c0bcSDag-Erling Smørgrav 	if (lstat(oldpath, &sb) == -1)
836d0c8c0bcSDag-Erling Smørgrav 		status = errno_to_portable(errno);
837d0c8c0bcSDag-Erling Smørgrav 	else if (S_ISREG(sb.st_mode)) {
838d0c8c0bcSDag-Erling Smørgrav 		/* Race-free rename of regular files */
839d74d50a8SDag-Erling Smørgrav 		if (link(oldpath, newpath) == -1) {
840d74d50a8SDag-Erling Smørgrav 			if (errno == EOPNOTSUPP
841d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO
842d74d50a8SDag-Erling Smørgrav 			    || errno == LINK_OPNOTSUPP_ERRNO
843d74d50a8SDag-Erling Smørgrav #endif
844d74d50a8SDag-Erling Smørgrav 			    ) {
845d74d50a8SDag-Erling Smørgrav 				struct stat st;
846d74d50a8SDag-Erling Smørgrav 
847d74d50a8SDag-Erling Smørgrav 				/*
848d74d50a8SDag-Erling Smørgrav 				 * fs doesn't support links, so fall back to
849d74d50a8SDag-Erling Smørgrav 				 * stat+rename.  This is racy.
850d74d50a8SDag-Erling Smørgrav 				 */
851d74d50a8SDag-Erling Smørgrav 				if (stat(newpath, &st) == -1) {
852d74d50a8SDag-Erling Smørgrav 					if (rename(oldpath, newpath) == -1)
853d74d50a8SDag-Erling Smørgrav 						status =
854d74d50a8SDag-Erling Smørgrav 						    errno_to_portable(errno);
855d74d50a8SDag-Erling Smørgrav 					else
856d74d50a8SDag-Erling Smørgrav 						status = SSH2_FX_OK;
857d74d50a8SDag-Erling Smørgrav 				}
858d74d50a8SDag-Erling Smørgrav 			} else {
859d0c8c0bcSDag-Erling Smørgrav 				status = errno_to_portable(errno);
860d74d50a8SDag-Erling Smørgrav 			}
861d74d50a8SDag-Erling Smørgrav 		} else if (unlink(oldpath) == -1) {
862d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
863d0c8c0bcSDag-Erling Smørgrav 			/* clean spare link */
864d0c8c0bcSDag-Erling Smørgrav 			unlink(newpath);
865d0c8c0bcSDag-Erling Smørgrav 		} else
866d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
867d0c8c0bcSDag-Erling Smørgrav 	} else if (stat(newpath, &sb) == -1) {
868d0c8c0bcSDag-Erling Smørgrav 		if (rename(oldpath, newpath) == -1)
869d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
870d0c8c0bcSDag-Erling Smørgrav 		else
871d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
8721e8db6e2SBrian Feldman 	}
873b66f2d16SKris Kennaway 	send_status(id, status);
874b66f2d16SKris Kennaway 	xfree(oldpath);
875b66f2d16SKris Kennaway 	xfree(newpath);
876b66f2d16SKris Kennaway }
877b66f2d16SKris Kennaway 
878ae1f160dSDag-Erling Smørgrav static void
8791e8db6e2SBrian Feldman process_readlink(void)
8801e8db6e2SBrian Feldman {
8811e8db6e2SBrian Feldman 	u_int32_t id;
882ae1f160dSDag-Erling Smørgrav 	int len;
883d74d50a8SDag-Erling Smørgrav 	char buf[MAXPATHLEN];
8841e8db6e2SBrian Feldman 	char *path;
8851e8db6e2SBrian Feldman 
8861e8db6e2SBrian Feldman 	id = get_int();
8871e8db6e2SBrian Feldman 	path = get_string(NULL);
888ee21a45fSDag-Erling Smørgrav 	TRACE("readlink id %u path %s", id, path);
889d74d50a8SDag-Erling Smørgrav 	if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
8901e8db6e2SBrian Feldman 		send_status(id, errno_to_portable(errno));
8911e8db6e2SBrian Feldman 	else {
8921e8db6e2SBrian Feldman 		Stat s;
8931e8db6e2SBrian Feldman 
894d74d50a8SDag-Erling Smørgrav 		buf[len] = '\0';
8951e8db6e2SBrian Feldman 		attrib_clear(&s.attrib);
896d74d50a8SDag-Erling Smørgrav 		s.name = s.long_name = buf;
8971e8db6e2SBrian Feldman 		send_names(id, 1, &s);
8981e8db6e2SBrian Feldman 	}
8991e8db6e2SBrian Feldman 	xfree(path);
9001e8db6e2SBrian Feldman }
9011e8db6e2SBrian Feldman 
902ae1f160dSDag-Erling Smørgrav static void
9031e8db6e2SBrian Feldman process_symlink(void)
9041e8db6e2SBrian Feldman {
9051e8db6e2SBrian Feldman 	u_int32_t id;
9061e8db6e2SBrian Feldman 	char *oldpath, *newpath;
907d0c8c0bcSDag-Erling Smørgrav 	int ret, status;
9081e8db6e2SBrian Feldman 
9091e8db6e2SBrian Feldman 	id = get_int();
9101e8db6e2SBrian Feldman 	oldpath = get_string(NULL);
9111e8db6e2SBrian Feldman 	newpath = get_string(NULL);
912ee21a45fSDag-Erling Smørgrav 	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
913d0c8c0bcSDag-Erling Smørgrav 	/* this will fail if 'newpath' exists */
9141e8db6e2SBrian Feldman 	ret = symlink(oldpath, newpath);
9151e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
9161e8db6e2SBrian Feldman 	send_status(id, status);
9171e8db6e2SBrian Feldman 	xfree(oldpath);
9181e8db6e2SBrian Feldman 	xfree(newpath);
9191e8db6e2SBrian Feldman }
9201e8db6e2SBrian Feldman 
921ae1f160dSDag-Erling Smørgrav static void
9221e8db6e2SBrian Feldman process_extended(void)
9231e8db6e2SBrian Feldman {
9241e8db6e2SBrian Feldman 	u_int32_t id;
9251e8db6e2SBrian Feldman 	char *request;
9261e8db6e2SBrian Feldman 
9271e8db6e2SBrian Feldman 	id = get_int();
9281e8db6e2SBrian Feldman 	request = get_string(NULL);
9291e8db6e2SBrian Feldman 	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
9301e8db6e2SBrian Feldman 	xfree(request);
9311e8db6e2SBrian Feldman }
932b66f2d16SKris Kennaway 
933b66f2d16SKris Kennaway /* stolen from ssh-agent */
934b66f2d16SKris Kennaway 
935ae1f160dSDag-Erling Smørgrav static void
936b66f2d16SKris Kennaway process(void)
937b66f2d16SKris Kennaway {
9381e8db6e2SBrian Feldman 	u_int msg_len;
939545d5ecaSDag-Erling Smørgrav 	u_int buf_len;
940545d5ecaSDag-Erling Smørgrav 	u_int consumed;
9411e8db6e2SBrian Feldman 	u_int type;
9421e8db6e2SBrian Feldman 	u_char *cp;
943b66f2d16SKris Kennaway 
944545d5ecaSDag-Erling Smørgrav 	buf_len = buffer_len(&iqueue);
945545d5ecaSDag-Erling Smørgrav 	if (buf_len < 5)
946b66f2d16SKris Kennaway 		return;		/* Incomplete message. */
947ae1f160dSDag-Erling Smørgrav 	cp = buffer_ptr(&iqueue);
948b66f2d16SKris Kennaway 	msg_len = GET_32BIT(cp);
949021d409fSDag-Erling Smørgrav 	if (msg_len > SFTP_MAX_MSG_LENGTH) {
950b66f2d16SKris Kennaway 		error("bad message ");
951b66f2d16SKris Kennaway 		exit(11);
952b66f2d16SKris Kennaway 	}
953545d5ecaSDag-Erling Smørgrav 	if (buf_len < msg_len + 4)
954b66f2d16SKris Kennaway 		return;
955b66f2d16SKris Kennaway 	buffer_consume(&iqueue, 4);
956545d5ecaSDag-Erling Smørgrav 	buf_len -= 4;
957b66f2d16SKris Kennaway 	type = buffer_get_char(&iqueue);
958b66f2d16SKris Kennaway 	switch (type) {
9591e8db6e2SBrian Feldman 	case SSH2_FXP_INIT:
960b66f2d16SKris Kennaway 		process_init();
961b66f2d16SKris Kennaway 		break;
9621e8db6e2SBrian Feldman 	case SSH2_FXP_OPEN:
963b66f2d16SKris Kennaway 		process_open();
964b66f2d16SKris Kennaway 		break;
9651e8db6e2SBrian Feldman 	case SSH2_FXP_CLOSE:
966b66f2d16SKris Kennaway 		process_close();
967b66f2d16SKris Kennaway 		break;
9681e8db6e2SBrian Feldman 	case SSH2_FXP_READ:
969b66f2d16SKris Kennaway 		process_read();
970b66f2d16SKris Kennaway 		break;
9711e8db6e2SBrian Feldman 	case SSH2_FXP_WRITE:
972b66f2d16SKris Kennaway 		process_write();
973b66f2d16SKris Kennaway 		break;
9741e8db6e2SBrian Feldman 	case SSH2_FXP_LSTAT:
975b66f2d16SKris Kennaway 		process_lstat();
976b66f2d16SKris Kennaway 		break;
9771e8db6e2SBrian Feldman 	case SSH2_FXP_FSTAT:
978b66f2d16SKris Kennaway 		process_fstat();
979b66f2d16SKris Kennaway 		break;
9801e8db6e2SBrian Feldman 	case SSH2_FXP_SETSTAT:
981b66f2d16SKris Kennaway 		process_setstat();
982b66f2d16SKris Kennaway 		break;
9831e8db6e2SBrian Feldman 	case SSH2_FXP_FSETSTAT:
984b66f2d16SKris Kennaway 		process_fsetstat();
985b66f2d16SKris Kennaway 		break;
9861e8db6e2SBrian Feldman 	case SSH2_FXP_OPENDIR:
987b66f2d16SKris Kennaway 		process_opendir();
988b66f2d16SKris Kennaway 		break;
9891e8db6e2SBrian Feldman 	case SSH2_FXP_READDIR:
990b66f2d16SKris Kennaway 		process_readdir();
991b66f2d16SKris Kennaway 		break;
9921e8db6e2SBrian Feldman 	case SSH2_FXP_REMOVE:
993b66f2d16SKris Kennaway 		process_remove();
994b66f2d16SKris Kennaway 		break;
9951e8db6e2SBrian Feldman 	case SSH2_FXP_MKDIR:
996b66f2d16SKris Kennaway 		process_mkdir();
997b66f2d16SKris Kennaway 		break;
9981e8db6e2SBrian Feldman 	case SSH2_FXP_RMDIR:
999b66f2d16SKris Kennaway 		process_rmdir();
1000b66f2d16SKris Kennaway 		break;
10011e8db6e2SBrian Feldman 	case SSH2_FXP_REALPATH:
1002b66f2d16SKris Kennaway 		process_realpath();
1003b66f2d16SKris Kennaway 		break;
10041e8db6e2SBrian Feldman 	case SSH2_FXP_STAT:
1005b66f2d16SKris Kennaway 		process_stat();
1006b66f2d16SKris Kennaway 		break;
10071e8db6e2SBrian Feldman 	case SSH2_FXP_RENAME:
1008b66f2d16SKris Kennaway 		process_rename();
1009b66f2d16SKris Kennaway 		break;
10101e8db6e2SBrian Feldman 	case SSH2_FXP_READLINK:
10111e8db6e2SBrian Feldman 		process_readlink();
10121e8db6e2SBrian Feldman 		break;
10131e8db6e2SBrian Feldman 	case SSH2_FXP_SYMLINK:
10141e8db6e2SBrian Feldman 		process_symlink();
10151e8db6e2SBrian Feldman 		break;
10161e8db6e2SBrian Feldman 	case SSH2_FXP_EXTENDED:
10171e8db6e2SBrian Feldman 		process_extended();
10181e8db6e2SBrian Feldman 		break;
1019b66f2d16SKris Kennaway 	default:
1020b66f2d16SKris Kennaway 		error("Unknown message %d", type);
1021b66f2d16SKris Kennaway 		break;
1022b66f2d16SKris Kennaway 	}
1023545d5ecaSDag-Erling Smørgrav 	/* discard the remaining bytes from the current packet */
1024545d5ecaSDag-Erling Smørgrav 	if (buf_len < buffer_len(&iqueue))
1025545d5ecaSDag-Erling Smørgrav 		fatal("iqueue grows");
1026545d5ecaSDag-Erling Smørgrav 	consumed = buf_len - buffer_len(&iqueue);
1027545d5ecaSDag-Erling Smørgrav 	if (msg_len < consumed)
1028545d5ecaSDag-Erling Smørgrav 		fatal("msg_len %d < consumed %d", msg_len, consumed);
1029545d5ecaSDag-Erling Smørgrav 	if (msg_len > consumed)
1030545d5ecaSDag-Erling Smørgrav 		buffer_consume(&iqueue, msg_len - consumed);
1031b66f2d16SKris Kennaway }
1032b66f2d16SKris Kennaway 
1033b66f2d16SKris Kennaway int
1034b66f2d16SKris Kennaway main(int ac, char **av)
1035b66f2d16SKris Kennaway {
10361e8db6e2SBrian Feldman 	fd_set *rset, *wset;
1037b66f2d16SKris Kennaway 	int in, out, max;
10381e8db6e2SBrian Feldman 	ssize_t len, olen, set_size;
10391e8db6e2SBrian Feldman 
1040021d409fSDag-Erling Smørgrav 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1041021d409fSDag-Erling Smørgrav 	sanitise_stdfd();
1042021d409fSDag-Erling Smørgrav 
10431e8db6e2SBrian Feldman 	/* XXX should use getopt */
1044b66f2d16SKris Kennaway 
1045d95e11bfSDag-Erling Smørgrav 	__progname = ssh_get_progname(av[0]);
1046b66f2d16SKris Kennaway 	handle_init();
1047b66f2d16SKris Kennaway 
10481e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER
10491e8db6e2SBrian Feldman 	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
10501e8db6e2SBrian Feldman #endif
10511e8db6e2SBrian Feldman 
1052b66f2d16SKris Kennaway 	in = dup(STDIN_FILENO);
1053b66f2d16SKris Kennaway 	out = dup(STDOUT_FILENO);
1054b66f2d16SKris Kennaway 
105583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN
105683d2307dSDag-Erling Smørgrav 	setmode(in, O_BINARY);
105783d2307dSDag-Erling Smørgrav 	setmode(out, O_BINARY);
105883d2307dSDag-Erling Smørgrav #endif
105983d2307dSDag-Erling Smørgrav 
1060b66f2d16SKris Kennaway 	max = 0;
1061b66f2d16SKris Kennaway 	if (in > max)
1062b66f2d16SKris Kennaway 		max = in;
1063b66f2d16SKris Kennaway 	if (out > max)
1064b66f2d16SKris Kennaway 		max = out;
1065b66f2d16SKris Kennaway 
1066b66f2d16SKris Kennaway 	buffer_init(&iqueue);
1067b66f2d16SKris Kennaway 	buffer_init(&oqueue);
1068b66f2d16SKris Kennaway 
10691e8db6e2SBrian Feldman 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
10701e8db6e2SBrian Feldman 	rset = (fd_set *)xmalloc(set_size);
10711e8db6e2SBrian Feldman 	wset = (fd_set *)xmalloc(set_size);
1072b66f2d16SKris Kennaway 
10731e8db6e2SBrian Feldman 	for (;;) {
10741e8db6e2SBrian Feldman 		memset(rset, 0, set_size);
10751e8db6e2SBrian Feldman 		memset(wset, 0, set_size);
10761e8db6e2SBrian Feldman 
10771e8db6e2SBrian Feldman 		FD_SET(in, rset);
1078b66f2d16SKris Kennaway 		olen = buffer_len(&oqueue);
1079b66f2d16SKris Kennaway 		if (olen > 0)
10801e8db6e2SBrian Feldman 			FD_SET(out, wset);
1081b66f2d16SKris Kennaway 
10821e8db6e2SBrian Feldman 		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1083b66f2d16SKris Kennaway 			if (errno == EINTR)
1084b66f2d16SKris Kennaway 				continue;
1085b66f2d16SKris Kennaway 			exit(2);
1086b66f2d16SKris Kennaway 		}
1087b66f2d16SKris Kennaway 
1088b66f2d16SKris Kennaway 		/* copy stdin to iqueue */
10891e8db6e2SBrian Feldman 		if (FD_ISSET(in, rset)) {
1090b66f2d16SKris Kennaway 			char buf[4*4096];
1091b66f2d16SKris Kennaway 			len = read(in, buf, sizeof buf);
1092b66f2d16SKris Kennaway 			if (len == 0) {
1093b66f2d16SKris Kennaway 				debug("read eof");
1094b66f2d16SKris Kennaway 				exit(0);
1095b66f2d16SKris Kennaway 			} else if (len < 0) {
1096b66f2d16SKris Kennaway 				error("read error");
1097b66f2d16SKris Kennaway 				exit(1);
1098b66f2d16SKris Kennaway 			} else {
1099b66f2d16SKris Kennaway 				buffer_append(&iqueue, buf, len);
1100b66f2d16SKris Kennaway 			}
1101b66f2d16SKris Kennaway 		}
1102b66f2d16SKris Kennaway 		/* send oqueue to stdout */
11031e8db6e2SBrian Feldman 		if (FD_ISSET(out, wset)) {
1104b66f2d16SKris Kennaway 			len = write(out, buffer_ptr(&oqueue), olen);
1105b66f2d16SKris Kennaway 			if (len < 0) {
1106b66f2d16SKris Kennaway 				error("write error");
1107b66f2d16SKris Kennaway 				exit(1);
1108b66f2d16SKris Kennaway 			} else {
1109b66f2d16SKris Kennaway 				buffer_consume(&oqueue, len);
1110b66f2d16SKris Kennaway 			}
1111b66f2d16SKris Kennaway 		}
1112b66f2d16SKris Kennaway 		/* process requests from client */
1113b66f2d16SKris Kennaway 		process();
1114b66f2d16SKris Kennaway 	}
1115b66f2d16SKris Kennaway }
1116