sftp-server.c revision 98937
1266733Speter/*
2266733Speter * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
3266733Speter *
4266733Speter * Redistribution and use in source and binary forms, with or without
5266733Speter * modification, are permitted provided that the following conditions
6266733Speter * are met:
7266733Speter * 1. Redistributions of source code must retain the above copyright
8266733Speter *    notice, this list of conditions and the following disclaimer.
9266733Speter * 2. Redistributions in binary form must reproduce the above copyright
10266733Speter *    notice, this list of conditions and the following disclaimer in the
11266733Speter *    documentation and/or other materials provided with the distribution.
12266733Speter *
13266733Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14266733Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15266733Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16266733Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17266733Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18266733Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19266733Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20266733Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21266733Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22266733Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23266733Speter */
24266733Speter#include "includes.h"
25266733SpeterRCSID("$OpenBSD: sftp-server.c,v 1.35 2002/06/06 17:30:11 markus Exp $");
26266733Speter
27266733Speter#include "buffer.h"
28266733Speter#include "bufaux.h"
29266733Speter#include "getput.h"
30266733Speter#include "log.h"
31266733Speter#include "xmalloc.h"
32266733Speter
33266733Speter#include "sftp.h"
34266733Speter#include "sftp-common.h"
35266733Speter
36266733Speter/* helper */
37266733Speter#define get_int64()			buffer_get_int64(&iqueue);
38266733Speter#define get_int()			buffer_get_int(&iqueue);
39266733Speter#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
40266733Speter#define TRACE				debug
41266733Speter
42266733Speter#ifdef HAVE___PROGNAME
43266733Speterextern char *__progname;
44266733Speter#else
45266733Speterchar *__progname;
46266733Speter#endif
47266733Speter
48266733Speter/* input and output queue */
49266733SpeterBuffer iqueue;
50266733SpeterBuffer oqueue;
51266733Speter
52266733Speter/* Version of client */
53266733Speterint version;
54266733Speter
55266733Speter/* portable attibutes, etc. */
56266733Speter
57266733Spetertypedef struct Stat Stat;
58266733Speter
59266733Speterstruct Stat {
60266733Speter	char *name;
61266733Speter	char *long_name;
62266733Speter	Attrib attrib;
63266733Speter};
64266733Speter
65266733Speterstatic int
66266733Spetererrno_to_portable(int unixerrno)
67266733Speter{
68266733Speter	int ret = 0;
69266733Speter
70266733Speter	switch (unixerrno) {
71266733Speter	case 0:
72266733Speter		ret = SSH2_FX_OK;
73266733Speter		break;
74266733Speter	case ENOENT:
75266733Speter	case ENOTDIR:
76266733Speter	case EBADF:
77266733Speter	case ELOOP:
78266733Speter		ret = SSH2_FX_NO_SUCH_FILE;
79266733Speter		break;
80266733Speter	case EPERM:
81266733Speter	case EACCES:
82266733Speter	case EFAULT:
83266733Speter		ret = SSH2_FX_PERMISSION_DENIED;
84266733Speter		break;
85266733Speter	case ENAMETOOLONG:
86266733Speter	case EINVAL:
87266733Speter		ret = SSH2_FX_BAD_MESSAGE;
88266733Speter		break;
89266733Speter	default:
90266733Speter		ret = SSH2_FX_FAILURE;
91266733Speter		break;
92266733Speter	}
93266733Speter	return ret;
94266733Speter}
95266733Speter
96266733Speterstatic int
97266733Speterflags_from_portable(int pflags)
98266733Speter{
99266733Speter	int flags = 0;
100266733Speter
101266733Speter	if ((pflags & SSH2_FXF_READ) &&
102266733Speter	    (pflags & SSH2_FXF_WRITE)) {
103266733Speter		flags = O_RDWR;
104266733Speter	} else if (pflags & SSH2_FXF_READ) {
105266733Speter		flags = O_RDONLY;
106266733Speter	} else if (pflags & SSH2_FXF_WRITE) {
107266733Speter		flags = O_WRONLY;
108266733Speter	}
109266733Speter	if (pflags & SSH2_FXF_CREAT)
110266733Speter		flags |= O_CREAT;
111266733Speter	if (pflags & SSH2_FXF_TRUNC)
112266733Speter		flags |= O_TRUNC;
113266733Speter	if (pflags & SSH2_FXF_EXCL)
114266733Speter		flags |= O_EXCL;
115266733Speter	return flags;
116}
117
118static Attrib *
119get_attrib(void)
120{
121	return decode_attrib(&iqueue);
122}
123
124/* handle handles */
125
126typedef struct Handle Handle;
127struct Handle {
128	int use;
129	DIR *dirp;
130	int fd;
131	char *name;
132};
133
134enum {
135	HANDLE_UNUSED,
136	HANDLE_DIR,
137	HANDLE_FILE
138};
139
140Handle	handles[100];
141
142static void
143handle_init(void)
144{
145	int i;
146
147	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
148		handles[i].use = HANDLE_UNUSED;
149}
150
151static int
152handle_new(int use, char *name, int fd, DIR *dirp)
153{
154	int i;
155
156	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
157		if (handles[i].use == HANDLE_UNUSED) {
158			handles[i].use = use;
159			handles[i].dirp = dirp;
160			handles[i].fd = fd;
161			handles[i].name = name;
162			return i;
163		}
164	}
165	return -1;
166}
167
168static int
169handle_is_ok(int i, int type)
170{
171	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172	    handles[i].use == type;
173}
174
175static int
176handle_to_string(int handle, char **stringp, int *hlenp)
177{
178	if (stringp == NULL || hlenp == NULL)
179		return -1;
180	*stringp = xmalloc(sizeof(int32_t));
181	PUT_32BIT(*stringp, handle);
182	*hlenp = sizeof(int32_t);
183	return 0;
184}
185
186static int
187handle_from_string(char *handle, u_int hlen)
188{
189	int val;
190
191	if (hlen != sizeof(int32_t))
192		return -1;
193	val = GET_32BIT(handle);
194	if (handle_is_ok(val, HANDLE_FILE) ||
195	    handle_is_ok(val, HANDLE_DIR))
196		return val;
197	return -1;
198}
199
200static char *
201handle_to_name(int handle)
202{
203	if (handle_is_ok(handle, HANDLE_DIR)||
204	    handle_is_ok(handle, HANDLE_FILE))
205		return handles[handle].name;
206	return NULL;
207}
208
209static DIR *
210handle_to_dir(int handle)
211{
212	if (handle_is_ok(handle, HANDLE_DIR))
213		return handles[handle].dirp;
214	return NULL;
215}
216
217static int
218handle_to_fd(int handle)
219{
220	if (handle_is_ok(handle, HANDLE_FILE))
221		return handles[handle].fd;
222	return -1;
223}
224
225static int
226handle_close(int handle)
227{
228	int ret = -1;
229
230	if (handle_is_ok(handle, HANDLE_FILE)) {
231		ret = close(handles[handle].fd);
232		handles[handle].use = HANDLE_UNUSED;
233	} else if (handle_is_ok(handle, HANDLE_DIR)) {
234		ret = closedir(handles[handle].dirp);
235		handles[handle].use = HANDLE_UNUSED;
236	} else {
237		errno = ENOENT;
238	}
239	return ret;
240}
241
242static int
243get_handle(void)
244{
245	char *handle;
246	int val = -1;
247	u_int hlen;
248
249	handle = get_string(&hlen);
250	if (hlen < 256)
251		val = handle_from_string(handle, hlen);
252	xfree(handle);
253	return val;
254}
255
256/* send replies */
257
258static void
259send_msg(Buffer *m)
260{
261	int mlen = buffer_len(m);
262
263	buffer_put_int(&oqueue, mlen);
264	buffer_append(&oqueue, buffer_ptr(m), mlen);
265	buffer_consume(m, mlen);
266}
267
268static void
269send_status(u_int32_t id, u_int32_t error)
270{
271	Buffer msg;
272	const char *status_messages[] = {
273		"Success",			/* SSH_FX_OK */
274		"End of file",			/* SSH_FX_EOF */
275		"No such file",			/* SSH_FX_NO_SUCH_FILE */
276		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
277		"Failure",			/* SSH_FX_FAILURE */
278		"Bad message",			/* SSH_FX_BAD_MESSAGE */
279		"No connection",		/* SSH_FX_NO_CONNECTION */
280		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
281		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
282		"Unknown error"			/* Others */
283	};
284
285	TRACE("sent status id %d error %d", id, error);
286	buffer_init(&msg);
287	buffer_put_char(&msg, SSH2_FXP_STATUS);
288	buffer_put_int(&msg, id);
289	buffer_put_int(&msg, error);
290	if (version >= 3) {
291		buffer_put_cstring(&msg,
292		    status_messages[MIN(error,SSH2_FX_MAX)]);
293		buffer_put_cstring(&msg, "");
294	}
295	send_msg(&msg);
296	buffer_free(&msg);
297}
298static void
299send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
300{
301	Buffer msg;
302
303	buffer_init(&msg);
304	buffer_put_char(&msg, type);
305	buffer_put_int(&msg, id);
306	buffer_put_string(&msg, data, dlen);
307	send_msg(&msg);
308	buffer_free(&msg);
309}
310
311static void
312send_data(u_int32_t id, char *data, int dlen)
313{
314	TRACE("sent data id %d len %d", id, dlen);
315	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
316}
317
318static void
319send_handle(u_int32_t id, int handle)
320{
321	char *string;
322	int hlen;
323
324	handle_to_string(handle, &string, &hlen);
325	TRACE("sent handle id %d handle %d", id, handle);
326	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
327	xfree(string);
328}
329
330static void
331send_names(u_int32_t id, int count, Stat *stats)
332{
333	Buffer msg;
334	int i;
335
336	buffer_init(&msg);
337	buffer_put_char(&msg, SSH2_FXP_NAME);
338	buffer_put_int(&msg, id);
339	buffer_put_int(&msg, count);
340	TRACE("sent names id %d count %d", id, count);
341	for (i = 0; i < count; i++) {
342		buffer_put_cstring(&msg, stats[i].name);
343		buffer_put_cstring(&msg, stats[i].long_name);
344		encode_attrib(&msg, &stats[i].attrib);
345	}
346	send_msg(&msg);
347	buffer_free(&msg);
348}
349
350static void
351send_attrib(u_int32_t id, Attrib *a)
352{
353	Buffer msg;
354
355	TRACE("sent attrib id %d have 0x%x", id, a->flags);
356	buffer_init(&msg);
357	buffer_put_char(&msg, SSH2_FXP_ATTRS);
358	buffer_put_int(&msg, id);
359	encode_attrib(&msg, a);
360	send_msg(&msg);
361	buffer_free(&msg);
362}
363
364/* parse incoming */
365
366static void
367process_init(void)
368{
369	Buffer msg;
370
371	version = get_int();
372	TRACE("client version %d", version);
373	buffer_init(&msg);
374	buffer_put_char(&msg, SSH2_FXP_VERSION);
375	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
376	send_msg(&msg);
377	buffer_free(&msg);
378}
379
380static void
381process_open(void)
382{
383	u_int32_t id, pflags;
384	Attrib *a;
385	char *name;
386	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
387
388	id = get_int();
389	name = get_string(NULL);
390	pflags = get_int();		/* portable flags */
391	a = get_attrib();
392	flags = flags_from_portable(pflags);
393	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
394	TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
395	fd = open(name, flags, mode);
396	if (fd < 0) {
397		status = errno_to_portable(errno);
398	} else {
399		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
400		if (handle < 0) {
401			close(fd);
402		} else {
403			send_handle(id, handle);
404			status = SSH2_FX_OK;
405		}
406	}
407	if (status != SSH2_FX_OK)
408		send_status(id, status);
409	xfree(name);
410}
411
412static void
413process_close(void)
414{
415	u_int32_t id;
416	int handle, ret, status = SSH2_FX_FAILURE;
417
418	id = get_int();
419	handle = get_handle();
420	TRACE("close id %d handle %d", id, handle);
421	ret = handle_close(handle);
422	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
423	send_status(id, status);
424}
425
426static void
427process_read(void)
428{
429	char buf[64*1024];
430	u_int32_t id, len;
431	int handle, fd, ret, status = SSH2_FX_FAILURE;
432	u_int64_t off;
433
434	id = get_int();
435	handle = get_handle();
436	off = get_int64();
437	len = get_int();
438
439	TRACE("read id %d handle %d off %llu len %d", id, handle,
440	    (u_int64_t)off, len);
441	if (len > sizeof buf) {
442		len = sizeof buf;
443		log("read change len %d", len);
444	}
445	fd = handle_to_fd(handle);
446	if (fd >= 0) {
447		if (lseek(fd, off, SEEK_SET) < 0) {
448			error("process_read: seek failed");
449			status = errno_to_portable(errno);
450		} else {
451			ret = read(fd, buf, len);
452			if (ret < 0) {
453				status = errno_to_portable(errno);
454			} else if (ret == 0) {
455				status = SSH2_FX_EOF;
456			} else {
457				send_data(id, buf, ret);
458				status = SSH2_FX_OK;
459			}
460		}
461	}
462	if (status != SSH2_FX_OK)
463		send_status(id, status);
464}
465
466static void
467process_write(void)
468{
469	u_int32_t id;
470	u_int64_t off;
471	u_int len;
472	int handle, fd, ret, status = SSH2_FX_FAILURE;
473	char *data;
474
475	id = get_int();
476	handle = get_handle();
477	off = get_int64();
478	data = get_string(&len);
479
480	TRACE("write id %d handle %d off %llu len %d", id, handle,
481	    (u_int64_t)off, len);
482	fd = handle_to_fd(handle);
483	if (fd >= 0) {
484		if (lseek(fd, off, SEEK_SET) < 0) {
485			status = errno_to_portable(errno);
486			error("process_write: seek failed");
487		} else {
488/* XXX ATOMICIO ? */
489			ret = write(fd, data, len);
490			if (ret == -1) {
491				error("process_write: write failed");
492				status = errno_to_portable(errno);
493			} else if (ret == len) {
494				status = SSH2_FX_OK;
495			} else {
496				log("nothing at all written");
497			}
498		}
499	}
500	send_status(id, status);
501	xfree(data);
502}
503
504static void
505process_do_stat(int do_lstat)
506{
507	Attrib a;
508	struct stat st;
509	u_int32_t id;
510	char *name;
511	int ret, status = SSH2_FX_FAILURE;
512
513	id = get_int();
514	name = get_string(NULL);
515	TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
516	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
517	if (ret < 0) {
518		status = errno_to_portable(errno);
519	} else {
520		stat_to_attrib(&st, &a);
521		send_attrib(id, &a);
522		status = SSH2_FX_OK;
523	}
524	if (status != SSH2_FX_OK)
525		send_status(id, status);
526	xfree(name);
527}
528
529static void
530process_stat(void)
531{
532	process_do_stat(0);
533}
534
535static void
536process_lstat(void)
537{
538	process_do_stat(1);
539}
540
541static void
542process_fstat(void)
543{
544	Attrib a;
545	struct stat st;
546	u_int32_t id;
547	int fd, ret, handle, status = SSH2_FX_FAILURE;
548
549	id = get_int();
550	handle = get_handle();
551	TRACE("fstat id %d handle %d", id, handle);
552	fd = handle_to_fd(handle);
553	if (fd  >= 0) {
554		ret = fstat(fd, &st);
555		if (ret < 0) {
556			status = errno_to_portable(errno);
557		} else {
558			stat_to_attrib(&st, &a);
559			send_attrib(id, &a);
560			status = SSH2_FX_OK;
561		}
562	}
563	if (status != SSH2_FX_OK)
564		send_status(id, status);
565}
566
567static struct timeval *
568attrib_to_tv(Attrib *a)
569{
570	static struct timeval tv[2];
571
572	tv[0].tv_sec = a->atime;
573	tv[0].tv_usec = 0;
574	tv[1].tv_sec = a->mtime;
575	tv[1].tv_usec = 0;
576	return tv;
577}
578
579static void
580process_setstat(void)
581{
582	Attrib *a;
583	u_int32_t id;
584	char *name;
585	int ret;
586	int status = SSH2_FX_OK;
587
588	id = get_int();
589	name = get_string(NULL);
590	a = get_attrib();
591	TRACE("setstat id %d name %s", id, name);
592	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
593		ret = truncate(name, a->size);
594		if (ret == -1)
595			status = errno_to_portable(errno);
596	}
597	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
598		ret = chmod(name, a->perm & 0777);
599		if (ret == -1)
600			status = errno_to_portable(errno);
601	}
602	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
603		ret = utimes(name, attrib_to_tv(a));
604		if (ret == -1)
605			status = errno_to_portable(errno);
606	}
607	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
608		ret = chown(name, a->uid, a->gid);
609		if (ret == -1)
610			status = errno_to_portable(errno);
611	}
612	send_status(id, status);
613	xfree(name);
614}
615
616static void
617process_fsetstat(void)
618{
619	Attrib *a;
620	u_int32_t id;
621	int handle, fd, ret;
622	int status = SSH2_FX_OK;
623	char *name;
624
625	id = get_int();
626	handle = get_handle();
627	a = get_attrib();
628	TRACE("fsetstat id %d handle %d", id, handle);
629	fd = handle_to_fd(handle);
630	name = handle_to_name(handle);
631	if (fd < 0 || name == NULL) {
632		status = SSH2_FX_FAILURE;
633	} else {
634		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
635			ret = ftruncate(fd, a->size);
636			if (ret == -1)
637				status = errno_to_portable(errno);
638		}
639		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
640#ifdef HAVE_FCHMOD
641			ret = fchmod(fd, a->perm & 0777);
642#else
643			ret = chmod(name, a->perm & 0777);
644#endif
645			if (ret == -1)
646				status = errno_to_portable(errno);
647		}
648		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
649#ifdef HAVE_FUTIMES
650			ret = futimes(fd, attrib_to_tv(a));
651#else
652			ret = utimes(name, attrib_to_tv(a));
653#endif
654			if (ret == -1)
655				status = errno_to_portable(errno);
656		}
657		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
658#ifdef HAVE_FCHOWN
659			ret = fchown(fd, a->uid, a->gid);
660#else
661			ret = chown(name, a->uid, a->gid);
662#endif
663			if (ret == -1)
664				status = errno_to_portable(errno);
665		}
666	}
667	send_status(id, status);
668}
669
670static void
671process_opendir(void)
672{
673	DIR *dirp = NULL;
674	char *path;
675	int handle, status = SSH2_FX_FAILURE;
676	u_int32_t id;
677
678	id = get_int();
679	path = get_string(NULL);
680	TRACE("opendir id %d path %s", id, path);
681	dirp = opendir(path);
682	if (dirp == NULL) {
683		status = errno_to_portable(errno);
684	} else {
685		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
686		if (handle < 0) {
687			closedir(dirp);
688		} else {
689			send_handle(id, handle);
690			status = SSH2_FX_OK;
691		}
692
693	}
694	if (status != SSH2_FX_OK)
695		send_status(id, status);
696	xfree(path);
697}
698
699/*
700 * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
701 */
702static char *
703ls_file(char *name, struct stat *st)
704{
705	int ulen, glen, sz = 0;
706	struct passwd *pw;
707	struct group *gr;
708	struct tm *ltime = localtime(&st->st_mtime);
709	char *user, *group;
710	char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
711
712	strmode(st->st_mode, mode);
713	if ((pw = getpwuid(st->st_uid)) != NULL) {
714		user = pw->pw_name;
715	} else {
716		snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
717		user = ubuf;
718	}
719	if ((gr = getgrgid(st->st_gid)) != NULL) {
720		group = gr->gr_name;
721	} else {
722		snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
723		group = gbuf;
724	}
725	if (ltime != NULL) {
726		if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
727			sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
728		else
729			sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
730	}
731	if (sz == 0)
732		tbuf[0] = '\0';
733	ulen = MAX(strlen(user), 8);
734	glen = MAX(strlen(group), 8);
735	snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
736	    st->st_nlink, ulen, user, glen, group,
737	    (u_int64_t)st->st_size, tbuf, name);
738	return xstrdup(buf);
739}
740
741static void
742process_readdir(void)
743{
744	DIR *dirp;
745	struct dirent *dp;
746	char *path;
747	int handle;
748	u_int32_t id;
749
750	id = get_int();
751	handle = get_handle();
752	TRACE("readdir id %d handle %d", id, handle);
753	dirp = handle_to_dir(handle);
754	path = handle_to_name(handle);
755	if (dirp == NULL || path == NULL) {
756		send_status(id, SSH2_FX_FAILURE);
757	} else {
758		struct stat st;
759		char pathname[1024];
760		Stat *stats;
761		int nstats = 10, count = 0, i;
762		stats = xmalloc(nstats * sizeof(Stat));
763		while ((dp = readdir(dirp)) != NULL) {
764			if (count >= nstats) {
765				nstats *= 2;
766				stats = xrealloc(stats, nstats * sizeof(Stat));
767			}
768/* XXX OVERFLOW ? */
769			snprintf(pathname, sizeof pathname, "%s%s%s", path,
770			    strcmp(path, "/") ? "/" : "", dp->d_name);
771			if (lstat(pathname, &st) < 0)
772				continue;
773			stat_to_attrib(&st, &(stats[count].attrib));
774			stats[count].name = xstrdup(dp->d_name);
775			stats[count].long_name = ls_file(dp->d_name, &st);
776			count++;
777			/* send up to 100 entries in one message */
778			/* XXX check packet size instead */
779			if (count == 100)
780				break;
781		}
782		if (count > 0) {
783			send_names(id, count, stats);
784			for (i = 0; i < count; i++) {
785				xfree(stats[i].name);
786				xfree(stats[i].long_name);
787			}
788		} else {
789			send_status(id, SSH2_FX_EOF);
790		}
791		xfree(stats);
792	}
793}
794
795static void
796process_remove(void)
797{
798	char *name;
799	u_int32_t id;
800	int status = SSH2_FX_FAILURE;
801	int ret;
802
803	id = get_int();
804	name = get_string(NULL);
805	TRACE("remove id %d name %s", id, name);
806	ret = unlink(name);
807	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
808	send_status(id, status);
809	xfree(name);
810}
811
812static void
813process_mkdir(void)
814{
815	Attrib *a;
816	u_int32_t id;
817	char *name;
818	int ret, mode, status = SSH2_FX_FAILURE;
819
820	id = get_int();
821	name = get_string(NULL);
822	a = get_attrib();
823	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
824	    a->perm & 0777 : 0777;
825	TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
826	ret = mkdir(name, mode);
827	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
828	send_status(id, status);
829	xfree(name);
830}
831
832static void
833process_rmdir(void)
834{
835	u_int32_t id;
836	char *name;
837	int ret, status;
838
839	id = get_int();
840	name = get_string(NULL);
841	TRACE("rmdir id %d name %s", id, name);
842	ret = rmdir(name);
843	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
844	send_status(id, status);
845	xfree(name);
846}
847
848static void
849process_realpath(void)
850{
851	char resolvedname[MAXPATHLEN];
852	u_int32_t id;
853	char *path;
854
855	id = get_int();
856	path = get_string(NULL);
857	if (path[0] == '\0') {
858		xfree(path);
859		path = xstrdup(".");
860	}
861	TRACE("realpath id %d path %s", id, path);
862	if (realpath(path, resolvedname) == NULL) {
863		send_status(id, errno_to_portable(errno));
864	} else {
865		Stat s;
866		attrib_clear(&s.attrib);
867		s.name = s.long_name = resolvedname;
868		send_names(id, 1, &s);
869	}
870	xfree(path);
871}
872
873static void
874process_rename(void)
875{
876	u_int32_t id;
877	struct stat st;
878	char *oldpath, *newpath;
879	int ret, status = SSH2_FX_FAILURE;
880
881	id = get_int();
882	oldpath = get_string(NULL);
883	newpath = get_string(NULL);
884	TRACE("rename id %d old %s new %s", id, oldpath, newpath);
885	/* fail if 'newpath' exists */
886	if (stat(newpath, &st) == -1) {
887		ret = rename(oldpath, newpath);
888		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
889	}
890	send_status(id, status);
891	xfree(oldpath);
892	xfree(newpath);
893}
894
895static void
896process_readlink(void)
897{
898	u_int32_t id;
899	int len;
900	char link[MAXPATHLEN];
901	char *path;
902
903	id = get_int();
904	path = get_string(NULL);
905	TRACE("readlink id %d path %s", id, path);
906	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
907		send_status(id, errno_to_portable(errno));
908	else {
909		Stat s;
910
911		link[len] = '\0';
912		attrib_clear(&s.attrib);
913		s.name = s.long_name = link;
914		send_names(id, 1, &s);
915	}
916	xfree(path);
917}
918
919static void
920process_symlink(void)
921{
922	u_int32_t id;
923	struct stat st;
924	char *oldpath, *newpath;
925	int ret, status = SSH2_FX_FAILURE;
926
927	id = get_int();
928	oldpath = get_string(NULL);
929	newpath = get_string(NULL);
930	TRACE("symlink id %d old %s new %s", id, oldpath, newpath);
931	/* fail if 'newpath' exists */
932	if (stat(newpath, &st) == -1) {
933		ret = symlink(oldpath, newpath);
934		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
935	}
936	send_status(id, status);
937	xfree(oldpath);
938	xfree(newpath);
939}
940
941static void
942process_extended(void)
943{
944	u_int32_t id;
945	char *request;
946
947	id = get_int();
948	request = get_string(NULL);
949	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
950	xfree(request);
951}
952
953/* stolen from ssh-agent */
954
955static void
956process(void)
957{
958	u_int msg_len;
959	u_int buf_len;
960	u_int consumed;
961	u_int type;
962	u_char *cp;
963
964	buf_len = buffer_len(&iqueue);
965	if (buf_len < 5)
966		return;		/* Incomplete message. */
967	cp = buffer_ptr(&iqueue);
968	msg_len = GET_32BIT(cp);
969	if (msg_len > 256 * 1024) {
970		error("bad message ");
971		exit(11);
972	}
973	if (buf_len < msg_len + 4)
974		return;
975	buffer_consume(&iqueue, 4);
976	buf_len -= 4;
977	type = buffer_get_char(&iqueue);
978	switch (type) {
979	case SSH2_FXP_INIT:
980		process_init();
981		break;
982	case SSH2_FXP_OPEN:
983		process_open();
984		break;
985	case SSH2_FXP_CLOSE:
986		process_close();
987		break;
988	case SSH2_FXP_READ:
989		process_read();
990		break;
991	case SSH2_FXP_WRITE:
992		process_write();
993		break;
994	case SSH2_FXP_LSTAT:
995		process_lstat();
996		break;
997	case SSH2_FXP_FSTAT:
998		process_fstat();
999		break;
1000	case SSH2_FXP_SETSTAT:
1001		process_setstat();
1002		break;
1003	case SSH2_FXP_FSETSTAT:
1004		process_fsetstat();
1005		break;
1006	case SSH2_FXP_OPENDIR:
1007		process_opendir();
1008		break;
1009	case SSH2_FXP_READDIR:
1010		process_readdir();
1011		break;
1012	case SSH2_FXP_REMOVE:
1013		process_remove();
1014		break;
1015	case SSH2_FXP_MKDIR:
1016		process_mkdir();
1017		break;
1018	case SSH2_FXP_RMDIR:
1019		process_rmdir();
1020		break;
1021	case SSH2_FXP_REALPATH:
1022		process_realpath();
1023		break;
1024	case SSH2_FXP_STAT:
1025		process_stat();
1026		break;
1027	case SSH2_FXP_RENAME:
1028		process_rename();
1029		break;
1030	case SSH2_FXP_READLINK:
1031		process_readlink();
1032		break;
1033	case SSH2_FXP_SYMLINK:
1034		process_symlink();
1035		break;
1036	case SSH2_FXP_EXTENDED:
1037		process_extended();
1038		break;
1039	default:
1040		error("Unknown message %d", type);
1041		break;
1042	}
1043	/* discard the remaining bytes from the current packet */
1044	if (buf_len < buffer_len(&iqueue))
1045		fatal("iqueue grows");
1046	consumed = buf_len - buffer_len(&iqueue);
1047	if (msg_len < consumed)
1048		fatal("msg_len %d < consumed %d", msg_len, consumed);
1049	if (msg_len > consumed)
1050		buffer_consume(&iqueue, msg_len - consumed);
1051}
1052
1053int
1054main(int ac, char **av)
1055{
1056	fd_set *rset, *wset;
1057	int in, out, max;
1058	ssize_t len, olen, set_size;
1059
1060	/* XXX should use getopt */
1061
1062	__progname = get_progname(av[0]);
1063	handle_init();
1064
1065#ifdef DEBUG_SFTP_SERVER
1066	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1067#endif
1068
1069	in = dup(STDIN_FILENO);
1070	out = dup(STDOUT_FILENO);
1071
1072#ifdef HAVE_CYGWIN
1073	setmode(in, O_BINARY);
1074	setmode(out, O_BINARY);
1075#endif
1076
1077	max = 0;
1078	if (in > max)
1079		max = in;
1080	if (out > max)
1081		max = out;
1082
1083	buffer_init(&iqueue);
1084	buffer_init(&oqueue);
1085
1086	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1087	rset = (fd_set *)xmalloc(set_size);
1088	wset = (fd_set *)xmalloc(set_size);
1089
1090	for (;;) {
1091		memset(rset, 0, set_size);
1092		memset(wset, 0, set_size);
1093
1094		FD_SET(in, rset);
1095		olen = buffer_len(&oqueue);
1096		if (olen > 0)
1097			FD_SET(out, wset);
1098
1099		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1100			if (errno == EINTR)
1101				continue;
1102			exit(2);
1103		}
1104
1105		/* copy stdin to iqueue */
1106		if (FD_ISSET(in, rset)) {
1107			char buf[4*4096];
1108			len = read(in, buf, sizeof buf);
1109			if (len == 0) {
1110				debug("read eof");
1111				exit(0);
1112			} else if (len < 0) {
1113				error("read error");
1114				exit(1);
1115			} else {
1116				buffer_append(&iqueue, buf, len);
1117			}
1118		}
1119		/* send oqueue to stdout */
1120		if (FD_ISSET(out, wset)) {
1121			len = write(out, buffer_ptr(&oqueue), olen);
1122			if (len < 0) {
1123				error("write error");
1124				exit(1);
1125			} else {
1126				buffer_consume(&oqueue, len);
1127			}
1128		}
1129		/* process requests from client */
1130		process();
1131	}
1132}
1133