xref: /freebsd/crypto/openssh/sftp-server.c (revision 043840df5be0cf8490b48a08fe6d9c316f473f58)
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"
17043840dfSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.48 2005/06/17 02:44:33 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"
24b66f2d16SKris Kennaway 
251e8db6e2SBrian Feldman #include "sftp.h"
261e8db6e2SBrian Feldman #include "sftp-common.h"
27b66f2d16SKris Kennaway 
28b66f2d16SKris Kennaway /* helper */
291e8db6e2SBrian Feldman #define get_int64()			buffer_get_int64(&iqueue);
30b66f2d16SKris Kennaway #define get_int()			buffer_get_int(&iqueue);
31b66f2d16SKris Kennaway #define get_string(lenp)		buffer_get_string(&iqueue, lenp);
321e8db6e2SBrian Feldman #define TRACE				debug
33b66f2d16SKris Kennaway 
3483d2307dSDag-Erling Smørgrav extern char *__progname;
3583d2307dSDag-Erling Smørgrav 
36b66f2d16SKris Kennaway /* input and output queue */
37b66f2d16SKris Kennaway Buffer iqueue;
38b66f2d16SKris Kennaway Buffer oqueue;
39b66f2d16SKris Kennaway 
401e8db6e2SBrian Feldman /* Version of client */
411e8db6e2SBrian Feldman int version;
421e8db6e2SBrian Feldman 
43d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */
44b66f2d16SKris Kennaway 
45b66f2d16SKris Kennaway typedef struct Stat Stat;
46b66f2d16SKris Kennaway 
471e8db6e2SBrian Feldman struct Stat {
48b66f2d16SKris Kennaway 	char *name;
49b66f2d16SKris Kennaway 	char *long_name;
50b66f2d16SKris Kennaway 	Attrib attrib;
51b66f2d16SKris Kennaway };
52b66f2d16SKris Kennaway 
53ae1f160dSDag-Erling Smørgrav static int
54b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
55b66f2d16SKris Kennaway {
56b66f2d16SKris Kennaway 	int ret = 0;
571e8db6e2SBrian Feldman 
58b66f2d16SKris Kennaway 	switch (unixerrno) {
59b66f2d16SKris Kennaway 	case 0:
601e8db6e2SBrian Feldman 		ret = SSH2_FX_OK;
61b66f2d16SKris Kennaway 		break;
62b66f2d16SKris Kennaway 	case ENOENT:
63b66f2d16SKris Kennaway 	case ENOTDIR:
64b66f2d16SKris Kennaway 	case EBADF:
65b66f2d16SKris Kennaway 	case ELOOP:
661e8db6e2SBrian Feldman 		ret = SSH2_FX_NO_SUCH_FILE;
67b66f2d16SKris Kennaway 		break;
68b66f2d16SKris Kennaway 	case EPERM:
69b66f2d16SKris Kennaway 	case EACCES:
70b66f2d16SKris Kennaway 	case EFAULT:
711e8db6e2SBrian Feldman 		ret = SSH2_FX_PERMISSION_DENIED;
72b66f2d16SKris Kennaway 		break;
73b66f2d16SKris Kennaway 	case ENAMETOOLONG:
74b66f2d16SKris Kennaway 	case EINVAL:
751e8db6e2SBrian Feldman 		ret = SSH2_FX_BAD_MESSAGE;
76b66f2d16SKris Kennaway 		break;
77b66f2d16SKris Kennaway 	default:
781e8db6e2SBrian Feldman 		ret = SSH2_FX_FAILURE;
79b66f2d16SKris Kennaway 		break;
80b66f2d16SKris Kennaway 	}
81b66f2d16SKris Kennaway 	return ret;
82b66f2d16SKris Kennaway }
83b66f2d16SKris Kennaway 
84ae1f160dSDag-Erling Smørgrav static int
85b66f2d16SKris Kennaway flags_from_portable(int pflags)
86b66f2d16SKris Kennaway {
87b66f2d16SKris Kennaway 	int flags = 0;
881e8db6e2SBrian Feldman 
891e8db6e2SBrian Feldman 	if ((pflags & SSH2_FXF_READ) &&
901e8db6e2SBrian Feldman 	    (pflags & SSH2_FXF_WRITE)) {
91b66f2d16SKris Kennaway 		flags = O_RDWR;
921e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_READ) {
93b66f2d16SKris Kennaway 		flags = O_RDONLY;
941e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_WRITE) {
95b66f2d16SKris Kennaway 		flags = O_WRONLY;
96b66f2d16SKris Kennaway 	}
971e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_CREAT)
98b66f2d16SKris Kennaway 		flags |= O_CREAT;
991e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_TRUNC)
100b66f2d16SKris Kennaway 		flags |= O_TRUNC;
1011e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_EXCL)
102b66f2d16SKris Kennaway 		flags |= O_EXCL;
103b66f2d16SKris Kennaway 	return flags;
104b66f2d16SKris Kennaway }
105b66f2d16SKris Kennaway 
106ae1f160dSDag-Erling Smørgrav static Attrib *
107b66f2d16SKris Kennaway get_attrib(void)
108b66f2d16SKris Kennaway {
109b66f2d16SKris Kennaway 	return decode_attrib(&iqueue);
110b66f2d16SKris Kennaway }
111b66f2d16SKris Kennaway 
112b66f2d16SKris Kennaway /* handle handles */
113b66f2d16SKris Kennaway 
114b66f2d16SKris Kennaway typedef struct Handle Handle;
115b66f2d16SKris Kennaway struct Handle {
116b66f2d16SKris Kennaway 	int use;
117b66f2d16SKris Kennaway 	DIR *dirp;
118b66f2d16SKris Kennaway 	int fd;
119b66f2d16SKris Kennaway 	char *name;
120b66f2d16SKris Kennaway };
1211e8db6e2SBrian Feldman 
122b66f2d16SKris Kennaway enum {
123b66f2d16SKris Kennaway 	HANDLE_UNUSED,
124b66f2d16SKris Kennaway 	HANDLE_DIR,
125b66f2d16SKris Kennaway 	HANDLE_FILE
126b66f2d16SKris Kennaway };
1271e8db6e2SBrian Feldman 
128b66f2d16SKris Kennaway Handle	handles[100];
129b66f2d16SKris Kennaway 
130ae1f160dSDag-Erling Smørgrav static void
131b66f2d16SKris Kennaway handle_init(void)
132b66f2d16SKris Kennaway {
133043840dfSDag-Erling Smørgrav 	u_int i;
1341e8db6e2SBrian Feldman 
135b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
136b66f2d16SKris Kennaway 		handles[i].use = HANDLE_UNUSED;
137b66f2d16SKris Kennaway }
138b66f2d16SKris Kennaway 
139ae1f160dSDag-Erling Smørgrav static int
140efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp)
141b66f2d16SKris Kennaway {
142043840dfSDag-Erling Smørgrav 	u_int i;
1431e8db6e2SBrian Feldman 
144b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
145b66f2d16SKris Kennaway 		if (handles[i].use == HANDLE_UNUSED) {
146b66f2d16SKris Kennaway 			handles[i].use = use;
147b66f2d16SKris Kennaway 			handles[i].dirp = dirp;
148b66f2d16SKris Kennaway 			handles[i].fd = fd;
149d0c8c0bcSDag-Erling Smørgrav 			handles[i].name = xstrdup(name);
150b66f2d16SKris Kennaway 			return i;
151b66f2d16SKris Kennaway 		}
152b66f2d16SKris Kennaway 	}
153b66f2d16SKris Kennaway 	return -1;
154b66f2d16SKris Kennaway }
155b66f2d16SKris Kennaway 
156ae1f160dSDag-Erling Smørgrav static int
157b66f2d16SKris Kennaway handle_is_ok(int i, int type)
158b66f2d16SKris Kennaway {
159043840dfSDag-Erling Smørgrav 	return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
1601e8db6e2SBrian Feldman 	    handles[i].use == type;
161b66f2d16SKris Kennaway }
162b66f2d16SKris Kennaway 
163ae1f160dSDag-Erling Smørgrav static int
164b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp)
165b66f2d16SKris Kennaway {
166b66f2d16SKris Kennaway 	if (stringp == NULL || hlenp == NULL)
167b66f2d16SKris Kennaway 		return -1;
1681e8db6e2SBrian Feldman 	*stringp = xmalloc(sizeof(int32_t));
1691e8db6e2SBrian Feldman 	PUT_32BIT(*stringp, handle);
1701e8db6e2SBrian Feldman 	*hlenp = sizeof(int32_t);
171b66f2d16SKris Kennaway 	return 0;
172b66f2d16SKris Kennaway }
173b66f2d16SKris Kennaway 
174ae1f160dSDag-Erling Smørgrav static int
175efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen)
176b66f2d16SKris Kennaway {
1771e8db6e2SBrian Feldman 	int val;
1781e8db6e2SBrian Feldman 
1791e8db6e2SBrian Feldman 	if (hlen != sizeof(int32_t))
180b66f2d16SKris Kennaway 		return -1;
1811e8db6e2SBrian Feldman 	val = GET_32BIT(handle);
182b66f2d16SKris Kennaway 	if (handle_is_ok(val, HANDLE_FILE) ||
183b66f2d16SKris Kennaway 	    handle_is_ok(val, HANDLE_DIR))
184b66f2d16SKris Kennaway 		return val;
185b66f2d16SKris Kennaway 	return -1;
186b66f2d16SKris Kennaway }
187b66f2d16SKris Kennaway 
188ae1f160dSDag-Erling Smørgrav static char *
189b66f2d16SKris Kennaway handle_to_name(int handle)
190b66f2d16SKris Kennaway {
191b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR)||
192b66f2d16SKris Kennaway 	    handle_is_ok(handle, HANDLE_FILE))
193b66f2d16SKris Kennaway 		return handles[handle].name;
194b66f2d16SKris Kennaway 	return NULL;
195b66f2d16SKris Kennaway }
196b66f2d16SKris Kennaway 
197ae1f160dSDag-Erling Smørgrav static DIR *
198b66f2d16SKris Kennaway handle_to_dir(int handle)
199b66f2d16SKris Kennaway {
200b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR))
201b66f2d16SKris Kennaway 		return handles[handle].dirp;
202b66f2d16SKris Kennaway 	return NULL;
203b66f2d16SKris Kennaway }
204b66f2d16SKris Kennaway 
205ae1f160dSDag-Erling Smørgrav static int
206b66f2d16SKris Kennaway handle_to_fd(int handle)
207b66f2d16SKris Kennaway {
208b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE))
209b66f2d16SKris Kennaway 		return handles[handle].fd;
210b66f2d16SKris Kennaway 	return -1;
211b66f2d16SKris Kennaway }
212b66f2d16SKris Kennaway 
213ae1f160dSDag-Erling Smørgrav static int
214b66f2d16SKris Kennaway handle_close(int handle)
215b66f2d16SKris Kennaway {
216b66f2d16SKris Kennaway 	int ret = -1;
2171e8db6e2SBrian Feldman 
218b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE)) {
219b66f2d16SKris Kennaway 		ret = close(handles[handle].fd);
220b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
221d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
222b66f2d16SKris Kennaway 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
223b66f2d16SKris Kennaway 		ret = closedir(handles[handle].dirp);
224b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
225d0c8c0bcSDag-Erling Smørgrav 		xfree(handles[handle].name);
226b66f2d16SKris Kennaway 	} else {
227b66f2d16SKris Kennaway 		errno = ENOENT;
228b66f2d16SKris Kennaway 	}
229b66f2d16SKris Kennaway 	return ret;
230b66f2d16SKris Kennaway }
231b66f2d16SKris Kennaway 
232ae1f160dSDag-Erling Smørgrav static int
233b66f2d16SKris Kennaway get_handle(void)
234b66f2d16SKris Kennaway {
235b66f2d16SKris Kennaway 	char *handle;
2361e8db6e2SBrian Feldman 	int val = -1;
237b66f2d16SKris Kennaway 	u_int hlen;
2381e8db6e2SBrian Feldman 
239b66f2d16SKris Kennaway 	handle = get_string(&hlen);
2401e8db6e2SBrian Feldman 	if (hlen < 256)
241b66f2d16SKris Kennaway 		val = handle_from_string(handle, hlen);
242b66f2d16SKris Kennaway 	xfree(handle);
243b66f2d16SKris Kennaway 	return val;
244b66f2d16SKris Kennaway }
245b66f2d16SKris Kennaway 
246b66f2d16SKris Kennaway /* send replies */
247b66f2d16SKris Kennaway 
248ae1f160dSDag-Erling Smørgrav static void
249b66f2d16SKris Kennaway send_msg(Buffer *m)
250b66f2d16SKris Kennaway {
251b66f2d16SKris Kennaway 	int mlen = buffer_len(m);
2521e8db6e2SBrian Feldman 
253b66f2d16SKris Kennaway 	buffer_put_int(&oqueue, mlen);
254b66f2d16SKris Kennaway 	buffer_append(&oqueue, buffer_ptr(m), mlen);
255b66f2d16SKris Kennaway 	buffer_consume(m, mlen);
256b66f2d16SKris Kennaway }
257b66f2d16SKris Kennaway 
258ae1f160dSDag-Erling Smørgrav static void
259d74d50a8SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status)
260b66f2d16SKris Kennaway {
261b66f2d16SKris Kennaway 	Buffer msg;
2621e8db6e2SBrian Feldman 	const char *status_messages[] = {
2631e8db6e2SBrian Feldman 		"Success",			/* SSH_FX_OK */
2641e8db6e2SBrian Feldman 		"End of file",			/* SSH_FX_EOF */
2651e8db6e2SBrian Feldman 		"No such file",			/* SSH_FX_NO_SUCH_FILE */
2661e8db6e2SBrian Feldman 		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
2671e8db6e2SBrian Feldman 		"Failure",			/* SSH_FX_FAILURE */
2681e8db6e2SBrian Feldman 		"Bad message",			/* SSH_FX_BAD_MESSAGE */
2691e8db6e2SBrian Feldman 		"No connection",		/* SSH_FX_NO_CONNECTION */
2701e8db6e2SBrian Feldman 		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
2711e8db6e2SBrian Feldman 		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
2721e8db6e2SBrian Feldman 		"Unknown error"			/* Others */
2731e8db6e2SBrian Feldman 	};
2741e8db6e2SBrian Feldman 
275d74d50a8SDag-Erling Smørgrav 	TRACE("sent status id %u error %u", id, status);
276b66f2d16SKris Kennaway 	buffer_init(&msg);
2771e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_STATUS);
278b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
279d74d50a8SDag-Erling Smørgrav 	buffer_put_int(&msg, status);
2801e8db6e2SBrian Feldman 	if (version >= 3) {
2811e8db6e2SBrian Feldman 		buffer_put_cstring(&msg,
282d74d50a8SDag-Erling Smørgrav 		    status_messages[MIN(status,SSH2_FX_MAX)]);
2831e8db6e2SBrian Feldman 		buffer_put_cstring(&msg, "");
2841e8db6e2SBrian Feldman 	}
285b66f2d16SKris Kennaway 	send_msg(&msg);
286b66f2d16SKris Kennaway 	buffer_free(&msg);
287b66f2d16SKris Kennaway }
288ae1f160dSDag-Erling Smørgrav static void
289efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
290b66f2d16SKris Kennaway {
291b66f2d16SKris Kennaway 	Buffer msg;
2921e8db6e2SBrian Feldman 
293b66f2d16SKris Kennaway 	buffer_init(&msg);
294b66f2d16SKris Kennaway 	buffer_put_char(&msg, type);
295b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
296b66f2d16SKris Kennaway 	buffer_put_string(&msg, data, dlen);
297b66f2d16SKris Kennaway 	send_msg(&msg);
298b66f2d16SKris Kennaway 	buffer_free(&msg);
299b66f2d16SKris Kennaway }
300b66f2d16SKris Kennaway 
301ae1f160dSDag-Erling Smørgrav static void
302efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen)
303b66f2d16SKris Kennaway {
304ee21a45fSDag-Erling Smørgrav 	TRACE("sent data id %u len %d", id, dlen);
3051e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
306b66f2d16SKris Kennaway }
307b66f2d16SKris Kennaway 
308ae1f160dSDag-Erling Smørgrav static void
309b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
310b66f2d16SKris Kennaway {
311b66f2d16SKris Kennaway 	char *string;
312b66f2d16SKris Kennaway 	int hlen;
3131e8db6e2SBrian Feldman 
314b66f2d16SKris Kennaway 	handle_to_string(handle, &string, &hlen);
315ee21a45fSDag-Erling Smørgrav 	TRACE("sent handle id %u handle %d", id, handle);
3161e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
317b66f2d16SKris Kennaway 	xfree(string);
318b66f2d16SKris Kennaway }
319b66f2d16SKris Kennaway 
320ae1f160dSDag-Erling Smørgrav static void
321efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats)
322b66f2d16SKris Kennaway {
323b66f2d16SKris Kennaway 	Buffer msg;
324b66f2d16SKris Kennaway 	int i;
3251e8db6e2SBrian Feldman 
326b66f2d16SKris Kennaway 	buffer_init(&msg);
3271e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_NAME);
328b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
329b66f2d16SKris Kennaway 	buffer_put_int(&msg, count);
330ee21a45fSDag-Erling Smørgrav 	TRACE("sent names id %u count %d", id, count);
331b66f2d16SKris Kennaway 	for (i = 0; i < count; i++) {
332b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].name);
333b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].long_name);
334b66f2d16SKris Kennaway 		encode_attrib(&msg, &stats[i].attrib);
335b66f2d16SKris Kennaway 	}
336b66f2d16SKris Kennaway 	send_msg(&msg);
337b66f2d16SKris Kennaway 	buffer_free(&msg);
338b66f2d16SKris Kennaway }
339b66f2d16SKris Kennaway 
340ae1f160dSDag-Erling Smørgrav static void
341efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a)
342b66f2d16SKris Kennaway {
343b66f2d16SKris Kennaway 	Buffer msg;
3441e8db6e2SBrian Feldman 
345ee21a45fSDag-Erling Smørgrav 	TRACE("sent attrib id %u have 0x%x", id, a->flags);
346b66f2d16SKris Kennaway 	buffer_init(&msg);
3471e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_ATTRS);
348b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
349b66f2d16SKris Kennaway 	encode_attrib(&msg, a);
350b66f2d16SKris Kennaway 	send_msg(&msg);
351b66f2d16SKris Kennaway 	buffer_free(&msg);
352b66f2d16SKris Kennaway }
353b66f2d16SKris Kennaway 
354b66f2d16SKris Kennaway /* parse incoming */
355b66f2d16SKris Kennaway 
356ae1f160dSDag-Erling Smørgrav static void
357b66f2d16SKris Kennaway process_init(void)
358b66f2d16SKris Kennaway {
359b66f2d16SKris Kennaway 	Buffer msg;
360b66f2d16SKris Kennaway 
361545d5ecaSDag-Erling Smørgrav 	version = get_int();
362b66f2d16SKris Kennaway 	TRACE("client version %d", version);
363b66f2d16SKris Kennaway 	buffer_init(&msg);
3641e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_VERSION);
3651e8db6e2SBrian Feldman 	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
366b66f2d16SKris Kennaway 	send_msg(&msg);
367b66f2d16SKris Kennaway 	buffer_free(&msg);
368b66f2d16SKris Kennaway }
369b66f2d16SKris Kennaway 
370ae1f160dSDag-Erling Smørgrav static void
371b66f2d16SKris Kennaway process_open(void)
372b66f2d16SKris Kennaway {
373b66f2d16SKris Kennaway 	u_int32_t id, pflags;
374b66f2d16SKris Kennaway 	Attrib *a;
375b66f2d16SKris Kennaway 	char *name;
3761e8db6e2SBrian Feldman 	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
377b66f2d16SKris Kennaway 
378b66f2d16SKris Kennaway 	id = get_int();
379b66f2d16SKris Kennaway 	name = get_string(NULL);
3801e8db6e2SBrian Feldman 	pflags = get_int();		/* portable flags */
381b66f2d16SKris Kennaway 	a = get_attrib();
382b66f2d16SKris Kennaway 	flags = flags_from_portable(pflags);
3831e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
384ee21a45fSDag-Erling Smørgrav 	TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
385b66f2d16SKris Kennaway 	fd = open(name, flags, mode);
386b66f2d16SKris Kennaway 	if (fd < 0) {
387b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
388b66f2d16SKris Kennaway 	} else {
389d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_FILE, name, fd, NULL);
390b66f2d16SKris Kennaway 		if (handle < 0) {
391b66f2d16SKris Kennaway 			close(fd);
392b66f2d16SKris Kennaway 		} else {
393b66f2d16SKris Kennaway 			send_handle(id, handle);
3941e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
395b66f2d16SKris Kennaway 		}
396b66f2d16SKris Kennaway 	}
3971e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
398b66f2d16SKris Kennaway 		send_status(id, status);
399b66f2d16SKris Kennaway 	xfree(name);
400b66f2d16SKris Kennaway }
401b66f2d16SKris Kennaway 
402ae1f160dSDag-Erling Smørgrav static void
403b66f2d16SKris Kennaway process_close(void)
404b66f2d16SKris Kennaway {
405b66f2d16SKris Kennaway 	u_int32_t id;
4061e8db6e2SBrian Feldman 	int handle, ret, status = SSH2_FX_FAILURE;
407b66f2d16SKris Kennaway 
408b66f2d16SKris Kennaway 	id = get_int();
409b66f2d16SKris Kennaway 	handle = get_handle();
410ee21a45fSDag-Erling Smørgrav 	TRACE("close id %u handle %d", id, handle);
411b66f2d16SKris Kennaway 	ret = handle_close(handle);
4121e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
413b66f2d16SKris Kennaway 	send_status(id, status);
414b66f2d16SKris Kennaway }
415b66f2d16SKris Kennaway 
416ae1f160dSDag-Erling Smørgrav static void
417b66f2d16SKris Kennaway process_read(void)
418b66f2d16SKris Kennaway {
419b66f2d16SKris Kennaway 	char buf[64*1024];
4201e8db6e2SBrian Feldman 	u_int32_t id, len;
4211e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
422b66f2d16SKris Kennaway 	u_int64_t off;
423b66f2d16SKris Kennaway 
424b66f2d16SKris Kennaway 	id = get_int();
425b66f2d16SKris Kennaway 	handle = get_handle();
4261e8db6e2SBrian Feldman 	off = get_int64();
427b66f2d16SKris Kennaway 	len = get_int();
428b66f2d16SKris Kennaway 
429ee21a45fSDag-Erling Smørgrav 	TRACE("read id %u handle %d off %llu len %d", id, handle,
43083d2307dSDag-Erling Smørgrav 	    (u_int64_t)off, len);
431b66f2d16SKris Kennaway 	if (len > sizeof buf) {
432b66f2d16SKris Kennaway 		len = sizeof buf;
433d95e11bfSDag-Erling Smørgrav 		logit("read change len %d", len);
434b66f2d16SKris Kennaway 	}
435b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
436b66f2d16SKris Kennaway 	if (fd >= 0) {
437b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
438b66f2d16SKris Kennaway 			error("process_read: seek failed");
439b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
440b66f2d16SKris Kennaway 		} else {
441b66f2d16SKris Kennaway 			ret = read(fd, buf, len);
442b66f2d16SKris Kennaway 			if (ret < 0) {
443b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
444b66f2d16SKris Kennaway 			} else if (ret == 0) {
4451e8db6e2SBrian Feldman 				status = SSH2_FX_EOF;
446b66f2d16SKris Kennaway 			} else {
447b66f2d16SKris Kennaway 				send_data(id, buf, ret);
4481e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
449b66f2d16SKris Kennaway 			}
450b66f2d16SKris Kennaway 		}
451b66f2d16SKris Kennaway 	}
4521e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
453b66f2d16SKris Kennaway 		send_status(id, status);
454b66f2d16SKris Kennaway }
455b66f2d16SKris Kennaway 
456ae1f160dSDag-Erling Smørgrav static void
457b66f2d16SKris Kennaway process_write(void)
458b66f2d16SKris Kennaway {
4591e8db6e2SBrian Feldman 	u_int32_t id;
460b66f2d16SKris Kennaway 	u_int64_t off;
461b66f2d16SKris Kennaway 	u_int len;
4621e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
463b66f2d16SKris Kennaway 	char *data;
464b66f2d16SKris Kennaway 
465b66f2d16SKris Kennaway 	id = get_int();
466b66f2d16SKris Kennaway 	handle = get_handle();
4671e8db6e2SBrian Feldman 	off = get_int64();
468b66f2d16SKris Kennaway 	data = get_string(&len);
469b66f2d16SKris Kennaway 
470ee21a45fSDag-Erling Smørgrav 	TRACE("write id %u handle %d off %llu len %d", id, handle,
47183d2307dSDag-Erling Smørgrav 	    (u_int64_t)off, len);
472b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
473b66f2d16SKris Kennaway 	if (fd >= 0) {
474b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
475b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
476b66f2d16SKris Kennaway 			error("process_write: seek failed");
477b66f2d16SKris Kennaway 		} else {
478b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
479b66f2d16SKris Kennaway 			ret = write(fd, data, len);
480043840dfSDag-Erling Smørgrav 			if (ret < 0) {
481b66f2d16SKris Kennaway 				error("process_write: write failed");
482b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
483043840dfSDag-Erling Smørgrav 			} else if ((size_t)ret == len) {
4841e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
485b66f2d16SKris Kennaway 			} else {
486d95e11bfSDag-Erling Smørgrav 				logit("nothing at all written");
487b66f2d16SKris Kennaway 			}
488b66f2d16SKris Kennaway 		}
489b66f2d16SKris Kennaway 	}
490b66f2d16SKris Kennaway 	send_status(id, status);
491b66f2d16SKris Kennaway 	xfree(data);
492b66f2d16SKris Kennaway }
493b66f2d16SKris Kennaway 
494ae1f160dSDag-Erling Smørgrav static void
495b66f2d16SKris Kennaway process_do_stat(int do_lstat)
496b66f2d16SKris Kennaway {
4971e8db6e2SBrian Feldman 	Attrib a;
498b66f2d16SKris Kennaway 	struct stat st;
499b66f2d16SKris Kennaway 	u_int32_t id;
500b66f2d16SKris Kennaway 	char *name;
5011e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
502b66f2d16SKris Kennaway 
503b66f2d16SKris Kennaway 	id = get_int();
504b66f2d16SKris Kennaway 	name = get_string(NULL);
505ee21a45fSDag-Erling Smørgrav 	TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
506b66f2d16SKris Kennaway 	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
507b66f2d16SKris Kennaway 	if (ret < 0) {
508b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
509b66f2d16SKris Kennaway 	} else {
5101e8db6e2SBrian Feldman 		stat_to_attrib(&st, &a);
5111e8db6e2SBrian Feldman 		send_attrib(id, &a);
5121e8db6e2SBrian Feldman 		status = SSH2_FX_OK;
513b66f2d16SKris Kennaway 	}
5141e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
515b66f2d16SKris Kennaway 		send_status(id, status);
516b66f2d16SKris Kennaway 	xfree(name);
517b66f2d16SKris Kennaway }
518b66f2d16SKris Kennaway 
519ae1f160dSDag-Erling Smørgrav static void
520b66f2d16SKris Kennaway process_stat(void)
521b66f2d16SKris Kennaway {
522b66f2d16SKris Kennaway 	process_do_stat(0);
523b66f2d16SKris Kennaway }
524b66f2d16SKris Kennaway 
525ae1f160dSDag-Erling Smørgrav static void
526b66f2d16SKris Kennaway process_lstat(void)
527b66f2d16SKris Kennaway {
528b66f2d16SKris Kennaway 	process_do_stat(1);
529b66f2d16SKris Kennaway }
530b66f2d16SKris Kennaway 
531ae1f160dSDag-Erling Smørgrav static void
532b66f2d16SKris Kennaway process_fstat(void)
533b66f2d16SKris Kennaway {
5341e8db6e2SBrian Feldman 	Attrib a;
535b66f2d16SKris Kennaway 	struct stat st;
536b66f2d16SKris Kennaway 	u_int32_t id;
5371e8db6e2SBrian Feldman 	int fd, ret, handle, status = SSH2_FX_FAILURE;
538b66f2d16SKris Kennaway 
539b66f2d16SKris Kennaway 	id = get_int();
540b66f2d16SKris Kennaway 	handle = get_handle();
541ee21a45fSDag-Erling Smørgrav 	TRACE("fstat id %u handle %d", id, handle);
542b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
543b66f2d16SKris Kennaway 	if (fd  >= 0) {
544b66f2d16SKris Kennaway 		ret = fstat(fd, &st);
545b66f2d16SKris Kennaway 		if (ret < 0) {
546b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
547b66f2d16SKris Kennaway 		} else {
5481e8db6e2SBrian Feldman 			stat_to_attrib(&st, &a);
5491e8db6e2SBrian Feldman 			send_attrib(id, &a);
5501e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
551b66f2d16SKris Kennaway 		}
552b66f2d16SKris Kennaway 	}
5531e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
554b66f2d16SKris Kennaway 		send_status(id, status);
555b66f2d16SKris Kennaway }
556b66f2d16SKris Kennaway 
557ae1f160dSDag-Erling Smørgrav static struct timeval *
558efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a)
559b66f2d16SKris Kennaway {
560b66f2d16SKris Kennaway 	static struct timeval tv[2];
5611e8db6e2SBrian Feldman 
562b66f2d16SKris Kennaway 	tv[0].tv_sec = a->atime;
563b66f2d16SKris Kennaway 	tv[0].tv_usec = 0;
564b66f2d16SKris Kennaway 	tv[1].tv_sec = a->mtime;
565b66f2d16SKris Kennaway 	tv[1].tv_usec = 0;
566b66f2d16SKris Kennaway 	return tv;
567b66f2d16SKris Kennaway }
568b66f2d16SKris Kennaway 
569ae1f160dSDag-Erling Smørgrav static void
570b66f2d16SKris Kennaway process_setstat(void)
571b66f2d16SKris Kennaway {
572b66f2d16SKris Kennaway 	Attrib *a;
573b66f2d16SKris Kennaway 	u_int32_t id;
574b66f2d16SKris Kennaway 	char *name;
575ee21a45fSDag-Erling Smørgrav 	int status = SSH2_FX_OK, ret;
576b66f2d16SKris Kennaway 
577b66f2d16SKris Kennaway 	id = get_int();
578b66f2d16SKris Kennaway 	name = get_string(NULL);
579b66f2d16SKris Kennaway 	a = get_attrib();
580ee21a45fSDag-Erling Smørgrav 	TRACE("setstat id %u name %s", id, name);
581ae1f160dSDag-Erling Smørgrav 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
582ae1f160dSDag-Erling Smørgrav 		ret = truncate(name, a->size);
583ae1f160dSDag-Erling Smørgrav 		if (ret == -1)
584ae1f160dSDag-Erling Smørgrav 			status = errno_to_portable(errno);
585ae1f160dSDag-Erling Smørgrav 	}
5861e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
587b66f2d16SKris Kennaway 		ret = chmod(name, a->perm & 0777);
588b66f2d16SKris Kennaway 		if (ret == -1)
589b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
590b66f2d16SKris Kennaway 	}
5911e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
592b66f2d16SKris Kennaway 		ret = utimes(name, attrib_to_tv(a));
593b66f2d16SKris Kennaway 		if (ret == -1)
594b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
595b66f2d16SKris Kennaway 	}
5961e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
5971e8db6e2SBrian Feldman 		ret = chown(name, a->uid, a->gid);
5981e8db6e2SBrian Feldman 		if (ret == -1)
5991e8db6e2SBrian Feldman 			status = errno_to_portable(errno);
6001e8db6e2SBrian Feldman 	}
601b66f2d16SKris Kennaway 	send_status(id, status);
602b66f2d16SKris Kennaway 	xfree(name);
603b66f2d16SKris Kennaway }
604b66f2d16SKris Kennaway 
605ae1f160dSDag-Erling Smørgrav static void
606b66f2d16SKris Kennaway process_fsetstat(void)
607b66f2d16SKris Kennaway {
608b66f2d16SKris Kennaway 	Attrib *a;
609b66f2d16SKris Kennaway 	u_int32_t id;
610b66f2d16SKris Kennaway 	int handle, fd, ret;
6111e8db6e2SBrian Feldman 	int status = SSH2_FX_OK;
61283d2307dSDag-Erling Smørgrav 	char *name;
613b66f2d16SKris Kennaway 
614b66f2d16SKris Kennaway 	id = get_int();
615b66f2d16SKris Kennaway 	handle = get_handle();
616b66f2d16SKris Kennaway 	a = get_attrib();
617ee21a45fSDag-Erling Smørgrav 	TRACE("fsetstat id %u handle %d", id, handle);
618b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
61983d2307dSDag-Erling Smørgrav 	name = handle_to_name(handle);
62083d2307dSDag-Erling Smørgrav 	if (fd < 0 || name == NULL) {
6211e8db6e2SBrian Feldman 		status = SSH2_FX_FAILURE;
622b66f2d16SKris Kennaway 	} else {
623ae1f160dSDag-Erling Smørgrav 		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
624ae1f160dSDag-Erling Smørgrav 			ret = ftruncate(fd, a->size);
625ae1f160dSDag-Erling Smørgrav 			if (ret == -1)
626ae1f160dSDag-Erling Smørgrav 				status = errno_to_portable(errno);
627ae1f160dSDag-Erling Smørgrav 		}
6281e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
62983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD
630b66f2d16SKris Kennaway 			ret = fchmod(fd, a->perm & 0777);
63183d2307dSDag-Erling Smørgrav #else
63283d2307dSDag-Erling Smørgrav 			ret = chmod(name, a->perm & 0777);
63383d2307dSDag-Erling Smørgrav #endif
634b66f2d16SKris Kennaway 			if (ret == -1)
635b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
636b66f2d16SKris Kennaway 		}
6371e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
63883d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES
639b66f2d16SKris Kennaway 			ret = futimes(fd, attrib_to_tv(a));
64083d2307dSDag-Erling Smørgrav #else
64183d2307dSDag-Erling Smørgrav 			ret = utimes(name, attrib_to_tv(a));
64283d2307dSDag-Erling Smørgrav #endif
643b66f2d16SKris Kennaway 			if (ret == -1)
644b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
645b66f2d16SKris Kennaway 		}
6461e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
64783d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN
6481e8db6e2SBrian Feldman 			ret = fchown(fd, a->uid, a->gid);
64983d2307dSDag-Erling Smørgrav #else
65083d2307dSDag-Erling Smørgrav 			ret = chown(name, a->uid, a->gid);
65183d2307dSDag-Erling Smørgrav #endif
6521e8db6e2SBrian Feldman 			if (ret == -1)
6531e8db6e2SBrian Feldman 				status = errno_to_portable(errno);
6541e8db6e2SBrian Feldman 		}
655b66f2d16SKris Kennaway 	}
656b66f2d16SKris Kennaway 	send_status(id, status);
657b66f2d16SKris Kennaway }
658b66f2d16SKris Kennaway 
659ae1f160dSDag-Erling Smørgrav static void
660b66f2d16SKris Kennaway process_opendir(void)
661b66f2d16SKris Kennaway {
662b66f2d16SKris Kennaway 	DIR *dirp = NULL;
663b66f2d16SKris Kennaway 	char *path;
6641e8db6e2SBrian Feldman 	int handle, status = SSH2_FX_FAILURE;
665b66f2d16SKris Kennaway 	u_int32_t id;
666b66f2d16SKris Kennaway 
667b66f2d16SKris Kennaway 	id = get_int();
668b66f2d16SKris Kennaway 	path = get_string(NULL);
669ee21a45fSDag-Erling Smørgrav 	TRACE("opendir id %u path %s", id, path);
670b66f2d16SKris Kennaway 	dirp = opendir(path);
671b66f2d16SKris Kennaway 	if (dirp == NULL) {
672b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
673b66f2d16SKris Kennaway 	} else {
674d0c8c0bcSDag-Erling Smørgrav 		handle = handle_new(HANDLE_DIR, path, 0, dirp);
675b66f2d16SKris Kennaway 		if (handle < 0) {
676b66f2d16SKris Kennaway 			closedir(dirp);
677b66f2d16SKris Kennaway 		} else {
678b66f2d16SKris Kennaway 			send_handle(id, handle);
6791e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
680b66f2d16SKris Kennaway 		}
681b66f2d16SKris Kennaway 
682b66f2d16SKris Kennaway 	}
6831e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
684b66f2d16SKris Kennaway 		send_status(id, status);
685b66f2d16SKris Kennaway 	xfree(path);
686b66f2d16SKris Kennaway }
687b66f2d16SKris Kennaway 
688ae1f160dSDag-Erling Smørgrav static void
689b66f2d16SKris Kennaway process_readdir(void)
690b66f2d16SKris Kennaway {
691b66f2d16SKris Kennaway 	DIR *dirp;
692b66f2d16SKris Kennaway 	struct dirent *dp;
693b66f2d16SKris Kennaway 	char *path;
694b66f2d16SKris Kennaway 	int handle;
695b66f2d16SKris Kennaway 	u_int32_t id;
696b66f2d16SKris Kennaway 
697b66f2d16SKris Kennaway 	id = get_int();
698b66f2d16SKris Kennaway 	handle = get_handle();
699ee21a45fSDag-Erling Smørgrav 	TRACE("readdir id %u handle %d", id, handle);
700b66f2d16SKris Kennaway 	dirp = handle_to_dir(handle);
701b66f2d16SKris Kennaway 	path = handle_to_name(handle);
702b66f2d16SKris Kennaway 	if (dirp == NULL || path == NULL) {
7031e8db6e2SBrian Feldman 		send_status(id, SSH2_FX_FAILURE);
704b66f2d16SKris Kennaway 	} else {
705b66f2d16SKris Kennaway 		struct stat st;
706b66f2d16SKris Kennaway 		char pathname[1024];
707b66f2d16SKris Kennaway 		Stat *stats;
708b66f2d16SKris Kennaway 		int nstats = 10, count = 0, i;
709ee21a45fSDag-Erling Smørgrav 
710b66f2d16SKris Kennaway 		stats = xmalloc(nstats * sizeof(Stat));
711b66f2d16SKris Kennaway 		while ((dp = readdir(dirp)) != NULL) {
712b66f2d16SKris Kennaway 			if (count >= nstats) {
713b66f2d16SKris Kennaway 				nstats *= 2;
714b66f2d16SKris Kennaway 				stats = xrealloc(stats, nstats * sizeof(Stat));
715b66f2d16SKris Kennaway 			}
716b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
717ae1f160dSDag-Erling Smørgrav 			snprintf(pathname, sizeof pathname, "%s%s%s", path,
718ae1f160dSDag-Erling Smørgrav 			    strcmp(path, "/") ? "/" : "", dp->d_name);
719b66f2d16SKris Kennaway 			if (lstat(pathname, &st) < 0)
720b66f2d16SKris Kennaway 				continue;
7211e8db6e2SBrian Feldman 			stat_to_attrib(&st, &(stats[count].attrib));
722b66f2d16SKris Kennaway 			stats[count].name = xstrdup(dp->d_name);
7234b17dab0SDag-Erling Smørgrav 			stats[count].long_name = ls_file(dp->d_name, &st, 0);
724b66f2d16SKris Kennaway 			count++;
725b66f2d16SKris Kennaway 			/* send up to 100 entries in one message */
7261e8db6e2SBrian Feldman 			/* XXX check packet size instead */
727b66f2d16SKris Kennaway 			if (count == 100)
728b66f2d16SKris Kennaway 				break;
729b66f2d16SKris Kennaway 		}
7301e8db6e2SBrian Feldman 		if (count > 0) {
731b66f2d16SKris Kennaway 			send_names(id, count, stats);
732b66f2d16SKris Kennaway 			for (i = 0; i < count; i++) {
733b66f2d16SKris Kennaway 				xfree(stats[i].name);
734b66f2d16SKris Kennaway 				xfree(stats[i].long_name);
735b66f2d16SKris Kennaway 			}
7361e8db6e2SBrian Feldman 		} else {
7371e8db6e2SBrian Feldman 			send_status(id, SSH2_FX_EOF);
7381e8db6e2SBrian Feldman 		}
739b66f2d16SKris Kennaway 		xfree(stats);
740b66f2d16SKris Kennaway 	}
741b66f2d16SKris Kennaway }
742b66f2d16SKris Kennaway 
743ae1f160dSDag-Erling Smørgrav static void
744b66f2d16SKris Kennaway process_remove(void)
745b66f2d16SKris Kennaway {
746b66f2d16SKris Kennaway 	char *name;
747b66f2d16SKris Kennaway 	u_int32_t id;
7481e8db6e2SBrian Feldman 	int status = SSH2_FX_FAILURE;
749b66f2d16SKris Kennaway 	int ret;
750b66f2d16SKris Kennaway 
751b66f2d16SKris Kennaway 	id = get_int();
752b66f2d16SKris Kennaway 	name = get_string(NULL);
753ee21a45fSDag-Erling Smørgrav 	TRACE("remove id %u name %s", id, name);
7541e8db6e2SBrian Feldman 	ret = unlink(name);
7551e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
756b66f2d16SKris Kennaway 	send_status(id, status);
757b66f2d16SKris Kennaway 	xfree(name);
758b66f2d16SKris Kennaway }
759b66f2d16SKris Kennaway 
760ae1f160dSDag-Erling Smørgrav static void
761b66f2d16SKris Kennaway process_mkdir(void)
762b66f2d16SKris Kennaway {
763b66f2d16SKris Kennaway 	Attrib *a;
764b66f2d16SKris Kennaway 	u_int32_t id;
765b66f2d16SKris Kennaway 	char *name;
7661e8db6e2SBrian Feldman 	int ret, mode, status = SSH2_FX_FAILURE;
767b66f2d16SKris Kennaway 
768b66f2d16SKris Kennaway 	id = get_int();
769b66f2d16SKris Kennaway 	name = get_string(NULL);
770b66f2d16SKris Kennaway 	a = get_attrib();
7711e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
7721e8db6e2SBrian Feldman 	    a->perm & 0777 : 0777;
773ee21a45fSDag-Erling Smørgrav 	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
774b66f2d16SKris Kennaway 	ret = mkdir(name, mode);
7751e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
776b66f2d16SKris Kennaway 	send_status(id, status);
777b66f2d16SKris Kennaway 	xfree(name);
778b66f2d16SKris Kennaway }
779b66f2d16SKris Kennaway 
780ae1f160dSDag-Erling Smørgrav static void
781b66f2d16SKris Kennaway process_rmdir(void)
782b66f2d16SKris Kennaway {
783b66f2d16SKris Kennaway 	u_int32_t id;
784b66f2d16SKris Kennaway 	char *name;
785b66f2d16SKris Kennaway 	int ret, status;
786b66f2d16SKris Kennaway 
787b66f2d16SKris Kennaway 	id = get_int();
788b66f2d16SKris Kennaway 	name = get_string(NULL);
789ee21a45fSDag-Erling Smørgrav 	TRACE("rmdir id %u name %s", id, name);
790b66f2d16SKris Kennaway 	ret = rmdir(name);
7911e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
792b66f2d16SKris Kennaway 	send_status(id, status);
793b66f2d16SKris Kennaway 	xfree(name);
794b66f2d16SKris Kennaway }
795b66f2d16SKris Kennaway 
796ae1f160dSDag-Erling Smørgrav static void
797b66f2d16SKris Kennaway process_realpath(void)
798b66f2d16SKris Kennaway {
799b66f2d16SKris Kennaway 	char resolvedname[MAXPATHLEN];
800b66f2d16SKris Kennaway 	u_int32_t id;
801b66f2d16SKris Kennaway 	char *path;
802b66f2d16SKris Kennaway 
803b66f2d16SKris Kennaway 	id = get_int();
804b66f2d16SKris Kennaway 	path = get_string(NULL);
8051e8db6e2SBrian Feldman 	if (path[0] == '\0') {
8061e8db6e2SBrian Feldman 		xfree(path);
8071e8db6e2SBrian Feldman 		path = xstrdup(".");
8081e8db6e2SBrian Feldman 	}
809ee21a45fSDag-Erling Smørgrav 	TRACE("realpath id %u path %s", id, path);
810b66f2d16SKris Kennaway 	if (realpath(path, resolvedname) == NULL) {
811b66f2d16SKris Kennaway 		send_status(id, errno_to_portable(errno));
812b66f2d16SKris Kennaway 	} else {
813b66f2d16SKris Kennaway 		Stat s;
814b66f2d16SKris Kennaway 		attrib_clear(&s.attrib);
815b66f2d16SKris Kennaway 		s.name = s.long_name = resolvedname;
816b66f2d16SKris Kennaway 		send_names(id, 1, &s);
817b66f2d16SKris Kennaway 	}
818b66f2d16SKris Kennaway 	xfree(path);
819b66f2d16SKris Kennaway }
820b66f2d16SKris Kennaway 
821ae1f160dSDag-Erling Smørgrav static void
822b66f2d16SKris Kennaway process_rename(void)
823b66f2d16SKris Kennaway {
824b66f2d16SKris Kennaway 	u_int32_t id;
825b66f2d16SKris Kennaway 	char *oldpath, *newpath;
826d0c8c0bcSDag-Erling Smørgrav 	int status;
827d0c8c0bcSDag-Erling Smørgrav 	struct stat sb;
828b66f2d16SKris Kennaway 
829b66f2d16SKris Kennaway 	id = get_int();
830b66f2d16SKris Kennaway 	oldpath = get_string(NULL);
831b66f2d16SKris Kennaway 	newpath = get_string(NULL);
832ee21a45fSDag-Erling Smørgrav 	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
833d0c8c0bcSDag-Erling Smørgrav 	status = SSH2_FX_FAILURE;
834d0c8c0bcSDag-Erling Smørgrav 	if (lstat(oldpath, &sb) == -1)
835d0c8c0bcSDag-Erling Smørgrav 		status = errno_to_portable(errno);
836d0c8c0bcSDag-Erling Smørgrav 	else if (S_ISREG(sb.st_mode)) {
837d0c8c0bcSDag-Erling Smørgrav 		/* Race-free rename of regular files */
838d74d50a8SDag-Erling Smørgrav 		if (link(oldpath, newpath) == -1) {
839d74d50a8SDag-Erling Smørgrav 			if (errno == EOPNOTSUPP
840d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO
841d74d50a8SDag-Erling Smørgrav 			    || errno == LINK_OPNOTSUPP_ERRNO
842d74d50a8SDag-Erling Smørgrav #endif
843d74d50a8SDag-Erling Smørgrav 			    ) {
844d74d50a8SDag-Erling Smørgrav 				struct stat st;
845d74d50a8SDag-Erling Smørgrav 
846d74d50a8SDag-Erling Smørgrav 				/*
847d74d50a8SDag-Erling Smørgrav 				 * fs doesn't support links, so fall back to
848d74d50a8SDag-Erling Smørgrav 				 * stat+rename.  This is racy.
849d74d50a8SDag-Erling Smørgrav 				 */
850d74d50a8SDag-Erling Smørgrav 				if (stat(newpath, &st) == -1) {
851d74d50a8SDag-Erling Smørgrav 					if (rename(oldpath, newpath) == -1)
852d74d50a8SDag-Erling Smørgrav 						status =
853d74d50a8SDag-Erling Smørgrav 						    errno_to_portable(errno);
854d74d50a8SDag-Erling Smørgrav 					else
855d74d50a8SDag-Erling Smørgrav 						status = SSH2_FX_OK;
856d74d50a8SDag-Erling Smørgrav 				}
857d74d50a8SDag-Erling Smørgrav 			} else {
858d0c8c0bcSDag-Erling Smørgrav 				status = errno_to_portable(errno);
859d74d50a8SDag-Erling Smørgrav 			}
860d74d50a8SDag-Erling Smørgrav 		} else if (unlink(oldpath) == -1) {
861d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
862d0c8c0bcSDag-Erling Smørgrav 			/* clean spare link */
863d0c8c0bcSDag-Erling Smørgrav 			unlink(newpath);
864d0c8c0bcSDag-Erling Smørgrav 		} else
865d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
866d0c8c0bcSDag-Erling Smørgrav 	} else if (stat(newpath, &sb) == -1) {
867d0c8c0bcSDag-Erling Smørgrav 		if (rename(oldpath, newpath) == -1)
868d0c8c0bcSDag-Erling Smørgrav 			status = errno_to_portable(errno);
869d0c8c0bcSDag-Erling Smørgrav 		else
870d0c8c0bcSDag-Erling Smørgrav 			status = SSH2_FX_OK;
8711e8db6e2SBrian Feldman 	}
872b66f2d16SKris Kennaway 	send_status(id, status);
873b66f2d16SKris Kennaway 	xfree(oldpath);
874b66f2d16SKris Kennaway 	xfree(newpath);
875b66f2d16SKris Kennaway }
876b66f2d16SKris Kennaway 
877ae1f160dSDag-Erling Smørgrav static void
8781e8db6e2SBrian Feldman process_readlink(void)
8791e8db6e2SBrian Feldman {
8801e8db6e2SBrian Feldman 	u_int32_t id;
881ae1f160dSDag-Erling Smørgrav 	int len;
882d74d50a8SDag-Erling Smørgrav 	char buf[MAXPATHLEN];
8831e8db6e2SBrian Feldman 	char *path;
8841e8db6e2SBrian Feldman 
8851e8db6e2SBrian Feldman 	id = get_int();
8861e8db6e2SBrian Feldman 	path = get_string(NULL);
887ee21a45fSDag-Erling Smørgrav 	TRACE("readlink id %u path %s", id, path);
888d74d50a8SDag-Erling Smørgrav 	if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
8891e8db6e2SBrian Feldman 		send_status(id, errno_to_portable(errno));
8901e8db6e2SBrian Feldman 	else {
8911e8db6e2SBrian Feldman 		Stat s;
8921e8db6e2SBrian Feldman 
893d74d50a8SDag-Erling Smørgrav 		buf[len] = '\0';
8941e8db6e2SBrian Feldman 		attrib_clear(&s.attrib);
895d74d50a8SDag-Erling Smørgrav 		s.name = s.long_name = buf;
8961e8db6e2SBrian Feldman 		send_names(id, 1, &s);
8971e8db6e2SBrian Feldman 	}
8981e8db6e2SBrian Feldman 	xfree(path);
8991e8db6e2SBrian Feldman }
9001e8db6e2SBrian Feldman 
901ae1f160dSDag-Erling Smørgrav static void
9021e8db6e2SBrian Feldman process_symlink(void)
9031e8db6e2SBrian Feldman {
9041e8db6e2SBrian Feldman 	u_int32_t id;
9051e8db6e2SBrian Feldman 	char *oldpath, *newpath;
906d0c8c0bcSDag-Erling Smørgrav 	int ret, status;
9071e8db6e2SBrian Feldman 
9081e8db6e2SBrian Feldman 	id = get_int();
9091e8db6e2SBrian Feldman 	oldpath = get_string(NULL);
9101e8db6e2SBrian Feldman 	newpath = get_string(NULL);
911ee21a45fSDag-Erling Smørgrav 	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
912d0c8c0bcSDag-Erling Smørgrav 	/* this will fail if 'newpath' exists */
9131e8db6e2SBrian Feldman 	ret = symlink(oldpath, newpath);
9141e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
9151e8db6e2SBrian Feldman 	send_status(id, status);
9161e8db6e2SBrian Feldman 	xfree(oldpath);
9171e8db6e2SBrian Feldman 	xfree(newpath);
9181e8db6e2SBrian Feldman }
9191e8db6e2SBrian Feldman 
920ae1f160dSDag-Erling Smørgrav static void
9211e8db6e2SBrian Feldman process_extended(void)
9221e8db6e2SBrian Feldman {
9231e8db6e2SBrian Feldman 	u_int32_t id;
9241e8db6e2SBrian Feldman 	char *request;
9251e8db6e2SBrian Feldman 
9261e8db6e2SBrian Feldman 	id = get_int();
9271e8db6e2SBrian Feldman 	request = get_string(NULL);
9281e8db6e2SBrian Feldman 	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
9291e8db6e2SBrian Feldman 	xfree(request);
9301e8db6e2SBrian Feldman }
931b66f2d16SKris Kennaway 
932b66f2d16SKris Kennaway /* stolen from ssh-agent */
933b66f2d16SKris Kennaway 
934ae1f160dSDag-Erling Smørgrav static void
935b66f2d16SKris Kennaway process(void)
936b66f2d16SKris Kennaway {
9371e8db6e2SBrian Feldman 	u_int msg_len;
938545d5ecaSDag-Erling Smørgrav 	u_int buf_len;
939545d5ecaSDag-Erling Smørgrav 	u_int consumed;
9401e8db6e2SBrian Feldman 	u_int type;
9411e8db6e2SBrian Feldman 	u_char *cp;
942b66f2d16SKris Kennaway 
943545d5ecaSDag-Erling Smørgrav 	buf_len = buffer_len(&iqueue);
944545d5ecaSDag-Erling Smørgrav 	if (buf_len < 5)
945b66f2d16SKris Kennaway 		return;		/* Incomplete message. */
946ae1f160dSDag-Erling Smørgrav 	cp = buffer_ptr(&iqueue);
947b66f2d16SKris Kennaway 	msg_len = GET_32BIT(cp);
948b66f2d16SKris Kennaway 	if (msg_len > 256 * 1024) {
949b66f2d16SKris Kennaway 		error("bad message ");
950b66f2d16SKris Kennaway 		exit(11);
951b66f2d16SKris Kennaway 	}
952545d5ecaSDag-Erling Smørgrav 	if (buf_len < msg_len + 4)
953b66f2d16SKris Kennaway 		return;
954b66f2d16SKris Kennaway 	buffer_consume(&iqueue, 4);
955545d5ecaSDag-Erling Smørgrav 	buf_len -= 4;
956b66f2d16SKris Kennaway 	type = buffer_get_char(&iqueue);
957b66f2d16SKris Kennaway 	switch (type) {
9581e8db6e2SBrian Feldman 	case SSH2_FXP_INIT:
959b66f2d16SKris Kennaway 		process_init();
960b66f2d16SKris Kennaway 		break;
9611e8db6e2SBrian Feldman 	case SSH2_FXP_OPEN:
962b66f2d16SKris Kennaway 		process_open();
963b66f2d16SKris Kennaway 		break;
9641e8db6e2SBrian Feldman 	case SSH2_FXP_CLOSE:
965b66f2d16SKris Kennaway 		process_close();
966b66f2d16SKris Kennaway 		break;
9671e8db6e2SBrian Feldman 	case SSH2_FXP_READ:
968b66f2d16SKris Kennaway 		process_read();
969b66f2d16SKris Kennaway 		break;
9701e8db6e2SBrian Feldman 	case SSH2_FXP_WRITE:
971b66f2d16SKris Kennaway 		process_write();
972b66f2d16SKris Kennaway 		break;
9731e8db6e2SBrian Feldman 	case SSH2_FXP_LSTAT:
974b66f2d16SKris Kennaway 		process_lstat();
975b66f2d16SKris Kennaway 		break;
9761e8db6e2SBrian Feldman 	case SSH2_FXP_FSTAT:
977b66f2d16SKris Kennaway 		process_fstat();
978b66f2d16SKris Kennaway 		break;
9791e8db6e2SBrian Feldman 	case SSH2_FXP_SETSTAT:
980b66f2d16SKris Kennaway 		process_setstat();
981b66f2d16SKris Kennaway 		break;
9821e8db6e2SBrian Feldman 	case SSH2_FXP_FSETSTAT:
983b66f2d16SKris Kennaway 		process_fsetstat();
984b66f2d16SKris Kennaway 		break;
9851e8db6e2SBrian Feldman 	case SSH2_FXP_OPENDIR:
986b66f2d16SKris Kennaway 		process_opendir();
987b66f2d16SKris Kennaway 		break;
9881e8db6e2SBrian Feldman 	case SSH2_FXP_READDIR:
989b66f2d16SKris Kennaway 		process_readdir();
990b66f2d16SKris Kennaway 		break;
9911e8db6e2SBrian Feldman 	case SSH2_FXP_REMOVE:
992b66f2d16SKris Kennaway 		process_remove();
993b66f2d16SKris Kennaway 		break;
9941e8db6e2SBrian Feldman 	case SSH2_FXP_MKDIR:
995b66f2d16SKris Kennaway 		process_mkdir();
996b66f2d16SKris Kennaway 		break;
9971e8db6e2SBrian Feldman 	case SSH2_FXP_RMDIR:
998b66f2d16SKris Kennaway 		process_rmdir();
999b66f2d16SKris Kennaway 		break;
10001e8db6e2SBrian Feldman 	case SSH2_FXP_REALPATH:
1001b66f2d16SKris Kennaway 		process_realpath();
1002b66f2d16SKris Kennaway 		break;
10031e8db6e2SBrian Feldman 	case SSH2_FXP_STAT:
1004b66f2d16SKris Kennaway 		process_stat();
1005b66f2d16SKris Kennaway 		break;
10061e8db6e2SBrian Feldman 	case SSH2_FXP_RENAME:
1007b66f2d16SKris Kennaway 		process_rename();
1008b66f2d16SKris Kennaway 		break;
10091e8db6e2SBrian Feldman 	case SSH2_FXP_READLINK:
10101e8db6e2SBrian Feldman 		process_readlink();
10111e8db6e2SBrian Feldman 		break;
10121e8db6e2SBrian Feldman 	case SSH2_FXP_SYMLINK:
10131e8db6e2SBrian Feldman 		process_symlink();
10141e8db6e2SBrian Feldman 		break;
10151e8db6e2SBrian Feldman 	case SSH2_FXP_EXTENDED:
10161e8db6e2SBrian Feldman 		process_extended();
10171e8db6e2SBrian Feldman 		break;
1018b66f2d16SKris Kennaway 	default:
1019b66f2d16SKris Kennaway 		error("Unknown message %d", type);
1020b66f2d16SKris Kennaway 		break;
1021b66f2d16SKris Kennaway 	}
1022545d5ecaSDag-Erling Smørgrav 	/* discard the remaining bytes from the current packet */
1023545d5ecaSDag-Erling Smørgrav 	if (buf_len < buffer_len(&iqueue))
1024545d5ecaSDag-Erling Smørgrav 		fatal("iqueue grows");
1025545d5ecaSDag-Erling Smørgrav 	consumed = buf_len - buffer_len(&iqueue);
1026545d5ecaSDag-Erling Smørgrav 	if (msg_len < consumed)
1027545d5ecaSDag-Erling Smørgrav 		fatal("msg_len %d < consumed %d", msg_len, consumed);
1028545d5ecaSDag-Erling Smørgrav 	if (msg_len > consumed)
1029545d5ecaSDag-Erling Smørgrav 		buffer_consume(&iqueue, msg_len - consumed);
1030b66f2d16SKris Kennaway }
1031b66f2d16SKris Kennaway 
1032b66f2d16SKris Kennaway int
1033b66f2d16SKris Kennaway main(int ac, char **av)
1034b66f2d16SKris Kennaway {
10351e8db6e2SBrian Feldman 	fd_set *rset, *wset;
1036b66f2d16SKris Kennaway 	int in, out, max;
10371e8db6e2SBrian Feldman 	ssize_t len, olen, set_size;
10381e8db6e2SBrian Feldman 
10391e8db6e2SBrian Feldman 	/* XXX should use getopt */
1040b66f2d16SKris Kennaway 
1041d95e11bfSDag-Erling Smørgrav 	__progname = ssh_get_progname(av[0]);
1042b66f2d16SKris Kennaway 	handle_init();
1043b66f2d16SKris Kennaway 
10441e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER
10451e8db6e2SBrian Feldman 	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
10461e8db6e2SBrian Feldman #endif
10471e8db6e2SBrian Feldman 
1048b66f2d16SKris Kennaway 	in = dup(STDIN_FILENO);
1049b66f2d16SKris Kennaway 	out = dup(STDOUT_FILENO);
1050b66f2d16SKris Kennaway 
105183d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN
105283d2307dSDag-Erling Smørgrav 	setmode(in, O_BINARY);
105383d2307dSDag-Erling Smørgrav 	setmode(out, O_BINARY);
105483d2307dSDag-Erling Smørgrav #endif
105583d2307dSDag-Erling Smørgrav 
1056b66f2d16SKris Kennaway 	max = 0;
1057b66f2d16SKris Kennaway 	if (in > max)
1058b66f2d16SKris Kennaway 		max = in;
1059b66f2d16SKris Kennaway 	if (out > max)
1060b66f2d16SKris Kennaway 		max = out;
1061b66f2d16SKris Kennaway 
1062b66f2d16SKris Kennaway 	buffer_init(&iqueue);
1063b66f2d16SKris Kennaway 	buffer_init(&oqueue);
1064b66f2d16SKris Kennaway 
10651e8db6e2SBrian Feldman 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
10661e8db6e2SBrian Feldman 	rset = (fd_set *)xmalloc(set_size);
10671e8db6e2SBrian Feldman 	wset = (fd_set *)xmalloc(set_size);
1068b66f2d16SKris Kennaway 
10691e8db6e2SBrian Feldman 	for (;;) {
10701e8db6e2SBrian Feldman 		memset(rset, 0, set_size);
10711e8db6e2SBrian Feldman 		memset(wset, 0, set_size);
10721e8db6e2SBrian Feldman 
10731e8db6e2SBrian Feldman 		FD_SET(in, rset);
1074b66f2d16SKris Kennaway 		olen = buffer_len(&oqueue);
1075b66f2d16SKris Kennaway 		if (olen > 0)
10761e8db6e2SBrian Feldman 			FD_SET(out, wset);
1077b66f2d16SKris Kennaway 
10781e8db6e2SBrian Feldman 		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1079b66f2d16SKris Kennaway 			if (errno == EINTR)
1080b66f2d16SKris Kennaway 				continue;
1081b66f2d16SKris Kennaway 			exit(2);
1082b66f2d16SKris Kennaway 		}
1083b66f2d16SKris Kennaway 
1084b66f2d16SKris Kennaway 		/* copy stdin to iqueue */
10851e8db6e2SBrian Feldman 		if (FD_ISSET(in, rset)) {
1086b66f2d16SKris Kennaway 			char buf[4*4096];
1087b66f2d16SKris Kennaway 			len = read(in, buf, sizeof buf);
1088b66f2d16SKris Kennaway 			if (len == 0) {
1089b66f2d16SKris Kennaway 				debug("read eof");
1090b66f2d16SKris Kennaway 				exit(0);
1091b66f2d16SKris Kennaway 			} else if (len < 0) {
1092b66f2d16SKris Kennaway 				error("read error");
1093b66f2d16SKris Kennaway 				exit(1);
1094b66f2d16SKris Kennaway 			} else {
1095b66f2d16SKris Kennaway 				buffer_append(&iqueue, buf, len);
1096b66f2d16SKris Kennaway 			}
1097b66f2d16SKris Kennaway 		}
1098b66f2d16SKris Kennaway 		/* send oqueue to stdout */
10991e8db6e2SBrian Feldman 		if (FD_ISSET(out, wset)) {
1100b66f2d16SKris Kennaway 			len = write(out, buffer_ptr(&oqueue), olen);
1101b66f2d16SKris Kennaway 			if (len < 0) {
1102b66f2d16SKris Kennaway 				error("write error");
1103b66f2d16SKris Kennaway 				exit(1);
1104b66f2d16SKris Kennaway 			} else {
1105b66f2d16SKris Kennaway 				buffer_consume(&oqueue, len);
1106b66f2d16SKris Kennaway 			}
1107b66f2d16SKris Kennaway 		}
1108b66f2d16SKris Kennaway 		/* process requests from client */
1109b66f2d16SKris Kennaway 		process();
1110b66f2d16SKris Kennaway 	}
1111b66f2d16SKris Kennaway }
1112