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