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