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