Merge from vendor branch GPERF:
[dragonfly.git] / crypto / openssh-3.9p1 / 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.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
34 extern char *__progname;
35
36 /* input and output queue */
37 Buffer iqueue;
38 Buffer oqueue;
39
40 /* Version of client */
41 int version;
42
43 /* portable attributes, etc. */
44
45 typedef struct Stat Stat;
46
47 struct Stat {
48         char *name;
49         char *long_name;
50         Attrib attrib;
51 };
52
53 static int
54 errno_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
84 static int
85 flags_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
106 static Attrib *
107 get_attrib(void)
108 {
109         return decode_attrib(&iqueue);
110 }
111
112 /* handle handles */
113
114 typedef struct Handle Handle;
115 struct Handle {
116         int use;
117         DIR *dirp;
118         int fd;
119         char *name;
120 };
121
122 enum {
123         HANDLE_UNUSED,
124         HANDLE_DIR,
125         HANDLE_FILE
126 };
127
128 Handle  handles[100];
129
130 static void
131 handle_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
139 static int
140 handle_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
156 static int
157 handle_is_ok(int i, int type)
158 {
159         return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
160             handles[i].use == type;
161 }
162
163 static int
164 handle_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
174 static int
175 handle_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
188 static char *
189 handle_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
197 static DIR *
198 handle_to_dir(int handle)
199 {
200         if (handle_is_ok(handle, HANDLE_DIR))
201                 return handles[handle].dirp;
202         return NULL;
203 }
204
205 static int
206 handle_to_fd(int handle)
207 {
208         if (handle_is_ok(handle, HANDLE_FILE))
209                 return handles[handle].fd;
210         return -1;
211 }
212
213 static int
214 handle_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
232 static int
233 get_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
248 static void
249 send_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
258 static void
259 send_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 }
288 static void
289 send_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
301 static void
302 send_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
308 static void
309 send_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
320 static void
321 send_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
340 static void
341 send_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
356 static void
357 process_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
370 static void
371 process_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
402 static void
403 process_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
416 static void
417 process_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
456 static void
457 process_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
494 static void
495 process_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
519 static void
520 process_stat(void)
521 {
522         process_do_stat(0);
523 }
524
525 static void
526 process_lstat(void)
527 {
528         process_do_stat(1);
529 }
530
531 static void
532 process_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
557 static struct timeval *
558 attrib_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
569 static void
570 process_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
605 static void
606 process_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
659 static void
660 process_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
688 static void
689 process_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
743 static void
744 process_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
760 static void
761 process_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
780 static void
781 process_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
796 static void
797 process_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
821 static void
822 process_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
877 static void
878 process_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
901 static void
902 process_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
920 static void
921 process_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
934 static void
935 process(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
1032 int
1033 main(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 }