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