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