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