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