xref: /freebsd/crypto/openssh/sftp-server.c (revision b66f2d16a0435b7e7f3edde6101797004ae8d3b9)
1b66f2d16SKris Kennaway /*
2b66f2d16SKris Kennaway  * Copyright (c) 2000 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"
25b66f2d16SKris Kennaway RCSID("$OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp $");
26b66f2d16SKris Kennaway 
27b66f2d16SKris Kennaway #include "ssh.h"
28b66f2d16SKris Kennaway #include "buffer.h"
29b66f2d16SKris Kennaway #include "bufaux.h"
30b66f2d16SKris Kennaway #include "getput.h"
31b66f2d16SKris Kennaway #include "xmalloc.h"
32b66f2d16SKris Kennaway 
33b66f2d16SKris Kennaway /* version */
34b66f2d16SKris Kennaway #define	SSH_FILEXFER_VERSION		2
35b66f2d16SKris Kennaway 
36b66f2d16SKris Kennaway /* client to server */
37b66f2d16SKris Kennaway #define	SSH_FXP_INIT			1
38b66f2d16SKris Kennaway #define	SSH_FXP_OPEN			3
39b66f2d16SKris Kennaway #define	SSH_FXP_CLOSE			4
40b66f2d16SKris Kennaway #define	SSH_FXP_READ			5
41b66f2d16SKris Kennaway #define	SSH_FXP_WRITE			6
42b66f2d16SKris Kennaway #define	SSH_FXP_LSTAT			7
43b66f2d16SKris Kennaway #define	SSH_FXP_FSTAT			8
44b66f2d16SKris Kennaway #define	SSH_FXP_SETSTAT			9
45b66f2d16SKris Kennaway #define	SSH_FXP_FSETSTAT		10
46b66f2d16SKris Kennaway #define	SSH_FXP_OPENDIR			11
47b66f2d16SKris Kennaway #define	SSH_FXP_READDIR			12
48b66f2d16SKris Kennaway #define	SSH_FXP_REMOVE			13
49b66f2d16SKris Kennaway #define	SSH_FXP_MKDIR			14
50b66f2d16SKris Kennaway #define	SSH_FXP_RMDIR			15
51b66f2d16SKris Kennaway #define	SSH_FXP_REALPATH		16
52b66f2d16SKris Kennaway #define	SSH_FXP_STAT			17
53b66f2d16SKris Kennaway #define	SSH_FXP_RENAME			18
54b66f2d16SKris Kennaway 
55b66f2d16SKris Kennaway /* server to client */
56b66f2d16SKris Kennaway #define	SSH_FXP_VERSION			2
57b66f2d16SKris Kennaway #define	SSH_FXP_STATUS			101
58b66f2d16SKris Kennaway #define	SSH_FXP_HANDLE			102
59b66f2d16SKris Kennaway #define	SSH_FXP_DATA			103
60b66f2d16SKris Kennaway #define	SSH_FXP_NAME			104
61b66f2d16SKris Kennaway #define	SSH_FXP_ATTRS			105
62b66f2d16SKris Kennaway 
63b66f2d16SKris Kennaway /* portable open modes */
64b66f2d16SKris Kennaway #define	SSH_FXF_READ			0x01
65b66f2d16SKris Kennaway #define	SSH_FXF_WRITE			0x02
66b66f2d16SKris Kennaway #define	SSH_FXF_APPEND			0x04
67b66f2d16SKris Kennaway #define	SSH_FXF_CREAT			0x08
68b66f2d16SKris Kennaway #define	SSH_FXF_TRUNC			0x10
69b66f2d16SKris Kennaway #define	SSH_FXF_EXCL			0x20
70b66f2d16SKris Kennaway 
71b66f2d16SKris Kennaway /* attributes */
72b66f2d16SKris Kennaway #define	SSH_FXA_HAVE_SIZE		0x01
73b66f2d16SKris Kennaway #define	SSH_FXA_HAVE_UGID		0x02
74b66f2d16SKris Kennaway #define	SSH_FXA_HAVE_PERM		0x04
75b66f2d16SKris Kennaway #define	SSH_FXA_HAVE_TIME		0x08
76b66f2d16SKris Kennaway 
77b66f2d16SKris Kennaway /* status messages */
78b66f2d16SKris Kennaway #define	SSH_FX_OK			0x00
79b66f2d16SKris Kennaway #define	SSH_FX_EOF			0x01
80b66f2d16SKris Kennaway #define	SSH_FX_NO_SUCH_FILE		0x02
81b66f2d16SKris Kennaway #define	SSH_FX_PERMISSION_DENIED	0x03
82b66f2d16SKris Kennaway #define	SSH_FX_FAILURE			0x04
83b66f2d16SKris Kennaway #define	SSH_FX_BAD_MESSAGE		0x05
84b66f2d16SKris Kennaway #define	SSH_FX_NO_CONNECTION		0x06
85b66f2d16SKris Kennaway #define	SSH_FX_CONNECTION_LOST		0x07
86b66f2d16SKris Kennaway 
87b66f2d16SKris Kennaway 
88b66f2d16SKris Kennaway /* helper */
89b66f2d16SKris Kennaway #define get_int()			buffer_get_int(&iqueue);
90b66f2d16SKris Kennaway #define get_string(lenp)		buffer_get_string(&iqueue, lenp);
91b66f2d16SKris Kennaway #define TRACE				log
92b66f2d16SKris Kennaway 
93b66f2d16SKris Kennaway /* input and output queue */
94b66f2d16SKris Kennaway Buffer iqueue;
95b66f2d16SKris Kennaway Buffer oqueue;
96b66f2d16SKris Kennaway 
97b66f2d16SKris Kennaway /* portable attibutes, etc. */
98b66f2d16SKris Kennaway 
99b66f2d16SKris Kennaway typedef struct Attrib Attrib;
100b66f2d16SKris Kennaway typedef struct Stat Stat;
101b66f2d16SKris Kennaway 
102b66f2d16SKris Kennaway struct Attrib
103b66f2d16SKris Kennaway {
104b66f2d16SKris Kennaway 	u_int32_t	flags;
105b66f2d16SKris Kennaway 	u_int32_t	size_high;
106b66f2d16SKris Kennaway 	u_int32_t	size_low;
107b66f2d16SKris Kennaway 	u_int64_t	size;
108b66f2d16SKris Kennaway 	u_int32_t	uid;
109b66f2d16SKris Kennaway 	u_int32_t	gid;
110b66f2d16SKris Kennaway 	u_int32_t	perm;
111b66f2d16SKris Kennaway 	u_int32_t	atime;
112b66f2d16SKris Kennaway 	u_int32_t	mtime;
113b66f2d16SKris Kennaway };
114b66f2d16SKris Kennaway 
115b66f2d16SKris Kennaway struct Stat
116b66f2d16SKris Kennaway {
117b66f2d16SKris Kennaway 	char *name;
118b66f2d16SKris Kennaway 	char *long_name;
119b66f2d16SKris Kennaway 	Attrib attrib;
120b66f2d16SKris Kennaway };
121b66f2d16SKris Kennaway 
122b66f2d16SKris Kennaway int
123b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
124b66f2d16SKris Kennaway {
125b66f2d16SKris Kennaway 	int ret = 0;
126b66f2d16SKris Kennaway 	switch (unixerrno) {
127b66f2d16SKris Kennaway 	case 0:
128b66f2d16SKris Kennaway 		ret = SSH_FX_OK;
129b66f2d16SKris Kennaway 		break;
130b66f2d16SKris Kennaway 	case ENOENT:
131b66f2d16SKris Kennaway 	case ENOTDIR:
132b66f2d16SKris Kennaway 	case EBADF:
133b66f2d16SKris Kennaway 	case ELOOP:
134b66f2d16SKris Kennaway 		ret = SSH_FX_NO_SUCH_FILE;
135b66f2d16SKris Kennaway 		break;
136b66f2d16SKris Kennaway 	case EPERM:
137b66f2d16SKris Kennaway 	case EACCES:
138b66f2d16SKris Kennaway 	case EFAULT:
139b66f2d16SKris Kennaway 		ret = SSH_FX_PERMISSION_DENIED;
140b66f2d16SKris Kennaway 		break;
141b66f2d16SKris Kennaway 	case ENAMETOOLONG:
142b66f2d16SKris Kennaway 	case EINVAL:
143b66f2d16SKris Kennaway 		ret = SSH_FX_BAD_MESSAGE;
144b66f2d16SKris Kennaway 		break;
145b66f2d16SKris Kennaway 	default:
146b66f2d16SKris Kennaway 		ret = SSH_FX_FAILURE;
147b66f2d16SKris Kennaway 		break;
148b66f2d16SKris Kennaway 	}
149b66f2d16SKris Kennaway 	return ret;
150b66f2d16SKris Kennaway }
151b66f2d16SKris Kennaway 
152b66f2d16SKris Kennaway int
153b66f2d16SKris Kennaway flags_from_portable(int pflags)
154b66f2d16SKris Kennaway {
155b66f2d16SKris Kennaway 	int flags = 0;
156b66f2d16SKris Kennaway 	if (pflags & SSH_FXF_READ &&
157b66f2d16SKris Kennaway 	    pflags & SSH_FXF_WRITE) {
158b66f2d16SKris Kennaway 		flags = O_RDWR;
159b66f2d16SKris Kennaway 	} else if (pflags & SSH_FXF_READ) {
160b66f2d16SKris Kennaway 		flags = O_RDONLY;
161b66f2d16SKris Kennaway 	} else if (pflags & SSH_FXF_WRITE) {
162b66f2d16SKris Kennaway 		flags = O_WRONLY;
163b66f2d16SKris Kennaway 	}
164b66f2d16SKris Kennaway 	if (pflags & SSH_FXF_CREAT)
165b66f2d16SKris Kennaway 		flags |= O_CREAT;
166b66f2d16SKris Kennaway 	if (pflags & SSH_FXF_TRUNC)
167b66f2d16SKris Kennaway 		flags |= O_TRUNC;
168b66f2d16SKris Kennaway 	if (pflags & SSH_FXF_EXCL)
169b66f2d16SKris Kennaway 		flags |= O_EXCL;
170b66f2d16SKris Kennaway 	return flags;
171b66f2d16SKris Kennaway }
172b66f2d16SKris Kennaway 
173b66f2d16SKris Kennaway void
174b66f2d16SKris Kennaway attrib_clear(Attrib *a)
175b66f2d16SKris Kennaway {
176b66f2d16SKris Kennaway 	a->flags = 0;
177b66f2d16SKris Kennaway 	a->size_low = 0;
178b66f2d16SKris Kennaway 	a->size_high = 0;
179b66f2d16SKris Kennaway 	a->size = 0;
180b66f2d16SKris Kennaway 	a->uid = 0;
181b66f2d16SKris Kennaway 	a->gid = 0;
182b66f2d16SKris Kennaway 	a->perm = 0;
183b66f2d16SKris Kennaway 	a->atime = 0;
184b66f2d16SKris Kennaway 	a->mtime = 0;
185b66f2d16SKris Kennaway }
186b66f2d16SKris Kennaway 
187b66f2d16SKris Kennaway Attrib *
188b66f2d16SKris Kennaway decode_attrib(Buffer *b)
189b66f2d16SKris Kennaway {
190b66f2d16SKris Kennaway 	static Attrib a;
191b66f2d16SKris Kennaway 	attrib_clear(&a);
192b66f2d16SKris Kennaway 	a.flags = buffer_get_int(b);
193b66f2d16SKris Kennaway 	if (a.flags & SSH_FXA_HAVE_SIZE) {
194b66f2d16SKris Kennaway 		a.size_high = buffer_get_int(b);
195b66f2d16SKris Kennaway 		a.size_low = buffer_get_int(b);
196b66f2d16SKris Kennaway 		a.size = (((u_int64_t) a.size_high) << 32) + a.size_low;
197b66f2d16SKris Kennaway 	}
198b66f2d16SKris Kennaway 	if (a.flags & SSH_FXA_HAVE_UGID) {
199b66f2d16SKris Kennaway 		a.uid = buffer_get_int(b);
200b66f2d16SKris Kennaway 		a.gid = buffer_get_int(b);
201b66f2d16SKris Kennaway 	}
202b66f2d16SKris Kennaway 	if (a.flags & SSH_FXA_HAVE_PERM) {
203b66f2d16SKris Kennaway 		a.perm = buffer_get_int(b);
204b66f2d16SKris Kennaway 	}
205b66f2d16SKris Kennaway 	if (a.flags & SSH_FXA_HAVE_TIME) {
206b66f2d16SKris Kennaway 		a.atime = buffer_get_int(b);
207b66f2d16SKris Kennaway 		a.mtime = buffer_get_int(b);
208b66f2d16SKris Kennaway 	}
209b66f2d16SKris Kennaway 	return &a;
210b66f2d16SKris Kennaway }
211b66f2d16SKris Kennaway 
212b66f2d16SKris Kennaway void
213b66f2d16SKris Kennaway encode_attrib(Buffer *b, Attrib *a)
214b66f2d16SKris Kennaway {
215b66f2d16SKris Kennaway 	buffer_put_int(b, a->flags);
216b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_SIZE) {
217b66f2d16SKris Kennaway 		buffer_put_int(b, a->size_high);
218b66f2d16SKris Kennaway 		buffer_put_int(b, a->size_low);
219b66f2d16SKris Kennaway 	}
220b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_UGID) {
221b66f2d16SKris Kennaway 		buffer_put_int(b, a->uid);
222b66f2d16SKris Kennaway 		buffer_put_int(b, a->gid);
223b66f2d16SKris Kennaway 	}
224b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_PERM) {
225b66f2d16SKris Kennaway 		buffer_put_int(b, a->perm);
226b66f2d16SKris Kennaway 	}
227b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_TIME) {
228b66f2d16SKris Kennaway 		buffer_put_int(b, a->atime);
229b66f2d16SKris Kennaway 		buffer_put_int(b, a->mtime);
230b66f2d16SKris Kennaway 	}
231b66f2d16SKris Kennaway }
232b66f2d16SKris Kennaway 
233b66f2d16SKris Kennaway Attrib *
234b66f2d16SKris Kennaway stat_to_attrib(struct stat *st)
235b66f2d16SKris Kennaway {
236b66f2d16SKris Kennaway 	static Attrib a;
237b66f2d16SKris Kennaway 	attrib_clear(&a);
238b66f2d16SKris Kennaway 	a.flags = 0;
239b66f2d16SKris Kennaway 	a.flags |= SSH_FXA_HAVE_SIZE;
240b66f2d16SKris Kennaway 	a.size = st->st_size;
241b66f2d16SKris Kennaway 	a.size_low = a.size;
242b66f2d16SKris Kennaway 	a.size_high = (u_int32_t) (a.size >> 32);
243b66f2d16SKris Kennaway 	a.flags |= SSH_FXA_HAVE_UGID;
244b66f2d16SKris Kennaway 	a.uid = st->st_uid;
245b66f2d16SKris Kennaway 	a.gid = st->st_gid;
246b66f2d16SKris Kennaway 	a.flags |= SSH_FXA_HAVE_PERM;
247b66f2d16SKris Kennaway 	a.perm = st->st_mode;
248b66f2d16SKris Kennaway 	a.flags |= SSH_FXA_HAVE_TIME;
249b66f2d16SKris Kennaway 	a.atime = st->st_atime;
250b66f2d16SKris Kennaway 	a.mtime = st->st_mtime;
251b66f2d16SKris Kennaway 	return &a;
252b66f2d16SKris Kennaway }
253b66f2d16SKris Kennaway 
254b66f2d16SKris Kennaway Attrib *
255b66f2d16SKris Kennaway get_attrib(void)
256b66f2d16SKris Kennaway {
257b66f2d16SKris Kennaway 	return decode_attrib(&iqueue);
258b66f2d16SKris Kennaway }
259b66f2d16SKris Kennaway 
260b66f2d16SKris Kennaway /* handle handles */
261b66f2d16SKris Kennaway 
262b66f2d16SKris Kennaway typedef struct Handle Handle;
263b66f2d16SKris Kennaway struct Handle {
264b66f2d16SKris Kennaway 	int use;
265b66f2d16SKris Kennaway 	DIR *dirp;
266b66f2d16SKris Kennaway 	int fd;
267b66f2d16SKris Kennaway 	char *name;
268b66f2d16SKris Kennaway };
269b66f2d16SKris Kennaway enum {
270b66f2d16SKris Kennaway 	HANDLE_UNUSED,
271b66f2d16SKris Kennaway 	HANDLE_DIR,
272b66f2d16SKris Kennaway 	HANDLE_FILE
273b66f2d16SKris Kennaway };
274b66f2d16SKris Kennaway Handle	handles[100];
275b66f2d16SKris Kennaway 
276b66f2d16SKris Kennaway void
277b66f2d16SKris Kennaway handle_init(void)
278b66f2d16SKris Kennaway {
279b66f2d16SKris Kennaway 	int i;
280b66f2d16SKris Kennaway 	for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
281b66f2d16SKris Kennaway 		handles[i].use = HANDLE_UNUSED;
282b66f2d16SKris Kennaway }
283b66f2d16SKris Kennaway 
284b66f2d16SKris Kennaway int
285b66f2d16SKris Kennaway handle_new(int use, char *name, int fd, DIR *dirp)
286b66f2d16SKris Kennaway {
287b66f2d16SKris Kennaway 	int i;
288b66f2d16SKris Kennaway 	for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
289b66f2d16SKris Kennaway 		if (handles[i].use == HANDLE_UNUSED) {
290b66f2d16SKris Kennaway 			handles[i].use = use;
291b66f2d16SKris Kennaway 			handles[i].dirp = dirp;
292b66f2d16SKris Kennaway 			handles[i].fd = fd;
293b66f2d16SKris Kennaway 			handles[i].name = name;
294b66f2d16SKris Kennaway 			return i;
295b66f2d16SKris Kennaway 		}
296b66f2d16SKris Kennaway 	}
297b66f2d16SKris Kennaway 	return -1;
298b66f2d16SKris Kennaway }
299b66f2d16SKris Kennaway 
300b66f2d16SKris Kennaway int
301b66f2d16SKris Kennaway handle_is_ok(int i, int type)
302b66f2d16SKris Kennaway {
303b66f2d16SKris Kennaway 	return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type;
304b66f2d16SKris Kennaway }
305b66f2d16SKris Kennaway 
306b66f2d16SKris Kennaway int
307b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp)
308b66f2d16SKris Kennaway {
309b66f2d16SKris Kennaway 	char buf[1024];
310b66f2d16SKris Kennaway 	if (stringp == NULL || hlenp == NULL)
311b66f2d16SKris Kennaway 		return -1;
312b66f2d16SKris Kennaway 	snprintf(buf, sizeof buf, "%d", handle);
313b66f2d16SKris Kennaway 	*stringp = xstrdup(buf);
314b66f2d16SKris Kennaway 	*hlenp = strlen(*stringp);
315b66f2d16SKris Kennaway 	return 0;
316b66f2d16SKris Kennaway }
317b66f2d16SKris Kennaway 
318b66f2d16SKris Kennaway int
319b66f2d16SKris Kennaway handle_from_string(char *handle, u_int hlen)
320b66f2d16SKris Kennaway {
321b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
322b66f2d16SKris Kennaway 	char *ep;
323b66f2d16SKris Kennaway 	long lval = strtol(handle, &ep, 10);
324b66f2d16SKris Kennaway 	int val = lval;
325b66f2d16SKris Kennaway 	if (*ep != '\0')
326b66f2d16SKris Kennaway 		return -1;
327b66f2d16SKris Kennaway 	if (handle_is_ok(val, HANDLE_FILE) ||
328b66f2d16SKris Kennaway 	    handle_is_ok(val, HANDLE_DIR))
329b66f2d16SKris Kennaway 		return val;
330b66f2d16SKris Kennaway 	return -1;
331b66f2d16SKris Kennaway }
332b66f2d16SKris Kennaway 
333b66f2d16SKris Kennaway char *
334b66f2d16SKris Kennaway handle_to_name(int handle)
335b66f2d16SKris Kennaway {
336b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR)||
337b66f2d16SKris Kennaway 	    handle_is_ok(handle, HANDLE_FILE))
338b66f2d16SKris Kennaway 		return handles[handle].name;
339b66f2d16SKris Kennaway 	return NULL;
340b66f2d16SKris Kennaway }
341b66f2d16SKris Kennaway 
342b66f2d16SKris Kennaway DIR *
343b66f2d16SKris Kennaway handle_to_dir(int handle)
344b66f2d16SKris Kennaway {
345b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_DIR))
346b66f2d16SKris Kennaway 		return handles[handle].dirp;
347b66f2d16SKris Kennaway 	return NULL;
348b66f2d16SKris Kennaway }
349b66f2d16SKris Kennaway 
350b66f2d16SKris Kennaway int
351b66f2d16SKris Kennaway handle_to_fd(int handle)
352b66f2d16SKris Kennaway {
353b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE))
354b66f2d16SKris Kennaway 		return handles[handle].fd;
355b66f2d16SKris Kennaway 	return -1;
356b66f2d16SKris Kennaway }
357b66f2d16SKris Kennaway 
358b66f2d16SKris Kennaway int
359b66f2d16SKris Kennaway handle_close(int handle)
360b66f2d16SKris Kennaway {
361b66f2d16SKris Kennaway 	int ret = -1;
362b66f2d16SKris Kennaway 	if (handle_is_ok(handle, HANDLE_FILE)) {
363b66f2d16SKris Kennaway 		ret = close(handles[handle].fd);
364b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
365b66f2d16SKris Kennaway 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
366b66f2d16SKris Kennaway 		ret = closedir(handles[handle].dirp);
367b66f2d16SKris Kennaway 		handles[handle].use = HANDLE_UNUSED;
368b66f2d16SKris Kennaway 	} else {
369b66f2d16SKris Kennaway 		errno = ENOENT;
370b66f2d16SKris Kennaway 	}
371b66f2d16SKris Kennaway 	return ret;
372b66f2d16SKris Kennaway }
373b66f2d16SKris Kennaway 
374b66f2d16SKris Kennaway int
375b66f2d16SKris Kennaway get_handle(void)
376b66f2d16SKris Kennaway {
377b66f2d16SKris Kennaway 	char *handle;
378b66f2d16SKris Kennaway 	int val;
379b66f2d16SKris Kennaway 	u_int hlen;
380b66f2d16SKris Kennaway 	handle = get_string(&hlen);
381b66f2d16SKris Kennaway 	val = handle_from_string(handle, hlen);
382b66f2d16SKris Kennaway 	xfree(handle);
383b66f2d16SKris Kennaway 	return val;
384b66f2d16SKris Kennaway }
385b66f2d16SKris Kennaway 
386b66f2d16SKris Kennaway /* send replies */
387b66f2d16SKris Kennaway 
388b66f2d16SKris Kennaway void
389b66f2d16SKris Kennaway send_msg(Buffer *m)
390b66f2d16SKris Kennaway {
391b66f2d16SKris Kennaway 	int mlen = buffer_len(m);
392b66f2d16SKris Kennaway 	buffer_put_int(&oqueue, mlen);
393b66f2d16SKris Kennaway 	buffer_append(&oqueue, buffer_ptr(m), mlen);
394b66f2d16SKris Kennaway 	buffer_consume(m, mlen);
395b66f2d16SKris Kennaway }
396b66f2d16SKris Kennaway 
397b66f2d16SKris Kennaway void
398b66f2d16SKris Kennaway send_status(u_int32_t id, u_int32_t error)
399b66f2d16SKris Kennaway {
400b66f2d16SKris Kennaway 	Buffer msg;
401b66f2d16SKris Kennaway 	TRACE("sent status id %d error %d", id, error);
402b66f2d16SKris Kennaway 	buffer_init(&msg);
403b66f2d16SKris Kennaway 	buffer_put_char(&msg, SSH_FXP_STATUS);
404b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
405b66f2d16SKris Kennaway 	buffer_put_int(&msg, error);
406b66f2d16SKris Kennaway 	send_msg(&msg);
407b66f2d16SKris Kennaway 	buffer_free(&msg);
408b66f2d16SKris Kennaway }
409b66f2d16SKris Kennaway void
410b66f2d16SKris Kennaway send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
411b66f2d16SKris Kennaway {
412b66f2d16SKris Kennaway 	Buffer msg;
413b66f2d16SKris Kennaway 	buffer_init(&msg);
414b66f2d16SKris Kennaway 	buffer_put_char(&msg, type);
415b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
416b66f2d16SKris Kennaway 	buffer_put_string(&msg, data, dlen);
417b66f2d16SKris Kennaway 	send_msg(&msg);
418b66f2d16SKris Kennaway 	buffer_free(&msg);
419b66f2d16SKris Kennaway }
420b66f2d16SKris Kennaway 
421b66f2d16SKris Kennaway void
422b66f2d16SKris Kennaway send_data(u_int32_t id, char *data, int dlen)
423b66f2d16SKris Kennaway {
424b66f2d16SKris Kennaway 	TRACE("sent data id %d len %d", id, dlen);
425b66f2d16SKris Kennaway 	send_data_or_handle(SSH_FXP_DATA, id, data, dlen);
426b66f2d16SKris Kennaway }
427b66f2d16SKris Kennaway 
428b66f2d16SKris Kennaway void
429b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
430b66f2d16SKris Kennaway {
431b66f2d16SKris Kennaway 	char *string;
432b66f2d16SKris Kennaway 	int hlen;
433b66f2d16SKris Kennaway 	handle_to_string(handle, &string, &hlen);
434b66f2d16SKris Kennaway 	TRACE("sent handle id %d handle %d", id, handle);
435b66f2d16SKris Kennaway 	send_data_or_handle(SSH_FXP_HANDLE, id, string, hlen);
436b66f2d16SKris Kennaway 	xfree(string);
437b66f2d16SKris Kennaway }
438b66f2d16SKris Kennaway 
439b66f2d16SKris Kennaway void
440b66f2d16SKris Kennaway send_names(u_int32_t id, int count, Stat *stats)
441b66f2d16SKris Kennaway {
442b66f2d16SKris Kennaway 	Buffer msg;
443b66f2d16SKris Kennaway 	int i;
444b66f2d16SKris Kennaway 	buffer_init(&msg);
445b66f2d16SKris Kennaway 	buffer_put_char(&msg, SSH_FXP_NAME);
446b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
447b66f2d16SKris Kennaway 	buffer_put_int(&msg, count);
448b66f2d16SKris Kennaway 	TRACE("sent names id %d count %d", id, count);
449b66f2d16SKris Kennaway 	for (i = 0; i < count; i++) {
450b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].name);
451b66f2d16SKris Kennaway 		buffer_put_cstring(&msg, stats[i].long_name);
452b66f2d16SKris Kennaway 		encode_attrib(&msg, &stats[i].attrib);
453b66f2d16SKris Kennaway 	}
454b66f2d16SKris Kennaway 	send_msg(&msg);
455b66f2d16SKris Kennaway 	buffer_free(&msg);
456b66f2d16SKris Kennaway }
457b66f2d16SKris Kennaway 
458b66f2d16SKris Kennaway void
459b66f2d16SKris Kennaway send_attrib(u_int32_t id, Attrib *a)
460b66f2d16SKris Kennaway {
461b66f2d16SKris Kennaway 	Buffer msg;
462b66f2d16SKris Kennaway 	TRACE("sent attrib id %d have 0x%x", id, a->flags);
463b66f2d16SKris Kennaway 	buffer_init(&msg);
464b66f2d16SKris Kennaway 	buffer_put_char(&msg, SSH_FXP_ATTRS);
465b66f2d16SKris Kennaway 	buffer_put_int(&msg, id);
466b66f2d16SKris Kennaway 	encode_attrib(&msg, a);
467b66f2d16SKris Kennaway 	send_msg(&msg);
468b66f2d16SKris Kennaway 	buffer_free(&msg);
469b66f2d16SKris Kennaway }
470b66f2d16SKris Kennaway 
471b66f2d16SKris Kennaway /* parse incoming */
472b66f2d16SKris Kennaway 
473b66f2d16SKris Kennaway void
474b66f2d16SKris Kennaway process_init(void)
475b66f2d16SKris Kennaway {
476b66f2d16SKris Kennaway 	Buffer msg;
477b66f2d16SKris Kennaway 	int version = buffer_get_int(&iqueue);
478b66f2d16SKris Kennaway 
479b66f2d16SKris Kennaway 	TRACE("client version %d", version);
480b66f2d16SKris Kennaway 	buffer_init(&msg);
481b66f2d16SKris Kennaway 	buffer_put_char(&msg, SSH_FXP_VERSION);
482b66f2d16SKris Kennaway 	buffer_put_int(&msg, SSH_FILEXFER_VERSION);
483b66f2d16SKris Kennaway 	send_msg(&msg);
484b66f2d16SKris Kennaway 	buffer_free(&msg);
485b66f2d16SKris Kennaway }
486b66f2d16SKris Kennaway 
487b66f2d16SKris Kennaway void
488b66f2d16SKris Kennaway process_open(void)
489b66f2d16SKris Kennaway {
490b66f2d16SKris Kennaway 	u_int32_t id, pflags;
491b66f2d16SKris Kennaway 	Attrib *a;
492b66f2d16SKris Kennaway 	char *name;
493b66f2d16SKris Kennaway 	int handle, fd, flags, mode, status = SSH_FX_FAILURE;
494b66f2d16SKris Kennaway 
495b66f2d16SKris Kennaway 	id = get_int();
496b66f2d16SKris Kennaway 	name = get_string(NULL);
497b66f2d16SKris Kennaway 	pflags = get_int();
498b66f2d16SKris Kennaway 	a = get_attrib();
499b66f2d16SKris Kennaway 	flags = flags_from_portable(pflags);
500b66f2d16SKris Kennaway 	mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666;
501b66f2d16SKris Kennaway 	TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
502b66f2d16SKris Kennaway 	fd = open(name, flags, mode);
503b66f2d16SKris Kennaway 	if (fd < 0) {
504b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
505b66f2d16SKris Kennaway 	} else {
506b66f2d16SKris Kennaway 		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
507b66f2d16SKris Kennaway 		if (handle < 0) {
508b66f2d16SKris Kennaway 			close(fd);
509b66f2d16SKris Kennaway 		} else {
510b66f2d16SKris Kennaway 			send_handle(id, handle);
511b66f2d16SKris Kennaway 			status = SSH_FX_OK;
512b66f2d16SKris Kennaway 		}
513b66f2d16SKris Kennaway 	}
514b66f2d16SKris Kennaway 	if (status != SSH_FX_OK)
515b66f2d16SKris Kennaway 		send_status(id, status);
516b66f2d16SKris Kennaway 	xfree(name);
517b66f2d16SKris Kennaway }
518b66f2d16SKris Kennaway 
519b66f2d16SKris Kennaway void
520b66f2d16SKris Kennaway process_close(void)
521b66f2d16SKris Kennaway {
522b66f2d16SKris Kennaway 	u_int32_t id;
523b66f2d16SKris Kennaway 	int handle, ret, status = SSH_FX_FAILURE;
524b66f2d16SKris Kennaway 
525b66f2d16SKris Kennaway 	id = get_int();
526b66f2d16SKris Kennaway 	handle = get_handle();
527b66f2d16SKris Kennaway 	TRACE("close id %d handle %d", id, handle);
528b66f2d16SKris Kennaway 	ret = handle_close(handle);
529b66f2d16SKris Kennaway 	status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
530b66f2d16SKris Kennaway 	send_status(id, status);
531b66f2d16SKris Kennaway }
532b66f2d16SKris Kennaway 
533b66f2d16SKris Kennaway void
534b66f2d16SKris Kennaway process_read(void)
535b66f2d16SKris Kennaway {
536b66f2d16SKris Kennaway 	char buf[64*1024];
537b66f2d16SKris Kennaway 	u_int32_t id, off_high, off_low, len;
538b66f2d16SKris Kennaway 	int handle, fd, ret, status = SSH_FX_FAILURE;
539b66f2d16SKris Kennaway 	u_int64_t off;
540b66f2d16SKris Kennaway 
541b66f2d16SKris Kennaway 	id = get_int();
542b66f2d16SKris Kennaway 	handle = get_handle();
543b66f2d16SKris Kennaway 	off_high = get_int();
544b66f2d16SKris Kennaway 	off_low = get_int();
545b66f2d16SKris Kennaway 	len = get_int();
546b66f2d16SKris Kennaway 
547b66f2d16SKris Kennaway 	off = (((u_int64_t) off_high) << 32) + off_low;
548b66f2d16SKris Kennaway 	TRACE("read id %d handle %d off %qd len %d", id, handle, off, len);
549b66f2d16SKris Kennaway 	if (len > sizeof buf) {
550b66f2d16SKris Kennaway 		len = sizeof buf;
551b66f2d16SKris Kennaway 		log("read change len %d", len);
552b66f2d16SKris Kennaway 	}
553b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
554b66f2d16SKris Kennaway 	if (fd >= 0) {
555b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
556b66f2d16SKris Kennaway 			error("process_read: seek failed");
557b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
558b66f2d16SKris Kennaway 		} else {
559b66f2d16SKris Kennaway 			ret = read(fd, buf, len);
560b66f2d16SKris Kennaway 			if (ret < 0) {
561b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
562b66f2d16SKris Kennaway 			} else if (ret == 0) {
563b66f2d16SKris Kennaway 				status = SSH_FX_EOF;
564b66f2d16SKris Kennaway 			} else {
565b66f2d16SKris Kennaway 				send_data(id, buf, ret);
566b66f2d16SKris Kennaway 				status = SSH_FX_OK;
567b66f2d16SKris Kennaway 			}
568b66f2d16SKris Kennaway 		}
569b66f2d16SKris Kennaway 	}
570b66f2d16SKris Kennaway 	if (status != SSH_FX_OK)
571b66f2d16SKris Kennaway 		send_status(id, status);
572b66f2d16SKris Kennaway }
573b66f2d16SKris Kennaway 
574b66f2d16SKris Kennaway void
575b66f2d16SKris Kennaway process_write(void)
576b66f2d16SKris Kennaway {
577b66f2d16SKris Kennaway 	u_int32_t id, off_high, off_low;
578b66f2d16SKris Kennaway 	u_int64_t off;
579b66f2d16SKris Kennaway 	u_int len;
580b66f2d16SKris Kennaway 	int handle, fd, ret, status = SSH_FX_FAILURE;
581b66f2d16SKris Kennaway 	char *data;
582b66f2d16SKris Kennaway 
583b66f2d16SKris Kennaway 	id = get_int();
584b66f2d16SKris Kennaway 	handle = get_handle();
585b66f2d16SKris Kennaway 	off_high = get_int();
586b66f2d16SKris Kennaway 	off_low = get_int();
587b66f2d16SKris Kennaway 	data = get_string(&len);
588b66f2d16SKris Kennaway 
589b66f2d16SKris Kennaway 	off = (((u_int64_t) off_high) << 32) + off_low;
590b66f2d16SKris Kennaway 	TRACE("write id %d handle %d off %qd len %d", id, handle, off, len);
591b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
592b66f2d16SKris Kennaway 	if (fd >= 0) {
593b66f2d16SKris Kennaway 		if (lseek(fd, off, SEEK_SET) < 0) {
594b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
595b66f2d16SKris Kennaway 			error("process_write: seek failed");
596b66f2d16SKris Kennaway 		} else {
597b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
598b66f2d16SKris Kennaway 			ret = write(fd, data, len);
599b66f2d16SKris Kennaway 			if (ret == -1) {
600b66f2d16SKris Kennaway 				error("process_write: write failed");
601b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
602b66f2d16SKris Kennaway 			} else if (ret == len) {
603b66f2d16SKris Kennaway 				status = SSH_FX_OK;
604b66f2d16SKris Kennaway 			} else {
605b66f2d16SKris Kennaway 				log("nothing at all written");
606b66f2d16SKris Kennaway 			}
607b66f2d16SKris Kennaway 		}
608b66f2d16SKris Kennaway 	}
609b66f2d16SKris Kennaway 	send_status(id, status);
610b66f2d16SKris Kennaway 	xfree(data);
611b66f2d16SKris Kennaway }
612b66f2d16SKris Kennaway 
613b66f2d16SKris Kennaway void
614b66f2d16SKris Kennaway process_do_stat(int do_lstat)
615b66f2d16SKris Kennaway {
616b66f2d16SKris Kennaway 	Attrib *a;
617b66f2d16SKris Kennaway 	struct stat st;
618b66f2d16SKris Kennaway 	u_int32_t id;
619b66f2d16SKris Kennaway 	char *name;
620b66f2d16SKris Kennaway 	int ret, status = SSH_FX_FAILURE;
621b66f2d16SKris Kennaway 
622b66f2d16SKris Kennaway 	id = get_int();
623b66f2d16SKris Kennaway 	name = get_string(NULL);
624b66f2d16SKris Kennaway 	TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
625b66f2d16SKris Kennaway 	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
626b66f2d16SKris Kennaway 	if (ret < 0) {
627b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
628b66f2d16SKris Kennaway 	} else {
629b66f2d16SKris Kennaway 		a = stat_to_attrib(&st);
630b66f2d16SKris Kennaway 		send_attrib(id, a);
631b66f2d16SKris Kennaway 		status = SSH_FX_OK;
632b66f2d16SKris Kennaway 	}
633b66f2d16SKris Kennaway 	if (status != SSH_FX_OK)
634b66f2d16SKris Kennaway 		send_status(id, status);
635b66f2d16SKris Kennaway 	xfree(name);
636b66f2d16SKris Kennaway }
637b66f2d16SKris Kennaway 
638b66f2d16SKris Kennaway void
639b66f2d16SKris Kennaway process_stat(void)
640b66f2d16SKris Kennaway {
641b66f2d16SKris Kennaway 	process_do_stat(0);
642b66f2d16SKris Kennaway }
643b66f2d16SKris Kennaway 
644b66f2d16SKris Kennaway void
645b66f2d16SKris Kennaway process_lstat(void)
646b66f2d16SKris Kennaway {
647b66f2d16SKris Kennaway 	process_do_stat(1);
648b66f2d16SKris Kennaway }
649b66f2d16SKris Kennaway 
650b66f2d16SKris Kennaway void
651b66f2d16SKris Kennaway process_fstat(void)
652b66f2d16SKris Kennaway {
653b66f2d16SKris Kennaway 	Attrib *a;
654b66f2d16SKris Kennaway 	struct stat st;
655b66f2d16SKris Kennaway 	u_int32_t id;
656b66f2d16SKris Kennaway 	int fd, ret, handle, status = SSH_FX_FAILURE;
657b66f2d16SKris Kennaway 
658b66f2d16SKris Kennaway 	id = get_int();
659b66f2d16SKris Kennaway 	handle = get_handle();
660b66f2d16SKris Kennaway 	TRACE("fstat id %d handle %d", id, handle);
661b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
662b66f2d16SKris Kennaway 	if (fd  >= 0) {
663b66f2d16SKris Kennaway 		ret = fstat(fd, &st);
664b66f2d16SKris Kennaway 		if (ret < 0) {
665b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
666b66f2d16SKris Kennaway 		} else {
667b66f2d16SKris Kennaway 			a = stat_to_attrib(&st);
668b66f2d16SKris Kennaway 			send_attrib(id, a);
669b66f2d16SKris Kennaway 			status = SSH_FX_OK;
670b66f2d16SKris Kennaway 		}
671b66f2d16SKris Kennaway 	}
672b66f2d16SKris Kennaway 	if (status != SSH_FX_OK)
673b66f2d16SKris Kennaway 		send_status(id, status);
674b66f2d16SKris Kennaway }
675b66f2d16SKris Kennaway 
676b66f2d16SKris Kennaway struct timeval *
677b66f2d16SKris Kennaway attrib_to_tv(Attrib *a)
678b66f2d16SKris Kennaway {
679b66f2d16SKris Kennaway 	static struct timeval tv[2];
680b66f2d16SKris Kennaway 	tv[0].tv_sec = a->atime;
681b66f2d16SKris Kennaway 	tv[0].tv_usec = 0;
682b66f2d16SKris Kennaway 	tv[1].tv_sec = a->mtime;
683b66f2d16SKris Kennaway 	tv[1].tv_usec = 0;
684b66f2d16SKris Kennaway 	return tv;
685b66f2d16SKris Kennaway }
686b66f2d16SKris Kennaway 
687b66f2d16SKris Kennaway void
688b66f2d16SKris Kennaway process_setstat(void)
689b66f2d16SKris Kennaway {
690b66f2d16SKris Kennaway 	Attrib *a;
691b66f2d16SKris Kennaway 	u_int32_t id;
692b66f2d16SKris Kennaway 	char *name;
693b66f2d16SKris Kennaway 	int ret;
694b66f2d16SKris Kennaway 	int status = SSH_FX_OK;
695b66f2d16SKris Kennaway 
696b66f2d16SKris Kennaway 	id = get_int();
697b66f2d16SKris Kennaway 	name = get_string(NULL);
698b66f2d16SKris Kennaway 	a = get_attrib();
699b66f2d16SKris Kennaway 	TRACE("setstat id %d name %s", id, name);
700b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_PERM) {
701b66f2d16SKris Kennaway 		ret = chmod(name, a->perm & 0777);
702b66f2d16SKris Kennaway 		if (ret == -1)
703b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
704b66f2d16SKris Kennaway 	}
705b66f2d16SKris Kennaway 	if (a->flags & SSH_FXA_HAVE_TIME) {
706b66f2d16SKris Kennaway 		ret = utimes(name, attrib_to_tv(a));
707b66f2d16SKris Kennaway 		if (ret == -1)
708b66f2d16SKris Kennaway 			status = errno_to_portable(errno);
709b66f2d16SKris Kennaway 	}
710b66f2d16SKris Kennaway 	send_status(id, status);
711b66f2d16SKris Kennaway 	xfree(name);
712b66f2d16SKris Kennaway }
713b66f2d16SKris Kennaway 
714b66f2d16SKris Kennaway void
715b66f2d16SKris Kennaway process_fsetstat(void)
716b66f2d16SKris Kennaway {
717b66f2d16SKris Kennaway 	Attrib *a;
718b66f2d16SKris Kennaway 	u_int32_t id;
719b66f2d16SKris Kennaway 	int handle, fd, ret;
720b66f2d16SKris Kennaway 	int status = SSH_FX_OK;
721b66f2d16SKris Kennaway 
722b66f2d16SKris Kennaway 	id = get_int();
723b66f2d16SKris Kennaway 	handle = get_handle();
724b66f2d16SKris Kennaway 	a = get_attrib();
725b66f2d16SKris Kennaway 	TRACE("fsetstat id %d handle %d", id, handle);
726b66f2d16SKris Kennaway 	fd = handle_to_fd(handle);
727b66f2d16SKris Kennaway 	if (fd < 0) {
728b66f2d16SKris Kennaway 		status = SSH_FX_FAILURE;
729b66f2d16SKris Kennaway 	} else {
730b66f2d16SKris Kennaway 		if (a->flags & SSH_FXA_HAVE_PERM) {
731b66f2d16SKris Kennaway 			ret = fchmod(fd, a->perm & 0777);
732b66f2d16SKris Kennaway 			if (ret == -1)
733b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
734b66f2d16SKris Kennaway 		}
735b66f2d16SKris Kennaway 		if (a->flags & SSH_FXA_HAVE_TIME) {
736b66f2d16SKris Kennaway 			ret = futimes(fd, attrib_to_tv(a));
737b66f2d16SKris Kennaway 			if (ret == -1)
738b66f2d16SKris Kennaway 				status = errno_to_portable(errno);
739b66f2d16SKris Kennaway 		}
740b66f2d16SKris Kennaway 	}
741b66f2d16SKris Kennaway 	send_status(id, status);
742b66f2d16SKris Kennaway }
743b66f2d16SKris Kennaway 
744b66f2d16SKris Kennaway void
745b66f2d16SKris Kennaway process_opendir(void)
746b66f2d16SKris Kennaway {
747b66f2d16SKris Kennaway 	DIR *dirp = NULL;
748b66f2d16SKris Kennaway 	char *path;
749b66f2d16SKris Kennaway 	int handle, status = SSH_FX_FAILURE;
750b66f2d16SKris Kennaway 	u_int32_t id;
751b66f2d16SKris Kennaway 
752b66f2d16SKris Kennaway 	id = get_int();
753b66f2d16SKris Kennaway 	path = get_string(NULL);
754b66f2d16SKris Kennaway 	TRACE("opendir id %d path %s", id, path);
755b66f2d16SKris Kennaway 	dirp = opendir(path);
756b66f2d16SKris Kennaway 	if (dirp == NULL) {
757b66f2d16SKris Kennaway 		status = errno_to_portable(errno);
758b66f2d16SKris Kennaway 	} else {
759b66f2d16SKris Kennaway 		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
760b66f2d16SKris Kennaway 		if (handle < 0) {
761b66f2d16SKris Kennaway 			closedir(dirp);
762b66f2d16SKris Kennaway 		} else {
763b66f2d16SKris Kennaway 			send_handle(id, handle);
764b66f2d16SKris Kennaway 			status = SSH_FX_OK;
765b66f2d16SKris Kennaway 		}
766b66f2d16SKris Kennaway 
767b66f2d16SKris Kennaway 	}
768b66f2d16SKris Kennaway 	if (status != SSH_FX_OK)
769b66f2d16SKris Kennaway 		send_status(id, status);
770b66f2d16SKris Kennaway 	xfree(path);
771b66f2d16SKris Kennaway }
772b66f2d16SKris Kennaway 
773b66f2d16SKris Kennaway char *
774b66f2d16SKris Kennaway ls_file(char *name, struct stat *st)
775b66f2d16SKris Kennaway {
776b66f2d16SKris Kennaway 	char buf[1024];
777b66f2d16SKris Kennaway 	snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s",
778b66f2d16SKris Kennaway 	    st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,(int) st->st_mtime,
779b66f2d16SKris Kennaway 	    name);
780b66f2d16SKris Kennaway 	return xstrdup(buf);
781b66f2d16SKris Kennaway }
782b66f2d16SKris Kennaway 
783b66f2d16SKris Kennaway void
784b66f2d16SKris Kennaway process_readdir(void)
785b66f2d16SKris Kennaway {
786b66f2d16SKris Kennaway 	DIR *dirp;
787b66f2d16SKris Kennaway 	struct dirent *dp;
788b66f2d16SKris Kennaway 	char *path;
789b66f2d16SKris Kennaway 	int handle;
790b66f2d16SKris Kennaway 	u_int32_t id;
791b66f2d16SKris Kennaway 
792b66f2d16SKris Kennaway 	id = get_int();
793b66f2d16SKris Kennaway 	handle = get_handle();
794b66f2d16SKris Kennaway 	TRACE("readdir id %d handle %d", id, handle);
795b66f2d16SKris Kennaway 	dirp = handle_to_dir(handle);
796b66f2d16SKris Kennaway 	path = handle_to_name(handle);
797b66f2d16SKris Kennaway 	if (dirp == NULL || path == NULL) {
798b66f2d16SKris Kennaway 		send_status(id, SSH_FX_FAILURE);
799b66f2d16SKris Kennaway 	} else {
800b66f2d16SKris Kennaway 		Attrib *a;
801b66f2d16SKris Kennaway 		struct stat st;
802b66f2d16SKris Kennaway 		char pathname[1024];
803b66f2d16SKris Kennaway 		Stat *stats;
804b66f2d16SKris Kennaway 		int nstats = 10, count = 0, i;
805b66f2d16SKris Kennaway 		stats = xmalloc(nstats * sizeof(Stat));
806b66f2d16SKris Kennaway 		while ((dp = readdir(dirp)) != NULL) {
807b66f2d16SKris Kennaway 			if (count >= nstats) {
808b66f2d16SKris Kennaway 				nstats *= 2;
809b66f2d16SKris Kennaway 				stats = xrealloc(stats, nstats * sizeof(Stat));
810b66f2d16SKris Kennaway 			}
811b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
812b66f2d16SKris Kennaway 			snprintf(pathname, sizeof pathname,
813b66f2d16SKris Kennaway 			    "%s/%s", path, dp->d_name);
814b66f2d16SKris Kennaway 			if (lstat(pathname, &st) < 0)
815b66f2d16SKris Kennaway 				continue;
816b66f2d16SKris Kennaway 			a = stat_to_attrib(&st);
817b66f2d16SKris Kennaway 			stats[count].attrib = *a;
818b66f2d16SKris Kennaway 			stats[count].name = xstrdup(dp->d_name);
819b66f2d16SKris Kennaway 			stats[count].long_name = ls_file(dp->d_name, &st);
820b66f2d16SKris Kennaway 			count++;
821b66f2d16SKris Kennaway 			/* send up to 100 entries in one message */
822b66f2d16SKris Kennaway 			if (count == 100)
823b66f2d16SKris Kennaway 				break;
824b66f2d16SKris Kennaway 		}
825b66f2d16SKris Kennaway 		send_names(id, count, stats);
826b66f2d16SKris Kennaway 		for(i = 0; i < count; i++) {
827b66f2d16SKris Kennaway 			xfree(stats[i].name);
828b66f2d16SKris Kennaway 			xfree(stats[i].long_name);
829b66f2d16SKris Kennaway 		}
830b66f2d16SKris Kennaway 		xfree(stats);
831b66f2d16SKris Kennaway 	}
832b66f2d16SKris Kennaway }
833b66f2d16SKris Kennaway 
834b66f2d16SKris Kennaway void
835b66f2d16SKris Kennaway process_remove(void)
836b66f2d16SKris Kennaway {
837b66f2d16SKris Kennaway 	char *name;
838b66f2d16SKris Kennaway 	u_int32_t id;
839b66f2d16SKris Kennaway 	int status = SSH_FX_FAILURE;
840b66f2d16SKris Kennaway 	int ret;
841b66f2d16SKris Kennaway 
842b66f2d16SKris Kennaway 	id = get_int();
843b66f2d16SKris Kennaway 	name = get_string(NULL);
844b66f2d16SKris Kennaway 	TRACE("remove id %d name %s", id, name);
845b66f2d16SKris Kennaway 	ret = remove(name);
846b66f2d16SKris Kennaway 	status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
847b66f2d16SKris Kennaway 	send_status(id, status);
848b66f2d16SKris Kennaway 	xfree(name);
849b66f2d16SKris Kennaway }
850b66f2d16SKris Kennaway 
851b66f2d16SKris Kennaway void
852b66f2d16SKris Kennaway process_mkdir(void)
853b66f2d16SKris Kennaway {
854b66f2d16SKris Kennaway 	Attrib *a;
855b66f2d16SKris Kennaway 	u_int32_t id;
856b66f2d16SKris Kennaway 	char *name;
857b66f2d16SKris Kennaway 	int ret, mode, status = SSH_FX_FAILURE;
858b66f2d16SKris Kennaway 
859b66f2d16SKris Kennaway 	id = get_int();
860b66f2d16SKris Kennaway 	name = get_string(NULL);
861b66f2d16SKris Kennaway 	a = get_attrib();
862b66f2d16SKris Kennaway 	mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777;
863b66f2d16SKris Kennaway 	TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
864b66f2d16SKris Kennaway 	ret = mkdir(name, mode);
865b66f2d16SKris Kennaway 	status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
866b66f2d16SKris Kennaway 	send_status(id, status);
867b66f2d16SKris Kennaway 	xfree(name);
868b66f2d16SKris Kennaway }
869b66f2d16SKris Kennaway 
870b66f2d16SKris Kennaway void
871b66f2d16SKris Kennaway process_rmdir(void)
872b66f2d16SKris Kennaway {
873b66f2d16SKris Kennaway 	u_int32_t id;
874b66f2d16SKris Kennaway 	char *name;
875b66f2d16SKris Kennaway 	int ret, status;
876b66f2d16SKris Kennaway 
877b66f2d16SKris Kennaway 	id = get_int();
878b66f2d16SKris Kennaway 	name = get_string(NULL);
879b66f2d16SKris Kennaway 	TRACE("rmdir id %d name %s", id, name);
880b66f2d16SKris Kennaway 	ret = rmdir(name);
881b66f2d16SKris Kennaway 	status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
882b66f2d16SKris Kennaway 	send_status(id, status);
883b66f2d16SKris Kennaway 	xfree(name);
884b66f2d16SKris Kennaway }
885b66f2d16SKris Kennaway 
886b66f2d16SKris Kennaway void
887b66f2d16SKris Kennaway process_realpath(void)
888b66f2d16SKris Kennaway {
889b66f2d16SKris Kennaway 	char resolvedname[MAXPATHLEN];
890b66f2d16SKris Kennaway 	u_int32_t id;
891b66f2d16SKris Kennaway 	char *path;
892b66f2d16SKris Kennaway 
893b66f2d16SKris Kennaway 	id = get_int();
894b66f2d16SKris Kennaway 	path = get_string(NULL);
895b66f2d16SKris Kennaway 	TRACE("realpath id %d path %s", id, path);
896b66f2d16SKris Kennaway 	if (realpath(path, resolvedname) == NULL) {
897b66f2d16SKris Kennaway 		send_status(id, errno_to_portable(errno));
898b66f2d16SKris Kennaway 	} else {
899b66f2d16SKris Kennaway 		Stat s;
900b66f2d16SKris Kennaway 		attrib_clear(&s.attrib);
901b66f2d16SKris Kennaway 		s.name = s.long_name = resolvedname;
902b66f2d16SKris Kennaway 		send_names(id, 1, &s);
903b66f2d16SKris Kennaway 	}
904b66f2d16SKris Kennaway 	xfree(path);
905b66f2d16SKris Kennaway }
906b66f2d16SKris Kennaway 
907b66f2d16SKris Kennaway void
908b66f2d16SKris Kennaway process_rename(void)
909b66f2d16SKris Kennaway {
910b66f2d16SKris Kennaway 	u_int32_t id;
911b66f2d16SKris Kennaway 	char *oldpath, *newpath;
912b66f2d16SKris Kennaway 	int ret, status;
913b66f2d16SKris Kennaway 
914b66f2d16SKris Kennaway 	id = get_int();
915b66f2d16SKris Kennaway 	oldpath = get_string(NULL);
916b66f2d16SKris Kennaway 	newpath = get_string(NULL);
917b66f2d16SKris Kennaway 	TRACE("rename id %d old %s new %s", id, oldpath, newpath);
918b66f2d16SKris Kennaway 	ret = rename(oldpath, newpath);
919b66f2d16SKris Kennaway 	status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
920b66f2d16SKris Kennaway 	send_status(id, status);
921b66f2d16SKris Kennaway 	xfree(oldpath);
922b66f2d16SKris Kennaway 	xfree(newpath);
923b66f2d16SKris Kennaway }
924b66f2d16SKris Kennaway 
925b66f2d16SKris Kennaway 
926b66f2d16SKris Kennaway /* stolen from ssh-agent */
927b66f2d16SKris Kennaway 
928b66f2d16SKris Kennaway void
929b66f2d16SKris Kennaway process(void)
930b66f2d16SKris Kennaway {
931b66f2d16SKris Kennaway 	unsigned int msg_len;
932b66f2d16SKris Kennaway 	unsigned int type;
933b66f2d16SKris Kennaway 	unsigned char *cp;
934b66f2d16SKris Kennaway 
935b66f2d16SKris Kennaway 	if (buffer_len(&iqueue) < 5)
936b66f2d16SKris Kennaway 		return;		/* Incomplete message. */
937b66f2d16SKris Kennaway 	cp = (unsigned char *) buffer_ptr(&iqueue);
938b66f2d16SKris Kennaway 	msg_len = GET_32BIT(cp);
939b66f2d16SKris Kennaway 	if (msg_len > 256 * 1024) {
940b66f2d16SKris Kennaway 		error("bad message ");
941b66f2d16SKris Kennaway 		exit(11);
942b66f2d16SKris Kennaway 	}
943b66f2d16SKris Kennaway 	if (buffer_len(&iqueue) < msg_len + 4)
944b66f2d16SKris Kennaway 		return;
945b66f2d16SKris Kennaway 	buffer_consume(&iqueue, 4);
946b66f2d16SKris Kennaway 	type = buffer_get_char(&iqueue);
947b66f2d16SKris Kennaway 	switch (type) {
948b66f2d16SKris Kennaway 	case SSH_FXP_INIT:
949b66f2d16SKris Kennaway 		process_init();
950b66f2d16SKris Kennaway 		break;
951b66f2d16SKris Kennaway 	case SSH_FXP_OPEN:
952b66f2d16SKris Kennaway 		process_open();
953b66f2d16SKris Kennaway 		break;
954b66f2d16SKris Kennaway 	case SSH_FXP_CLOSE:
955b66f2d16SKris Kennaway 		process_close();
956b66f2d16SKris Kennaway 		break;
957b66f2d16SKris Kennaway 	case SSH_FXP_READ:
958b66f2d16SKris Kennaway 		process_read();
959b66f2d16SKris Kennaway 		break;
960b66f2d16SKris Kennaway 	case SSH_FXP_WRITE:
961b66f2d16SKris Kennaway 		process_write();
962b66f2d16SKris Kennaway 		break;
963b66f2d16SKris Kennaway 	case SSH_FXP_LSTAT:
964b66f2d16SKris Kennaway 		process_lstat();
965b66f2d16SKris Kennaway 		break;
966b66f2d16SKris Kennaway 	case SSH_FXP_FSTAT:
967b66f2d16SKris Kennaway 		process_fstat();
968b66f2d16SKris Kennaway 		break;
969b66f2d16SKris Kennaway 	case SSH_FXP_SETSTAT:
970b66f2d16SKris Kennaway 		process_setstat();
971b66f2d16SKris Kennaway 		break;
972b66f2d16SKris Kennaway 	case SSH_FXP_FSETSTAT:
973b66f2d16SKris Kennaway 		process_fsetstat();
974b66f2d16SKris Kennaway 		break;
975b66f2d16SKris Kennaway 	case SSH_FXP_OPENDIR:
976b66f2d16SKris Kennaway 		process_opendir();
977b66f2d16SKris Kennaway 		break;
978b66f2d16SKris Kennaway 	case SSH_FXP_READDIR:
979b66f2d16SKris Kennaway 		process_readdir();
980b66f2d16SKris Kennaway 		break;
981b66f2d16SKris Kennaway 	case SSH_FXP_REMOVE:
982b66f2d16SKris Kennaway 		process_remove();
983b66f2d16SKris Kennaway 		break;
984b66f2d16SKris Kennaway 	case SSH_FXP_MKDIR:
985b66f2d16SKris Kennaway 		process_mkdir();
986b66f2d16SKris Kennaway 		break;
987b66f2d16SKris Kennaway 	case SSH_FXP_RMDIR:
988b66f2d16SKris Kennaway 		process_rmdir();
989b66f2d16SKris Kennaway 		break;
990b66f2d16SKris Kennaway 	case SSH_FXP_REALPATH:
991b66f2d16SKris Kennaway 		process_realpath();
992b66f2d16SKris Kennaway 		break;
993b66f2d16SKris Kennaway 	case SSH_FXP_STAT:
994b66f2d16SKris Kennaway 		process_stat();
995b66f2d16SKris Kennaway 		break;
996b66f2d16SKris Kennaway 	case SSH_FXP_RENAME:
997b66f2d16SKris Kennaway 		process_rename();
998b66f2d16SKris Kennaway 		break;
999b66f2d16SKris Kennaway 	default:
1000b66f2d16SKris Kennaway 		error("Unknown message %d", type);
1001b66f2d16SKris Kennaway 		break;
1002b66f2d16SKris Kennaway 	}
1003b66f2d16SKris Kennaway }
1004b66f2d16SKris Kennaway 
1005b66f2d16SKris Kennaway int
1006b66f2d16SKris Kennaway main(int ac, char **av)
1007b66f2d16SKris Kennaway {
1008b66f2d16SKris Kennaway 	fd_set rset, wset;
1009b66f2d16SKris Kennaway 	int in, out, max;
1010b66f2d16SKris Kennaway 	ssize_t len, olen;
1011b66f2d16SKris Kennaway 
1012b66f2d16SKris Kennaway 	handle_init();
1013b66f2d16SKris Kennaway 
1014b66f2d16SKris Kennaway 	in = dup(STDIN_FILENO);
1015b66f2d16SKris Kennaway 	out = dup(STDOUT_FILENO);
1016b66f2d16SKris Kennaway 
1017b66f2d16SKris Kennaway 	max = 0;
1018b66f2d16SKris Kennaway 	if (in > max)
1019b66f2d16SKris Kennaway 		max = in;
1020b66f2d16SKris Kennaway 	if (out > max)
1021b66f2d16SKris Kennaway 		max = out;
1022b66f2d16SKris Kennaway 
1023b66f2d16SKris Kennaway 	buffer_init(&iqueue);
1024b66f2d16SKris Kennaway 	buffer_init(&oqueue);
1025b66f2d16SKris Kennaway 
1026b66f2d16SKris Kennaway 	for (;;) {
1027b66f2d16SKris Kennaway 		FD_ZERO(&rset);
1028b66f2d16SKris Kennaway 		FD_ZERO(&wset);
1029b66f2d16SKris Kennaway 
1030b66f2d16SKris Kennaway 		FD_SET(in, &rset);
1031b66f2d16SKris Kennaway 		olen = buffer_len(&oqueue);
1032b66f2d16SKris Kennaway 		if (olen > 0)
1033b66f2d16SKris Kennaway 			FD_SET(out, &wset);
1034b66f2d16SKris Kennaway 
1035b66f2d16SKris Kennaway 		if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1036b66f2d16SKris Kennaway 			if (errno == EINTR)
1037b66f2d16SKris Kennaway 				continue;
1038b66f2d16SKris Kennaway 			exit(2);
1039b66f2d16SKris Kennaway 		}
1040b66f2d16SKris Kennaway 
1041b66f2d16SKris Kennaway 		/* copy stdin to iqueue */
1042b66f2d16SKris Kennaway 		if (FD_ISSET(in, &rset)) {
1043b66f2d16SKris Kennaway 			char buf[4*4096];
1044b66f2d16SKris Kennaway 			len = read(in, buf, sizeof buf);
1045b66f2d16SKris Kennaway 			if (len == 0) {
1046b66f2d16SKris Kennaway 				debug("read eof");
1047b66f2d16SKris Kennaway 				exit(0);
1048b66f2d16SKris Kennaway 			} else if (len < 0) {
1049b66f2d16SKris Kennaway 				error("read error");
1050b66f2d16SKris Kennaway 				exit(1);
1051b66f2d16SKris Kennaway 			} else {
1052b66f2d16SKris Kennaway 				buffer_append(&iqueue, buf, len);
1053b66f2d16SKris Kennaway 			}
1054b66f2d16SKris Kennaway 		}
1055b66f2d16SKris Kennaway 		/* send oqueue to stdout */
1056b66f2d16SKris Kennaway 		if (FD_ISSET(out, &wset)) {
1057b66f2d16SKris Kennaway 			len = write(out, buffer_ptr(&oqueue), olen);
1058b66f2d16SKris Kennaway 			if (len < 0) {
1059b66f2d16SKris Kennaway 				error("write error");
1060b66f2d16SKris Kennaway 				exit(1);
1061b66f2d16SKris Kennaway 			} else {
1062b66f2d16SKris Kennaway 				buffer_consume(&oqueue, len);
1063b66f2d16SKris Kennaway 			}
1064b66f2d16SKris Kennaway 		}
1065b66f2d16SKris Kennaway 		/* process requests from client */
1066b66f2d16SKris Kennaway 		process();
1067b66f2d16SKris Kennaway 	}
1068b66f2d16SKris Kennaway }
1069