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