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