xref: /freebsd/crypto/openssh/sftp-server.c (revision 545d5eca429a5967b3300cb527d49cae8184e79f)
1b66f2d16SKris Kennaway /*
2ae1f160dSDag-Erling Smørgrav  * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
3b66f2d16SKris Kennaway  *
4b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
5b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
6b66f2d16SKris Kennaway  * are met:
7b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
8b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
9b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
10b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
11b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
12b66f2d16SKris Kennaway  *
13b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23b66f2d16SKris Kennaway  */
24b66f2d16SKris Kennaway #include "includes.h"
25545d5ecaSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.35 2002/06/06 17:30:11 markus Exp $");
26b66f2d16SKris Kennaway 
27b66f2d16SKris Kennaway #include "buffer.h"
28b66f2d16SKris Kennaway #include "bufaux.h"
29b66f2d16SKris Kennaway #include "getput.h"
301e8db6e2SBrian Feldman #include "log.h"
31b66f2d16SKris Kennaway #include "xmalloc.h"
32b66f2d16SKris Kennaway 
331e8db6e2SBrian Feldman #include "sftp.h"
341e8db6e2SBrian Feldman #include "sftp-common.h"
35b66f2d16SKris Kennaway 
36b66f2d16SKris Kennaway /* helper */
371e8db6e2SBrian Feldman #define get_int64()			buffer_get_int64(&iqueue);
38b66f2d16SKris Kennaway #define get_int()			buffer_get_int(&iqueue);
39b66f2d16SKris Kennaway #define get_string(lenp)		buffer_get_string(&iqueue, lenp);
401e8db6e2SBrian Feldman #define TRACE				debug
41b66f2d16SKris Kennaway 
42b66f2d16SKris Kennaway /* input and output queue */
43b66f2d16SKris Kennaway Buffer iqueue;
44b66f2d16SKris Kennaway Buffer oqueue;
45b66f2d16SKris Kennaway 
461e8db6e2SBrian Feldman /* Version of client */
471e8db6e2SBrian Feldman int version;
481e8db6e2SBrian Feldman 
49b66f2d16SKris Kennaway /* portable attibutes, etc. */
50b66f2d16SKris Kennaway 
51b66f2d16SKris Kennaway typedef struct Stat Stat;
52b66f2d16SKris Kennaway 
531e8db6e2SBrian Feldman struct Stat {
54b66f2d16SKris Kennaway 	char *name;
55b66f2d16SKris Kennaway 	char *long_name;
56b66f2d16SKris Kennaway 	Attrib attrib;
57b66f2d16SKris Kennaway };
58b66f2d16SKris Kennaway 
59ae1f160dSDag-Erling Smørgrav static int
60b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
61b66f2d16SKris Kennaway {
62b66f2d16SKris Kennaway 	int ret = 0;
631e8db6e2SBrian Feldman 
64b66f2d16SKris Kennaway 	switch (unixerrno) {
65b66f2d16SKris Kennaway 	case 0:
661e8db6e2SBrian Feldman 		ret = SSH2_FX_OK;
67b66f2d16SKris Kennaway 		break;
68b66f2d16SKris Kennaway 	case ENOENT:
69b66f2d16SKris Kennaway 	case ENOTDIR:
70b66f2d16SKris Kennaway 	case EBADF:
71b66f2d16SKris Kennaway 	case ELOOP:
721e8db6e2SBrian Feldman 		ret = SSH2_FX_NO_SUCH_FILE;
73b66f2d16SKris Kennaway 		break;
74b66f2d16SKris Kennaway 	case EPERM:
75b66f2d16SKris Kennaway 	case EACCES:
76b66f2d16SKris Kennaway 	case EFAULT:
771e8db6e2SBrian Feldman 		ret = SSH2_FX_PERMISSION_DENIED;
78b66f2d16SKris Kennaway 		break;
79b66f2d16SKris Kennaway 	case ENAMETOOLONG:
80b66f2d16SKris Kennaway 	case EINVAL:
811e8db6e2SBrian Feldman 		ret = SSH2_FX_BAD_MESSAGE;
82b66f2d16SKris Kennaway 		break;
83b66f2d16SKris Kennaway 	default:
841e8db6e2SBrian Feldman 		ret = SSH2_FX_FAILURE;
85b66f2d16SKris Kennaway 		break;
86b66f2d16SKris Kennaway 	}
87b66f2d16SKris Kennaway 	return ret;
88b66f2d16SKris Kennaway }
89b66f2d16SKris Kennaway 
90ae1f160dSDag-Erling Smørgrav static int
91b66f2d16SKris Kennaway flags_from_portable(int pflags)
92b66f2d16SKris Kennaway {
93b66f2d16SKris Kennaway 	int flags = 0;
941e8db6e2SBrian Feldman 
951e8db6e2SBrian Feldman 	if ((pflags & SSH2_FXF_READ) &&
961e8db6e2SBrian Feldman 	    (pflags & SSH2_FXF_WRITE)) {
97b66f2d16SKris Kennaway 		flags = O_RDWR;
981e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_READ) {
99b66f2d16SKris Kennaway 		flags = O_RDONLY;
1001e8db6e2SBrian Feldman 	} else if (pflags & SSH2_FXF_WRITE) {
101b66f2d16SKris Kennaway 		flags = O_WRONLY;
102b66f2d16SKris Kennaway 	}
1031e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_CREAT)
104b66f2d16SKris Kennaway 		flags |= O_CREAT;
1051e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_TRUNC)
106b66f2d16SKris Kennaway 		flags |= O_TRUNC;
1071e8db6e2SBrian Feldman 	if (pflags & SSH2_FXF_EXCL)
108b66f2d16SKris Kennaway 		flags |= O_EXCL;
109b66f2d16SKris Kennaway 	return flags;
110b66f2d16SKris Kennaway }
111b66f2d16SKris Kennaway 
112ae1f160dSDag-Erling Smørgrav static Attrib *
113b66f2d16SKris Kennaway get_attrib(void)
114b66f2d16SKris Kennaway {
115b66f2d16SKris Kennaway 	return decode_attrib(&iqueue);
116b66f2d16SKris Kennaway }
117b66f2d16SKris Kennaway 
118b66f2d16SKris Kennaway /* handle handles */
119b66f2d16SKris Kennaway 
120b66f2d16SKris Kennaway typedef struct Handle Handle;
121b66f2d16SKris Kennaway struct Handle {
122b66f2d16SKris Kennaway 	int use;
123b66f2d16SKris Kennaway 	DIR *dirp;
124b66f2d16SKris Kennaway 	int fd;
125b66f2d16SKris Kennaway 	char *name;
126b66f2d16SKris Kennaway };
1271e8db6e2SBrian Feldman 
128b66f2d16SKris Kennaway enum {
129b66f2d16SKris Kennaway 	HANDLE_UNUSED,
130b66f2d16SKris Kennaway 	HANDLE_DIR,
131b66f2d16SKris Kennaway 	HANDLE_FILE
132b66f2d16SKris Kennaway };
1331e8db6e2SBrian Feldman 
134b66f2d16SKris Kennaway Handle	handles[100];
135b66f2d16SKris Kennaway 
136ae1f160dSDag-Erling Smørgrav static void
137b66f2d16SKris Kennaway handle_init(void)
138b66f2d16SKris Kennaway {
139b66f2d16SKris Kennaway 	int i;
1401e8db6e2SBrian Feldman 
141b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
142b66f2d16SKris Kennaway 		handles[i].use = HANDLE_UNUSED;
143b66f2d16SKris Kennaway }
144b66f2d16SKris Kennaway 
145ae1f160dSDag-Erling Smørgrav static int
146b66f2d16SKris Kennaway handle_new(int use, char *name, int fd, DIR *dirp)
147b66f2d16SKris Kennaway {
148b66f2d16SKris Kennaway 	int i;
1491e8db6e2SBrian Feldman 
150b66f2d16SKris Kennaway 	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
151b66f2d16SKris Kennaway 		if (handles[i].use == HANDLE_UNUSED) {
152b66f2d16SKris Kennaway 			handles[i].use = use;
153b66f2d16SKris Kennaway 			handles[i].dirp = dirp;
154b66f2d16SKris Kennaway 			handles[i].fd = fd;
155b66f2d16SKris Kennaway 			handles[i].name = name;
156b66f2d16SKris Kennaway 			return i;
157b66f2d16SKris Kennaway 		}
158b66f2d16SKris Kennaway 	}
159b66f2d16SKris Kennaway 	return -1;
160b66f2d16SKris Kennaway }
161b66f2d16SKris Kennaway 
162ae1f160dSDag-Erling Smørgrav static int
163b66f2d16SKris Kennaway handle_is_ok(int i, int type)
164b66f2d16SKris Kennaway {
1651e8db6e2SBrian Feldman 	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
1661e8db6e2SBrian Feldman 	    handles[i].use == type;
167b66f2d16SKris Kennaway }
168b66f2d16SKris Kennaway 
169ae1f160dSDag-Erling Smørgrav static int
170b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp)
171b66f2d16SKris Kennaway {
172b66f2d16SKris Kennaway 	if (stringp == NULL || hlenp == NULL)
173b66f2d16SKris Kennaway 		return -1;
1741e8db6e2SBrian Feldman 	*stringp = xmalloc(sizeof(int32_t));
1751e8db6e2SBrian Feldman 	PUT_32BIT(*stringp, handle);
1761e8db6e2SBrian Feldman 	*hlenp = sizeof(int32_t);
177b66f2d16SKris Kennaway 	return 0;
178b66f2d16SKris Kennaway }
179b66f2d16SKris Kennaway 
180ae1f160dSDag-Erling Smørgrav static int
181b66f2d16SKris Kennaway handle_from_string(char *handle, u_int hlen)
182b66f2d16SKris Kennaway {
1831e8db6e2SBrian Feldman 	int val;
1841e8db6e2SBrian Feldman 
1851e8db6e2SBrian Feldman 	if (hlen != sizeof(int32_t))
186b66f2d16SKris Kennaway 		return -1;
1871e8db6e2SBrian Feldman 	val = GET_32BIT(handle);
188b66f2d16SKris Kennaway 	if (handle_is_ok(val, HANDLE_FILE) ||
189b66f2d16SKris Kennaway 	    handle_is_ok(val, HANDLE_DIR))
190b66f2d16SKris Kennaway 		return val;
191b66f2d16SKris Kennaway 	return -1;
192b66f2d16SKris Kennaway }
193b66f2d16SKris Kennaway 
194ae1f160dSDag-Erling Smørgrav static char *
195b66f2d16SKris Kennaway handle_to_name(int handle)
196b66f2d16SKris Kennaway {
197b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR)||
198b66f2d16SKris Kennaway 	    handle_is_ok(handle, HANDLE_FILE))
199b66f2d16SKris Kennaway 		return handles[handle].name;
200b66f2d16SKris Kennaway 	return NULL;
201b66f2d16SKris Kennaway }
202b66f2d16SKris Kennaway 
203ae1f160dSDag-Erling Smørgrav static DIR *
204b66f2d16SKris Kennaway handle_to_dir(int handle)
205b66f2d16SKris Kennaway {
206b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR))
207b66f2d16SKris Kennaway 		return handles[handle].dirp;
208b66f2d16SKris Kennaway 	return NULL;
209b66f2d16SKris Kennaway }
210b66f2d16SKris Kennaway 
211ae1f160dSDag-Erling Smørgrav static int
212b66f2d16SKris Kennaway handle_to_fd(int handle)
213b66f2d16SKris Kennaway {
214b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE))
215b66f2d16SKris Kennaway 		return handles[handle].fd;
216b66f2d16SKris Kennaway 	return -1;
217b66f2d16SKris Kennaway }
218b66f2d16SKris Kennaway 
219ae1f160dSDag-Erling Smørgrav static int
220b66f2d16SKris Kennaway handle_close(int handle)
221b66f2d16SKris Kennaway {
222b66f2d16SKris Kennaway 	int ret = -1;
2231e8db6e2SBrian Feldman 
224b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE)) {
225b66f2d16SKris Kennaway 		ret = close(handles[handle].fd);
226b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
227b66f2d16SKris Kennaway 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
228b66f2d16SKris Kennaway 		ret = closedir(handles[handle].dirp);
229b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
230b66f2d16SKris Kennaway 	} else {
231b66f2d16SKris Kennaway 		errno = ENOENT;
232b66f2d16SKris Kennaway 	}
233b66f2d16SKris Kennaway 	return ret;
234b66f2d16SKris Kennaway }
235b66f2d16SKris Kennaway 
236ae1f160dSDag-Erling Smørgrav static int
237b66f2d16SKris Kennaway get_handle(void)
238b66f2d16SKris Kennaway {
239b66f2d16SKris Kennaway 	char *handle;
2401e8db6e2SBrian Feldman 	int val = -1;
241b66f2d16SKris Kennaway 	u_int hlen;
2421e8db6e2SBrian Feldman 
243b66f2d16SKris Kennaway 	handle = get_string(&hlen);
2441e8db6e2SBrian Feldman 	if (hlen < 256)
245b66f2d16SKris Kennaway 		val = handle_from_string(handle, hlen);
246b66f2d16SKris Kennaway 	xfree(handle);
247b66f2d16SKris Kennaway 	return val;
248b66f2d16SKris Kennaway }
249b66f2d16SKris Kennaway 
250b66f2d16SKris Kennaway /* send replies */
251b66f2d16SKris Kennaway 
252ae1f160dSDag-Erling Smørgrav static void
253b66f2d16SKris Kennaway send_msg(Buffer *m)
254b66f2d16SKris Kennaway {
255b66f2d16SKris Kennaway 	int mlen = buffer_len(m);
2561e8db6e2SBrian Feldman 
257b66f2d16SKris Kennaway 	buffer_put_int(&oqueue, mlen);
258b66f2d16SKris Kennaway 	buffer_append(&oqueue, buffer_ptr(m), mlen);
259b66f2d16SKris Kennaway 	buffer_consume(m, mlen);
260b66f2d16SKris Kennaway }
261b66f2d16SKris Kennaway 
262ae1f160dSDag-Erling Smørgrav static void
263b66f2d16SKris Kennaway send_status(u_int32_t id, u_int32_t error)
264b66f2d16SKris Kennaway {
265b66f2d16SKris Kennaway 	Buffer msg;
2661e8db6e2SBrian Feldman 	const char *status_messages[] = {
2671e8db6e2SBrian Feldman 		"Success",			/* SSH_FX_OK */
2681e8db6e2SBrian Feldman 		"End of file",			/* SSH_FX_EOF */
2691e8db6e2SBrian Feldman 		"No such file",			/* SSH_FX_NO_SUCH_FILE */
2701e8db6e2SBrian Feldman 		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
2711e8db6e2SBrian Feldman 		"Failure",			/* SSH_FX_FAILURE */
2721e8db6e2SBrian Feldman 		"Bad message",			/* SSH_FX_BAD_MESSAGE */
2731e8db6e2SBrian Feldman 		"No connection",		/* SSH_FX_NO_CONNECTION */
2741e8db6e2SBrian Feldman 		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
2751e8db6e2SBrian Feldman 		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
2761e8db6e2SBrian Feldman 		"Unknown error"			/* Others */
2771e8db6e2SBrian Feldman 	};
2781e8db6e2SBrian Feldman 
279b66f2d16SKris Kennaway 	TRACE("sent status id %d error %d", id, error);
280b66f2d16SKris Kennaway 	buffer_init(&msg);
2811e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_STATUS);
282b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
283b66f2d16SKris Kennaway 	buffer_put_int(&msg, error);
2841e8db6e2SBrian Feldman 	if (version >= 3) {
2851e8db6e2SBrian Feldman 		buffer_put_cstring(&msg,
2861e8db6e2SBrian Feldman 		    status_messages[MIN(error,SSH2_FX_MAX)]);
2871e8db6e2SBrian Feldman 		buffer_put_cstring(&msg, "");
2881e8db6e2SBrian Feldman 	}
289b66f2d16SKris Kennaway 	send_msg(&msg);
290b66f2d16SKris Kennaway 	buffer_free(&msg);
291b66f2d16SKris Kennaway }
292ae1f160dSDag-Erling Smørgrav static void
293b66f2d16SKris Kennaway send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
294b66f2d16SKris Kennaway {
295b66f2d16SKris Kennaway 	Buffer msg;
2961e8db6e2SBrian Feldman 
297b66f2d16SKris Kennaway 	buffer_init(&msg);
298b66f2d16SKris Kennaway 	buffer_put_char(&msg, type);
299b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
300b66f2d16SKris Kennaway 	buffer_put_string(&msg, data, dlen);
301b66f2d16SKris Kennaway 	send_msg(&msg);
302b66f2d16SKris Kennaway 	buffer_free(&msg);
303b66f2d16SKris Kennaway }
304b66f2d16SKris Kennaway 
305ae1f160dSDag-Erling Smørgrav static void
306b66f2d16SKris Kennaway send_data(u_int32_t id, char *data, int dlen)
307b66f2d16SKris Kennaway {
308b66f2d16SKris Kennaway 	TRACE("sent data id %d len %d", id, dlen);
3091e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
310b66f2d16SKris Kennaway }
311b66f2d16SKris Kennaway 
312ae1f160dSDag-Erling Smørgrav static void
313b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
314b66f2d16SKris Kennaway {
315b66f2d16SKris Kennaway 	char *string;
316b66f2d16SKris Kennaway 	int hlen;
3171e8db6e2SBrian Feldman 
318b66f2d16SKris Kennaway 	handle_to_string(handle, &string, &hlen);
319b66f2d16SKris Kennaway 	TRACE("sent handle id %d handle %d", id, handle);
3201e8db6e2SBrian Feldman 	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
321b66f2d16SKris Kennaway 	xfree(string);
322b66f2d16SKris Kennaway }
323b66f2d16SKris Kennaway 
324ae1f160dSDag-Erling Smørgrav static void
325b66f2d16SKris Kennaway send_names(u_int32_t id, int count, Stat *stats)
326b66f2d16SKris Kennaway {
327b66f2d16SKris Kennaway 	Buffer msg;
328b66f2d16SKris Kennaway 	int i;
3291e8db6e2SBrian Feldman 
330b66f2d16SKris Kennaway 	buffer_init(&msg);
3311e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_NAME);
332b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
333b66f2d16SKris Kennaway 	buffer_put_int(&msg, count);
334b66f2d16SKris Kennaway 	TRACE("sent names id %d count %d", id, count);
335b66f2d16SKris Kennaway 	for (i = 0; i < count; i++) {
336b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].name);
337b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].long_name);
338b66f2d16SKris Kennaway 		encode_attrib(&msg, &stats[i].attrib);
339b66f2d16SKris Kennaway 	}
340b66f2d16SKris Kennaway 	send_msg(&msg);
341b66f2d16SKris Kennaway 	buffer_free(&msg);
342b66f2d16SKris Kennaway }
343b66f2d16SKris Kennaway 
344ae1f160dSDag-Erling Smørgrav static void
345b66f2d16SKris Kennaway send_attrib(u_int32_t id, Attrib *a)
346b66f2d16SKris Kennaway {
347b66f2d16SKris Kennaway 	Buffer msg;
3481e8db6e2SBrian Feldman 
349b66f2d16SKris Kennaway 	TRACE("sent attrib id %d have 0x%x", id, a->flags);
350b66f2d16SKris Kennaway 	buffer_init(&msg);
3511e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_ATTRS);
352b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
353b66f2d16SKris Kennaway 	encode_attrib(&msg, a);
354b66f2d16SKris Kennaway 	send_msg(&msg);
355b66f2d16SKris Kennaway 	buffer_free(&msg);
356b66f2d16SKris Kennaway }
357b66f2d16SKris Kennaway 
358b66f2d16SKris Kennaway /* parse incoming */
359b66f2d16SKris Kennaway 
360ae1f160dSDag-Erling Smørgrav static void
361b66f2d16SKris Kennaway process_init(void)
362b66f2d16SKris Kennaway {
363b66f2d16SKris Kennaway 	Buffer msg;
364b66f2d16SKris Kennaway 
365545d5ecaSDag-Erling Smørgrav 	version = get_int();
366b66f2d16SKris Kennaway 	TRACE("client version %d", version);
367b66f2d16SKris Kennaway 	buffer_init(&msg);
3681e8db6e2SBrian Feldman 	buffer_put_char(&msg, SSH2_FXP_VERSION);
3691e8db6e2SBrian Feldman 	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
370b66f2d16SKris Kennaway 	send_msg(&msg);
371b66f2d16SKris Kennaway 	buffer_free(&msg);
372b66f2d16SKris Kennaway }
373b66f2d16SKris Kennaway 
374ae1f160dSDag-Erling Smørgrav static void
375b66f2d16SKris Kennaway process_open(void)
376b66f2d16SKris Kennaway {
377b66f2d16SKris Kennaway 	u_int32_t id, pflags;
378b66f2d16SKris Kennaway 	Attrib *a;
379b66f2d16SKris Kennaway 	char *name;
3801e8db6e2SBrian Feldman 	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
381b66f2d16SKris Kennaway 
382b66f2d16SKris Kennaway 	id = get_int();
383b66f2d16SKris Kennaway 	name = get_string(NULL);
3841e8db6e2SBrian Feldman 	pflags = get_int();		/* portable flags */
385b66f2d16SKris Kennaway 	a = get_attrib();
386b66f2d16SKris Kennaway 	flags = flags_from_portable(pflags);
3871e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
388b66f2d16SKris Kennaway 	TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
389b66f2d16SKris Kennaway 	fd = open(name, flags, mode);
390b66f2d16SKris Kennaway 	if (fd < 0) {
391b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
392b66f2d16SKris Kennaway 	} else {
393b66f2d16SKris Kennaway 		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
394b66f2d16SKris Kennaway 		if (handle < 0) {
395b66f2d16SKris Kennaway 			close(fd);
396b66f2d16SKris Kennaway 		} else {
397b66f2d16SKris Kennaway 			send_handle(id, handle);
3981e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
399b66f2d16SKris Kennaway 		}
400b66f2d16SKris Kennaway 	}
4011e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
402b66f2d16SKris Kennaway 		send_status(id, status);
403b66f2d16SKris Kennaway 	xfree(name);
404b66f2d16SKris Kennaway }
405b66f2d16SKris Kennaway 
406ae1f160dSDag-Erling Smørgrav static void
407b66f2d16SKris Kennaway process_close(void)
408b66f2d16SKris Kennaway {
409b66f2d16SKris Kennaway 	u_int32_t id;
4101e8db6e2SBrian Feldman 	int handle, ret, status = SSH2_FX_FAILURE;
411b66f2d16SKris Kennaway 
412b66f2d16SKris Kennaway 	id = get_int();
413b66f2d16SKris Kennaway 	handle = get_handle();
414b66f2d16SKris Kennaway 	TRACE("close id %d handle %d", id, handle);
415b66f2d16SKris Kennaway 	ret = handle_close(handle);
4161e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
417b66f2d16SKris Kennaway 	send_status(id, status);
418b66f2d16SKris Kennaway }
419b66f2d16SKris Kennaway 
420ae1f160dSDag-Erling Smørgrav static void
421b66f2d16SKris Kennaway process_read(void)
422b66f2d16SKris Kennaway {
423b66f2d16SKris Kennaway 	char buf[64*1024];
4241e8db6e2SBrian Feldman 	u_int32_t id, len;
4251e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
426b66f2d16SKris Kennaway 	u_int64_t off;
427b66f2d16SKris Kennaway 
428b66f2d16SKris Kennaway 	id = get_int();
429b66f2d16SKris Kennaway 	handle = get_handle();
4301e8db6e2SBrian Feldman 	off = get_int64();
431b66f2d16SKris Kennaway 	len = get_int();
432b66f2d16SKris Kennaway 
4331e8db6e2SBrian Feldman 	TRACE("read id %d handle %d off %llu len %d", id, handle,
4341e8db6e2SBrian Feldman 	    (unsigned long long)off, len);
435b66f2d16SKris Kennaway 	if (len > sizeof buf) {
436b66f2d16SKris Kennaway 		len = sizeof buf;
437b66f2d16SKris Kennaway 		log("read change len %d", len);
438b66f2d16SKris Kennaway 	}
439b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
440b66f2d16SKris Kennaway 	if (fd >= 0) {
441b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
442b66f2d16SKris Kennaway 			error("process_read: seek failed");
443b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
444b66f2d16SKris Kennaway 		} else {
445b66f2d16SKris Kennaway 			ret = read(fd, buf, len);
446b66f2d16SKris Kennaway 			if (ret < 0) {
447b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
448b66f2d16SKris Kennaway 			} else if (ret == 0) {
4491e8db6e2SBrian Feldman 				status = SSH2_FX_EOF;
450b66f2d16SKris Kennaway 			} else {
451b66f2d16SKris Kennaway 				send_data(id, buf, ret);
4521e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
453b66f2d16SKris Kennaway 			}
454b66f2d16SKris Kennaway 		}
455b66f2d16SKris Kennaway 	}
4561e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
457b66f2d16SKris Kennaway 		send_status(id, status);
458b66f2d16SKris Kennaway }
459b66f2d16SKris Kennaway 
460ae1f160dSDag-Erling Smørgrav static void
461b66f2d16SKris Kennaway process_write(void)
462b66f2d16SKris Kennaway {
4631e8db6e2SBrian Feldman 	u_int32_t id;
464b66f2d16SKris Kennaway 	u_int64_t off;
465b66f2d16SKris Kennaway 	u_int len;
4661e8db6e2SBrian Feldman 	int handle, fd, ret, status = SSH2_FX_FAILURE;
467b66f2d16SKris Kennaway 	char *data;
468b66f2d16SKris Kennaway 
469b66f2d16SKris Kennaway 	id = get_int();
470b66f2d16SKris Kennaway 	handle = get_handle();
4711e8db6e2SBrian Feldman 	off = get_int64();
472b66f2d16SKris Kennaway 	data = get_string(&len);
473b66f2d16SKris Kennaway 
4741e8db6e2SBrian Feldman 	TRACE("write id %d handle %d off %llu len %d", id, handle,
4751e8db6e2SBrian Feldman 	    (unsigned long long)off, len);
476b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
477b66f2d16SKris Kennaway 	if (fd >= 0) {
478b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
479b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
480b66f2d16SKris Kennaway 			error("process_write: seek failed");
481b66f2d16SKris Kennaway 		} else {
482b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
483b66f2d16SKris Kennaway 			ret = write(fd, data, len);
484b66f2d16SKris Kennaway 			if (ret == -1) {
485b66f2d16SKris Kennaway 				error("process_write: write failed");
486b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
487b66f2d16SKris Kennaway 			} else if (ret == len) {
4881e8db6e2SBrian Feldman 				status = SSH2_FX_OK;
489b66f2d16SKris Kennaway 			} else {
490b66f2d16SKris Kennaway 				log("nothing at all written");
491b66f2d16SKris Kennaway 			}
492b66f2d16SKris Kennaway 		}
493b66f2d16SKris Kennaway 	}
494b66f2d16SKris Kennaway 	send_status(id, status);
495b66f2d16SKris Kennaway 	xfree(data);
496b66f2d16SKris Kennaway }
497b66f2d16SKris Kennaway 
498ae1f160dSDag-Erling Smørgrav static void
499b66f2d16SKris Kennaway process_do_stat(int do_lstat)
500b66f2d16SKris Kennaway {
5011e8db6e2SBrian Feldman 	Attrib a;
502b66f2d16SKris Kennaway 	struct stat st;
503b66f2d16SKris Kennaway 	u_int32_t id;
504b66f2d16SKris Kennaway 	char *name;
5051e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
506b66f2d16SKris Kennaway 
507b66f2d16SKris Kennaway 	id = get_int();
508b66f2d16SKris Kennaway 	name = get_string(NULL);
509b66f2d16SKris Kennaway 	TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
510b66f2d16SKris Kennaway 	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
511b66f2d16SKris Kennaway 	if (ret < 0) {
512b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
513b66f2d16SKris Kennaway 	} else {
5141e8db6e2SBrian Feldman 		stat_to_attrib(&st, &a);
5151e8db6e2SBrian Feldman 		send_attrib(id, &a);
5161e8db6e2SBrian Feldman 		status = SSH2_FX_OK;
517b66f2d16SKris Kennaway 	}
5181e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
519b66f2d16SKris Kennaway 		send_status(id, status);
520b66f2d16SKris Kennaway 	xfree(name);
521b66f2d16SKris Kennaway }
522b66f2d16SKris Kennaway 
523ae1f160dSDag-Erling Smørgrav static void
524b66f2d16SKris Kennaway process_stat(void)
525b66f2d16SKris Kennaway {
526b66f2d16SKris Kennaway 	process_do_stat(0);
527b66f2d16SKris Kennaway }
528b66f2d16SKris Kennaway 
529ae1f160dSDag-Erling Smørgrav static void
530b66f2d16SKris Kennaway process_lstat(void)
531b66f2d16SKris Kennaway {
532b66f2d16SKris Kennaway 	process_do_stat(1);
533b66f2d16SKris Kennaway }
534b66f2d16SKris Kennaway 
535ae1f160dSDag-Erling Smørgrav static void
536b66f2d16SKris Kennaway process_fstat(void)
537b66f2d16SKris Kennaway {
5381e8db6e2SBrian Feldman 	Attrib a;
539b66f2d16SKris Kennaway 	struct stat st;
540b66f2d16SKris Kennaway 	u_int32_t id;
5411e8db6e2SBrian Feldman 	int fd, ret, handle, status = SSH2_FX_FAILURE;
542b66f2d16SKris Kennaway 
543b66f2d16SKris Kennaway 	id = get_int();
544b66f2d16SKris Kennaway 	handle = get_handle();
545b66f2d16SKris Kennaway 	TRACE("fstat id %d handle %d", id, handle);
546b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
547b66f2d16SKris Kennaway 	if (fd  >= 0) {
548b66f2d16SKris Kennaway 		ret = fstat(fd, &st);
549b66f2d16SKris Kennaway 		if (ret < 0) {
550b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
551b66f2d16SKris Kennaway 		} else {
5521e8db6e2SBrian Feldman 			stat_to_attrib(&st, &a);
5531e8db6e2SBrian Feldman 			send_attrib(id, &a);
5541e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
555b66f2d16SKris Kennaway 		}
556b66f2d16SKris Kennaway 	}
5571e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
558b66f2d16SKris Kennaway 		send_status(id, status);
559b66f2d16SKris Kennaway }
560b66f2d16SKris Kennaway 
561ae1f160dSDag-Erling Smørgrav static struct timeval *
562b66f2d16SKris Kennaway attrib_to_tv(Attrib *a)
563b66f2d16SKris Kennaway {
564b66f2d16SKris Kennaway 	static struct timeval tv[2];
5651e8db6e2SBrian Feldman 
566b66f2d16SKris Kennaway 	tv[0].tv_sec = a->atime;
567b66f2d16SKris Kennaway 	tv[0].tv_usec = 0;
568b66f2d16SKris Kennaway 	tv[1].tv_sec = a->mtime;
569b66f2d16SKris Kennaway 	tv[1].tv_usec = 0;
570b66f2d16SKris Kennaway 	return tv;
571b66f2d16SKris Kennaway }
572b66f2d16SKris Kennaway 
573ae1f160dSDag-Erling Smørgrav static void
574b66f2d16SKris Kennaway process_setstat(void)
575b66f2d16SKris Kennaway {
576b66f2d16SKris Kennaway 	Attrib *a;
577b66f2d16SKris Kennaway 	u_int32_t id;
578b66f2d16SKris Kennaway 	char *name;
579b66f2d16SKris Kennaway 	int ret;
5801e8db6e2SBrian Feldman 	int status = SSH2_FX_OK;
581b66f2d16SKris Kennaway 
582b66f2d16SKris Kennaway 	id = get_int();
583b66f2d16SKris Kennaway 	name = get_string(NULL);
584b66f2d16SKris Kennaway 	a = get_attrib();
585b66f2d16SKris Kennaway 	TRACE("setstat id %d name %s", id, name);
586ae1f160dSDag-Erling Smørgrav 	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
587ae1f160dSDag-Erling Smørgrav 		ret = truncate(name, a->size);
588ae1f160dSDag-Erling Smørgrav 		if (ret == -1)
589ae1f160dSDag-Erling Smørgrav 			status = errno_to_portable(errno);
590ae1f160dSDag-Erling Smørgrav 	}
5911e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
592b66f2d16SKris Kennaway 		ret = chmod(name, a->perm & 0777);
593b66f2d16SKris Kennaway 		if (ret == -1)
594b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
595b66f2d16SKris Kennaway 	}
5961e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
597b66f2d16SKris Kennaway 		ret = utimes(name, attrib_to_tv(a));
598b66f2d16SKris Kennaway 		if (ret == -1)
599b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
600b66f2d16SKris Kennaway 	}
6011e8db6e2SBrian Feldman 	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
6021e8db6e2SBrian Feldman 		ret = chown(name, a->uid, a->gid);
6031e8db6e2SBrian Feldman 		if (ret == -1)
6041e8db6e2SBrian Feldman 			status = errno_to_portable(errno);
6051e8db6e2SBrian Feldman 	}
606b66f2d16SKris Kennaway 	send_status(id, status);
607b66f2d16SKris Kennaway 	xfree(name);
608b66f2d16SKris Kennaway }
609b66f2d16SKris Kennaway 
610ae1f160dSDag-Erling Smørgrav static void
611b66f2d16SKris Kennaway process_fsetstat(void)
612b66f2d16SKris Kennaway {
613b66f2d16SKris Kennaway 	Attrib *a;
614b66f2d16SKris Kennaway 	u_int32_t id;
615b66f2d16SKris Kennaway 	int handle, fd, ret;
6161e8db6e2SBrian Feldman 	int status = SSH2_FX_OK;
617b66f2d16SKris Kennaway 
618b66f2d16SKris Kennaway 	id = get_int();
619b66f2d16SKris Kennaway 	handle = get_handle();
620b66f2d16SKris Kennaway 	a = get_attrib();
621b66f2d16SKris Kennaway 	TRACE("fsetstat id %d handle %d", id, handle);
622b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
623b66f2d16SKris Kennaway 	if (fd < 0) {
6241e8db6e2SBrian Feldman 		status = SSH2_FX_FAILURE;
625b66f2d16SKris Kennaway 	} else {
626ae1f160dSDag-Erling Smørgrav 		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
627ae1f160dSDag-Erling Smørgrav 			ret = ftruncate(fd, a->size);
628ae1f160dSDag-Erling Smørgrav 			if (ret == -1)
629ae1f160dSDag-Erling Smørgrav 				status = errno_to_portable(errno);
630ae1f160dSDag-Erling Smørgrav 		}
6311e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
632b66f2d16SKris Kennaway 			ret = fchmod(fd, a->perm & 0777);
633b66f2d16SKris Kennaway 			if (ret == -1)
634b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
635b66f2d16SKris Kennaway 		}
6361e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
637b66f2d16SKris Kennaway 			ret = futimes(fd, attrib_to_tv(a));
638b66f2d16SKris Kennaway 			if (ret == -1)
639b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
640b66f2d16SKris Kennaway 		}
6411e8db6e2SBrian Feldman 		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
6421e8db6e2SBrian Feldman 			ret = fchown(fd, a->uid, a->gid);
6431e8db6e2SBrian Feldman 			if (ret == -1)
6441e8db6e2SBrian Feldman 				status = errno_to_portable(errno);
6451e8db6e2SBrian Feldman 		}
646b66f2d16SKris Kennaway 	}
647b66f2d16SKris Kennaway 	send_status(id, status);
648b66f2d16SKris Kennaway }
649b66f2d16SKris Kennaway 
650ae1f160dSDag-Erling Smørgrav static void
651b66f2d16SKris Kennaway process_opendir(void)
652b66f2d16SKris Kennaway {
653b66f2d16SKris Kennaway 	DIR *dirp = NULL;
654b66f2d16SKris Kennaway 	char *path;
6551e8db6e2SBrian Feldman 	int handle, status = SSH2_FX_FAILURE;
656b66f2d16SKris Kennaway 	u_int32_t id;
657b66f2d16SKris Kennaway 
658b66f2d16SKris Kennaway 	id = get_int();
659b66f2d16SKris Kennaway 	path = get_string(NULL);
660b66f2d16SKris Kennaway 	TRACE("opendir id %d path %s", id, path);
661b66f2d16SKris Kennaway 	dirp = opendir(path);
662b66f2d16SKris Kennaway 	if (dirp == NULL) {
663b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
664b66f2d16SKris Kennaway 	} else {
665b66f2d16SKris Kennaway 		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
666b66f2d16SKris Kennaway 		if (handle < 0) {
667b66f2d16SKris Kennaway 			closedir(dirp);
668b66f2d16SKris Kennaway 		} else {
669b66f2d16SKris Kennaway 			send_handle(id, handle);
6701e8db6e2SBrian Feldman 			status = SSH2_FX_OK;
671b66f2d16SKris Kennaway 		}
672b66f2d16SKris Kennaway 
673b66f2d16SKris Kennaway 	}
6741e8db6e2SBrian Feldman 	if (status != SSH2_FX_OK)
675b66f2d16SKris Kennaway 		send_status(id, status);
676b66f2d16SKris Kennaway 	xfree(path);
677b66f2d16SKris Kennaway }
678b66f2d16SKris Kennaway 
6791e8db6e2SBrian Feldman /*
6801e8db6e2SBrian Feldman  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
6811e8db6e2SBrian Feldman  */
682ae1f160dSDag-Erling Smørgrav static char *
683b66f2d16SKris Kennaway ls_file(char *name, struct stat *st)
684b66f2d16SKris Kennaway {
685ae1f160dSDag-Erling Smørgrav 	int ulen, glen, sz = 0;
6861e8db6e2SBrian Feldman 	struct passwd *pw;
6871e8db6e2SBrian Feldman 	struct group *gr;
6881e8db6e2SBrian Feldman 	struct tm *ltime = localtime(&st->st_mtime);
6891e8db6e2SBrian Feldman 	char *user, *group;
6901e8db6e2SBrian Feldman 	char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
6911e8db6e2SBrian Feldman 
6921e8db6e2SBrian Feldman 	strmode(st->st_mode, mode);
6931e8db6e2SBrian Feldman 	if ((pw = getpwuid(st->st_uid)) != NULL) {
6941e8db6e2SBrian Feldman 		user = pw->pw_name;
6951e8db6e2SBrian Feldman 	} else {
6961e8db6e2SBrian Feldman 		snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
6971e8db6e2SBrian Feldman 		user = ubuf;
6981e8db6e2SBrian Feldman 	}
6991e8db6e2SBrian Feldman 	if ((gr = getgrgid(st->st_gid)) != NULL) {
7001e8db6e2SBrian Feldman 		group = gr->gr_name;
7011e8db6e2SBrian Feldman 	} else {
7021e8db6e2SBrian Feldman 		snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
7031e8db6e2SBrian Feldman 		group = gbuf;
7041e8db6e2SBrian Feldman 	}
7051e8db6e2SBrian Feldman 	if (ltime != NULL) {
7061e8db6e2SBrian Feldman 		if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
7071e8db6e2SBrian Feldman 			sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
7081e8db6e2SBrian Feldman 		else
7091e8db6e2SBrian Feldman 			sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
7101e8db6e2SBrian Feldman 	}
7111e8db6e2SBrian Feldman 	if (sz == 0)
7121e8db6e2SBrian Feldman 		tbuf[0] = '\0';
713ae1f160dSDag-Erling Smørgrav 	ulen = MAX(strlen(user), 8);
714ae1f160dSDag-Erling Smørgrav 	glen = MAX(strlen(group), 8);
715ae1f160dSDag-Erling Smørgrav 	snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
716ae1f160dSDag-Erling Smørgrav 	    st->st_nlink, ulen, user, glen, group,
717ae1f160dSDag-Erling Smørgrav 	    (unsigned long long)st->st_size, tbuf, name);
718b66f2d16SKris Kennaway 	return xstrdup(buf);
719b66f2d16SKris Kennaway }
720b66f2d16SKris Kennaway 
721ae1f160dSDag-Erling Smørgrav static void
722b66f2d16SKris Kennaway process_readdir(void)
723b66f2d16SKris Kennaway {
724b66f2d16SKris Kennaway 	DIR *dirp;
725b66f2d16SKris Kennaway 	struct dirent *dp;
726b66f2d16SKris Kennaway 	char *path;
727b66f2d16SKris Kennaway 	int handle;
728b66f2d16SKris Kennaway 	u_int32_t id;
729b66f2d16SKris Kennaway 
730b66f2d16SKris Kennaway 	id = get_int();
731b66f2d16SKris Kennaway 	handle = get_handle();
732b66f2d16SKris Kennaway 	TRACE("readdir id %d handle %d", id, handle);
733b66f2d16SKris Kennaway 	dirp = handle_to_dir(handle);
734b66f2d16SKris Kennaway 	path = handle_to_name(handle);
735b66f2d16SKris Kennaway 	if (dirp == NULL || path == NULL) {
7361e8db6e2SBrian Feldman 		send_status(id, SSH2_FX_FAILURE);
737b66f2d16SKris Kennaway 	} else {
738b66f2d16SKris Kennaway 		struct stat st;
739b66f2d16SKris Kennaway 		char pathname[1024];
740b66f2d16SKris Kennaway 		Stat *stats;
741b66f2d16SKris Kennaway 		int nstats = 10, count = 0, i;
742b66f2d16SKris Kennaway 		stats = xmalloc(nstats * sizeof(Stat));
743b66f2d16SKris Kennaway 		while ((dp = readdir(dirp)) != NULL) {
744b66f2d16SKris Kennaway 			if (count >= nstats) {
745b66f2d16SKris Kennaway 				nstats *= 2;
746b66f2d16SKris Kennaway 				stats = xrealloc(stats, nstats * sizeof(Stat));
747b66f2d16SKris Kennaway 			}
748b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
749ae1f160dSDag-Erling Smørgrav 			snprintf(pathname, sizeof pathname, "%s%s%s", path,
750ae1f160dSDag-Erling Smørgrav 			    strcmp(path, "/") ? "/" : "", dp->d_name);
751b66f2d16SKris Kennaway 			if (lstat(pathname, &st) < 0)
752b66f2d16SKris Kennaway 				continue;
7531e8db6e2SBrian Feldman 			stat_to_attrib(&st, &(stats[count].attrib));
754b66f2d16SKris Kennaway 			stats[count].name = xstrdup(dp->d_name);
755b66f2d16SKris Kennaway 			stats[count].long_name = ls_file(dp->d_name, &st);
756b66f2d16SKris Kennaway 			count++;
757b66f2d16SKris Kennaway 			/* send up to 100 entries in one message */
7581e8db6e2SBrian Feldman 			/* XXX check packet size instead */
759b66f2d16SKris Kennaway 			if (count == 100)
760b66f2d16SKris Kennaway 				break;
761b66f2d16SKris Kennaway 		}
7621e8db6e2SBrian Feldman 		if (count > 0) {
763b66f2d16SKris Kennaway 			send_names(id, count, stats);
764b66f2d16SKris Kennaway 			for (i = 0; i < count; i++) {
765b66f2d16SKris Kennaway 				xfree(stats[i].name);
766b66f2d16SKris Kennaway 				xfree(stats[i].long_name);
767b66f2d16SKris Kennaway 			}
7681e8db6e2SBrian Feldman 		} else {
7691e8db6e2SBrian Feldman 			send_status(id, SSH2_FX_EOF);
7701e8db6e2SBrian Feldman 		}
771b66f2d16SKris Kennaway 		xfree(stats);
772b66f2d16SKris Kennaway 	}
773b66f2d16SKris Kennaway }
774b66f2d16SKris Kennaway 
775ae1f160dSDag-Erling Smørgrav static void
776b66f2d16SKris Kennaway process_remove(void)
777b66f2d16SKris Kennaway {
778b66f2d16SKris Kennaway 	char *name;
779b66f2d16SKris Kennaway 	u_int32_t id;
7801e8db6e2SBrian Feldman 	int status = SSH2_FX_FAILURE;
781b66f2d16SKris Kennaway 	int ret;
782b66f2d16SKris Kennaway 
783b66f2d16SKris Kennaway 	id = get_int();
784b66f2d16SKris Kennaway 	name = get_string(NULL);
785b66f2d16SKris Kennaway 	TRACE("remove id %d name %s", id, name);
7861e8db6e2SBrian Feldman 	ret = unlink(name);
7871e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
788b66f2d16SKris Kennaway 	send_status(id, status);
789b66f2d16SKris Kennaway 	xfree(name);
790b66f2d16SKris Kennaway }
791b66f2d16SKris Kennaway 
792ae1f160dSDag-Erling Smørgrav static void
793b66f2d16SKris Kennaway process_mkdir(void)
794b66f2d16SKris Kennaway {
795b66f2d16SKris Kennaway 	Attrib *a;
796b66f2d16SKris Kennaway 	u_int32_t id;
797b66f2d16SKris Kennaway 	char *name;
7981e8db6e2SBrian Feldman 	int ret, mode, status = SSH2_FX_FAILURE;
799b66f2d16SKris Kennaway 
800b66f2d16SKris Kennaway 	id = get_int();
801b66f2d16SKris Kennaway 	name = get_string(NULL);
802b66f2d16SKris Kennaway 	a = get_attrib();
8031e8db6e2SBrian Feldman 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
8041e8db6e2SBrian Feldman 	    a->perm & 0777 : 0777;
805b66f2d16SKris Kennaway 	TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
806b66f2d16SKris Kennaway 	ret = mkdir(name, mode);
8071e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
808b66f2d16SKris Kennaway 	send_status(id, status);
809b66f2d16SKris Kennaway 	xfree(name);
810b66f2d16SKris Kennaway }
811b66f2d16SKris Kennaway 
812ae1f160dSDag-Erling Smørgrav static void
813b66f2d16SKris Kennaway process_rmdir(void)
814b66f2d16SKris Kennaway {
815b66f2d16SKris Kennaway 	u_int32_t id;
816b66f2d16SKris Kennaway 	char *name;
817b66f2d16SKris Kennaway 	int ret, status;
818b66f2d16SKris Kennaway 
819b66f2d16SKris Kennaway 	id = get_int();
820b66f2d16SKris Kennaway 	name = get_string(NULL);
821b66f2d16SKris Kennaway 	TRACE("rmdir id %d name %s", id, name);
822b66f2d16SKris Kennaway 	ret = rmdir(name);
8231e8db6e2SBrian Feldman 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
824b66f2d16SKris Kennaway 	send_status(id, status);
825b66f2d16SKris Kennaway 	xfree(name);
826b66f2d16SKris Kennaway }
827b66f2d16SKris Kennaway 
828ae1f160dSDag-Erling Smørgrav static void
829b66f2d16SKris Kennaway process_realpath(void)
830b66f2d16SKris Kennaway {
831b66f2d16SKris Kennaway 	char resolvedname[MAXPATHLEN];
832b66f2d16SKris Kennaway 	u_int32_t id;
833b66f2d16SKris Kennaway 	char *path;
834b66f2d16SKris Kennaway 
835b66f2d16SKris Kennaway 	id = get_int();
836b66f2d16SKris Kennaway 	path = get_string(NULL);
8371e8db6e2SBrian Feldman 	if (path[0] == '\0') {
8381e8db6e2SBrian Feldman 		xfree(path);
8391e8db6e2SBrian Feldman 		path = xstrdup(".");
8401e8db6e2SBrian Feldman 	}
841b66f2d16SKris Kennaway 	TRACE("realpath id %d path %s", id, path);
842b66f2d16SKris Kennaway 	if (realpath(path, resolvedname) == NULL) {
843b66f2d16SKris Kennaway 		send_status(id, errno_to_portable(errno));
844b66f2d16SKris Kennaway 	} else {
845b66f2d16SKris Kennaway 		Stat s;
846b66f2d16SKris Kennaway 		attrib_clear(&s.attrib);
847b66f2d16SKris Kennaway 		s.name = s.long_name = resolvedname;
848b66f2d16SKris Kennaway 		send_names(id, 1, &s);
849b66f2d16SKris Kennaway 	}
850b66f2d16SKris Kennaway 	xfree(path);
851b66f2d16SKris Kennaway }
852b66f2d16SKris Kennaway 
853ae1f160dSDag-Erling Smørgrav static void
854b66f2d16SKris Kennaway process_rename(void)
855b66f2d16SKris Kennaway {
856b66f2d16SKris Kennaway 	u_int32_t id;
8571e8db6e2SBrian Feldman 	struct stat st;
858b66f2d16SKris Kennaway 	char *oldpath, *newpath;
8591e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
860b66f2d16SKris Kennaway 
861b66f2d16SKris Kennaway 	id = get_int();
862b66f2d16SKris Kennaway 	oldpath = get_string(NULL);
863b66f2d16SKris Kennaway 	newpath = get_string(NULL);
864b66f2d16SKris Kennaway 	TRACE("rename id %d old %s new %s", id, oldpath, newpath);
8651e8db6e2SBrian Feldman 	/* fail if 'newpath' exists */
8661e8db6e2SBrian Feldman 	if (stat(newpath, &st) == -1) {
867b66f2d16SKris Kennaway 		ret = rename(oldpath, newpath);
8681e8db6e2SBrian Feldman 		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
8691e8db6e2SBrian Feldman 	}
870b66f2d16SKris Kennaway 	send_status(id, status);
871b66f2d16SKris Kennaway 	xfree(oldpath);
872b66f2d16SKris Kennaway 	xfree(newpath);
873b66f2d16SKris Kennaway }
874b66f2d16SKris Kennaway 
875ae1f160dSDag-Erling Smørgrav static void
8761e8db6e2SBrian Feldman process_readlink(void)
8771e8db6e2SBrian Feldman {
8781e8db6e2SBrian Feldman 	u_int32_t id;
879ae1f160dSDag-Erling Smørgrav 	int len;
8801e8db6e2SBrian Feldman 	char link[MAXPATHLEN];
8811e8db6e2SBrian Feldman 	char *path;
8821e8db6e2SBrian Feldman 
8831e8db6e2SBrian Feldman 	id = get_int();
8841e8db6e2SBrian Feldman 	path = get_string(NULL);
8851e8db6e2SBrian Feldman 	TRACE("readlink id %d path %s", id, path);
886ae1f160dSDag-Erling Smørgrav 	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
8871e8db6e2SBrian Feldman 		send_status(id, errno_to_portable(errno));
8881e8db6e2SBrian Feldman 	else {
8891e8db6e2SBrian Feldman 		Stat s;
8901e8db6e2SBrian Feldman 
891ae1f160dSDag-Erling Smørgrav 		link[len] = '\0';
8921e8db6e2SBrian Feldman 		attrib_clear(&s.attrib);
8931e8db6e2SBrian Feldman 		s.name = s.long_name = link;
8941e8db6e2SBrian Feldman 		send_names(id, 1, &s);
8951e8db6e2SBrian Feldman 	}
8961e8db6e2SBrian Feldman 	xfree(path);
8971e8db6e2SBrian Feldman }
8981e8db6e2SBrian Feldman 
899ae1f160dSDag-Erling Smørgrav static void
9001e8db6e2SBrian Feldman process_symlink(void)
9011e8db6e2SBrian Feldman {
9021e8db6e2SBrian Feldman 	u_int32_t id;
9031e8db6e2SBrian Feldman 	struct stat st;
9041e8db6e2SBrian Feldman 	char *oldpath, *newpath;
9051e8db6e2SBrian Feldman 	int ret, status = SSH2_FX_FAILURE;
9061e8db6e2SBrian Feldman 
9071e8db6e2SBrian Feldman 	id = get_int();
9081e8db6e2SBrian Feldman 	oldpath = get_string(NULL);
9091e8db6e2SBrian Feldman 	newpath = get_string(NULL);
9101e8db6e2SBrian Feldman 	TRACE("symlink id %d old %s new %s", id, oldpath, newpath);
9111e8db6e2SBrian Feldman 	/* fail if 'newpath' exists */
9121e8db6e2SBrian Feldman 	if (stat(newpath, &st) == -1) {
9131e8db6e2SBrian Feldman 		ret = symlink(oldpath, newpath);
9141e8db6e2SBrian Feldman 		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
9151e8db6e2SBrian Feldman 	}
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);
949b66f2d16SKris Kennaway 	if (msg_len > 256 * 1024) {
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 
10401e8db6e2SBrian Feldman 	/* XXX should use getopt */
1041b66f2d16SKris Kennaway 
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 
1051b66f2d16SKris Kennaway 	max = 0;
1052b66f2d16SKris Kennaway 	if (in > max)
1053b66f2d16SKris Kennaway 		max = in;
1054b66f2d16SKris Kennaway 	if (out > max)
1055b66f2d16SKris Kennaway 		max = out;
1056b66f2d16SKris Kennaway 
1057b66f2d16SKris Kennaway 	buffer_init(&iqueue);
1058b66f2d16SKris Kennaway 	buffer_init(&oqueue);
1059b66f2d16SKris Kennaway 
10601e8db6e2SBrian Feldman 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
10611e8db6e2SBrian Feldman 	rset = (fd_set *)xmalloc(set_size);
10621e8db6e2SBrian Feldman 	wset = (fd_set *)xmalloc(set_size);
1063b66f2d16SKris Kennaway 
10641e8db6e2SBrian Feldman 	for (;;) {
10651e8db6e2SBrian Feldman 		memset(rset, 0, set_size);
10661e8db6e2SBrian Feldman 		memset(wset, 0, set_size);
10671e8db6e2SBrian Feldman 
10681e8db6e2SBrian Feldman 		FD_SET(in, rset);
1069b66f2d16SKris Kennaway 		olen = buffer_len(&oqueue);
1070b66f2d16SKris Kennaway 		if (olen > 0)
10711e8db6e2SBrian Feldman 			FD_SET(out, wset);
1072b66f2d16SKris Kennaway 
10731e8db6e2SBrian Feldman 		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1074b66f2d16SKris Kennaway 			if (errno == EINTR)
1075b66f2d16SKris Kennaway 				continue;
1076b66f2d16SKris Kennaway 			exit(2);
1077b66f2d16SKris Kennaway 		}
1078b66f2d16SKris Kennaway 
1079b66f2d16SKris Kennaway 		/* copy stdin to iqueue */
10801e8db6e2SBrian Feldman 		if (FD_ISSET(in, rset)) {
1081b66f2d16SKris Kennaway 			char buf[4*4096];
1082b66f2d16SKris Kennaway 			len = read(in, buf, sizeof buf);
1083b66f2d16SKris Kennaway 			if (len == 0) {
1084b66f2d16SKris Kennaway 				debug("read eof");
1085b66f2d16SKris Kennaway 				exit(0);
1086b66f2d16SKris Kennaway 			} else if (len < 0) {
1087b66f2d16SKris Kennaway 				error("read error");
1088b66f2d16SKris Kennaway 				exit(1);
1089b66f2d16SKris Kennaway 			} else {
1090b66f2d16SKris Kennaway 				buffer_append(&iqueue, buf, len);
1091b66f2d16SKris Kennaway 			}
1092b66f2d16SKris Kennaway 		}
1093b66f2d16SKris Kennaway 		/* send oqueue to stdout */
10941e8db6e2SBrian Feldman 		if (FD_ISSET(out, wset)) {
1095b66f2d16SKris Kennaway 			len = write(out, buffer_ptr(&oqueue), olen);
1096b66f2d16SKris Kennaway 			if (len < 0) {
1097b66f2d16SKris Kennaway 				error("write error");
1098b66f2d16SKris Kennaway 				exit(1);
1099b66f2d16SKris Kennaway 			} else {
1100b66f2d16SKris Kennaway 				buffer_consume(&oqueue, len);
1101b66f2d16SKris Kennaway 			}
1102b66f2d16SKris Kennaway 		}
1103b66f2d16SKris Kennaway 		/* process requests from client */
1104b66f2d16SKris Kennaway 		process();
1105b66f2d16SKris Kennaway 	}
1106b66f2d16SKris Kennaway }
1107