]> andersk Git - openssh.git/blame - sftp-server.c
20001209
[openssh.git] / sftp-server.c
CommitLineData
b5e300c2 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.
b5e300c2 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"
bcbf86ec 25RCSID("$OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp $");
b5e300c2 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
260d427b 93#ifdef HAVE___PROGNAME
94extern char *__progname;
95#else
96char *__progname;
97#endif
98
b5e300c2 99/* input and output queue */
100Buffer iqueue;
101Buffer oqueue;
102
103/* portable attibutes, etc. */
104
105typedef struct Attrib Attrib;
106typedef struct Stat Stat;
107
108struct 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
121struct Stat
122{
123 char *name;
124 char *long_name;
125 Attrib attrib;
126};
127
128int
129errno_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
158int
159flags_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
179void
180attrib_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
193Attrib *
194decode_attrib(Buffer *b)
195{
196 static Attrib a;
197 attrib_clear(&a);
bcbf86ec 198 a.flags = buffer_get_int(b);
b5e300c2 199 if (a.flags & SSH_FXA_HAVE_SIZE) {
bcbf86ec 200 a.size_high = buffer_get_int(b);
201 a.size_low = buffer_get_int(b);
b5e300c2 202 a.size = (((u_int64_t) a.size_high) << 32) + a.size_low;
203 }
204 if (a.flags & SSH_FXA_HAVE_UGID) {
bcbf86ec 205 a.uid = buffer_get_int(b);
206 a.gid = buffer_get_int(b);
b5e300c2 207 }
208 if (a.flags & SSH_FXA_HAVE_PERM) {
bcbf86ec 209 a.perm = buffer_get_int(b);
b5e300c2 210 }
211 if (a.flags & SSH_FXA_HAVE_TIME) {
bcbf86ec 212 a.atime = buffer_get_int(b);
213 a.mtime = buffer_get_int(b);
b5e300c2 214 }
215 return &a;
216}
217
218void
219encode_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
239Attrib *
240stat_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
260Attrib *
261get_attrib(void)
262{
263 return decode_attrib(&iqueue);
264}
265
266/* handle handles */
267
268typedef struct Handle Handle;
269struct Handle {
270 int use;
271 DIR *dirp;
272 int fd;
273 char *name;
274};
275enum {
276 HANDLE_UNUSED,
277 HANDLE_DIR,
278 HANDLE_FILE
279};
280Handle handles[100];
281
282void
283handle_init(void)
284{
285 int i;
286 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
287 handles[i].use = HANDLE_UNUSED;
288}
289
290int
291handle_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
306int
307handle_is_ok(int i, int type)
308{
309 return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type;
310}
311
312int
313handle_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
324int
bcbf86ec 325handle_from_string(char *handle, u_int hlen)
b5e300c2 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
339char *
340handle_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
348DIR *
349handle_to_dir(int handle)
350{
351 if (handle_is_ok(handle, HANDLE_DIR))
352 return handles[handle].dirp;
353 return NULL;
354}
355
356int
357handle_to_fd(int handle)
358{
359 if (handle_is_ok(handle, HANDLE_FILE))
360 return handles[handle].fd;
361 return -1;
362}
363
364int
365handle_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
380int
381get_handle(void)
382{
383 char *handle;
bcbf86ec 384 int val;
385 u_int hlen;
b5e300c2 386 handle = get_string(&hlen);
387 val = handle_from_string(handle, hlen);
388 xfree(handle);
389 return val;
390}
391
392/* send replies */
393
394void
395send_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
403void
404send_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}
415void
416send_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
427void
428send_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
434void
435send_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
445void
446send_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
464void
465send_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
479void
480process_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
493void
494process_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
525void
526process_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
539void
540process_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;
e2144f11 554 TRACE("read id %d handle %d off %lld len %d", id, handle, off, len);
b5e300c2 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
580void
581process_write(void)
582{
583 u_int32_t id, off_high, off_low;
584 u_int64_t off;
bcbf86ec 585 u_int len;
b5e300c2 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;
e2144f11 596 TRACE("write id %d handle %d off %lld len %d", id, handle, off, len);
b5e300c2 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
619void
620process_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
644void
645process_stat(void)
646{
647 process_do_stat(0);
648}
649
650void
651process_lstat(void)
652{
653 process_do_stat(1);
654}
655
656void
657process_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
682struct timeval *
683attrib_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
693void
694process_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
720void
721process_fsetstat(void)
722{
723 Attrib *a;
724 u_int32_t id;
725 int handle, fd, ret;
726 int status = SSH_FX_OK;
bcbf86ec 727 char *name;
728
b5e300c2 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
756void
757process_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
785char *
786ls_file(char *name, struct stat *st)
787{
788 char buf[1024];
e2144f11 789 snprintf(buf, sizeof buf, "0%o %d %d %lld %d %s",
b5e300c2 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
795void
796process_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
846void
847process_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
863void
864process_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
882void
883process_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
898void
899process_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
919void
920process_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
940void
941process(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
1017int
1018main(int ac, char **av)
1019{
1020 fd_set rset, wset;
1021 int in, out, max;
bcbf86ec 1022 ssize_t len, olen;
b5e300c2 1023
260d427b 1024 __progname = get_progname(av[0]);
b5e300c2 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.526464 seconds and 5 git commands to generate.