xref: /freebsd/crypto/openssh/sftp-server.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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"
25 RCSID("$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 */
94 Buffer iqueue;
95 Buffer oqueue;
96 
97 /* portable attibutes, etc. */
98 
99 typedef struct Attrib Attrib;
100 typedef struct Stat Stat;
101 
102 struct 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 
115 struct Stat
116 {
117 	char *name;
118 	char *long_name;
119 	Attrib attrib;
120 };
121 
122 int
123 errno_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 
152 int
153 flags_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 
173 void
174 attrib_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 
187 Attrib *
188 decode_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 
212 void
213 encode_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 
233 Attrib *
234 stat_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 
254 Attrib *
255 get_attrib(void)
256 {
257 	return decode_attrib(&iqueue);
258 }
259 
260 /* handle handles */
261 
262 typedef struct Handle Handle;
263 struct Handle {
264 	int use;
265 	DIR *dirp;
266 	int fd;
267 	char *name;
268 };
269 enum {
270 	HANDLE_UNUSED,
271 	HANDLE_DIR,
272 	HANDLE_FILE
273 };
274 Handle	handles[100];
275 
276 void
277 handle_init(void)
278 {
279 	int i;
280 	for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
281 		handles[i].use = HANDLE_UNUSED;
282 }
283 
284 int
285 handle_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 
300 int
301 handle_is_ok(int i, int type)
302 {
303 	return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type;
304 }
305 
306 int
307 handle_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 
318 int
319 handle_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 
333 char *
334 handle_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 
342 DIR *
343 handle_to_dir(int handle)
344 {
345 	if (handle_is_ok(handle, HANDLE_DIR))
346 		return handles[handle].dirp;
347 	return NULL;
348 }
349 
350 int
351 handle_to_fd(int handle)
352 {
353 	if (handle_is_ok(handle, HANDLE_FILE))
354 		return handles[handle].fd;
355 	return -1;
356 }
357 
358 int
359 handle_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 
374 int
375 get_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 
388 void
389 send_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 
397 void
398 send_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 }
409 void
410 send_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 
421 void
422 send_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 
428 void
429 send_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 
439 void
440 send_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 
458 void
459 send_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 
473 void
474 process_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 
487 void
488 process_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 
519 void
520 process_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 
533 void
534 process_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 
574 void
575 process_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 
613 void
614 process_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 
638 void
639 process_stat(void)
640 {
641 	process_do_stat(0);
642 }
643 
644 void
645 process_lstat(void)
646 {
647 	process_do_stat(1);
648 }
649 
650 void
651 process_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 
676 struct timeval *
677 attrib_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 
687 void
688 process_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 
714 void
715 process_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 
744 void
745 process_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 
773 char *
774 ls_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 
783 void
784 process_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 
834 void
835 process_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 
851 void
852 process_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 
870 void
871 process_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 
886 void
887 process_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 
907 void
908 process_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 
928 void
929 process(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 
1005 int
1006 main(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