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