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