Annotation of src/usr.bin/ssh/sftp-server.c, Revision 1.2
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"
30: RCSID("$OpenBSD: sftp-server.c,v 1.33 2000/08/19 21:34:43 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 */
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);
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();
1.2 ! 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) {
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:
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;
! 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
324: handle_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:
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;
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:
392: void
393: send_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:
401: void
402: send_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: }
413: void
414: send_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:
425: void
426: send_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:
432: void
433: send_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:
443: void
444: send_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:
462: void
463: send_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:
477: void
478: process_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:
491: void
492: process_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:
523: void
524: process_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:
537: void
538: process_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:
1.2 ! markus 551: off = (u_int64_t) off_high << 32 + off_low;
1.1 markus 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:
578: void
579: process_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:
1.2 ! markus 593: off = (u_int64_t) off_high << 32 + off_low;
1.1 markus 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:
617: void
618: process_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:
642: void
643: process_stat(void)
644: {
645: process_do_stat(0);
646: }
647:
648: void
649: process_lstat(void)
650: {
651: process_do_stat(1);
652: }
653:
654: void
655: process_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:
680: struct timeval *
681: attrib_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:
691: void
692: process_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:
718: void
719: process_fsetstat(void)
720: {
721: Attrib *a;
722: u_int32_t id;
723: int handle, fd, ret;
724: int status = SSH_FX_OK;
725:
726: id = get_int();
727: handle = get_handle();
728: a = get_attrib();
729: TRACE("fsetstat id %d handle %d", id, handle);
730: fd = handle_to_fd(handle);
731: if (fd < 0) {
732: status = SSH_FX_FAILURE;
733: } else {
734: if (a->flags & SSH_FXA_HAVE_PERM) {
735: ret = fchmod(fd, a->perm & 0777);
736: if (ret == -1)
737: status = errno_to_portable(errno);
738: }
739: if (a->flags & SSH_FXA_HAVE_TIME) {
740: ret = futimes(fd, attrib_to_tv(a));
741: if (ret == -1)
742: status = errno_to_portable(errno);
743: }
744: }
745: send_status(id, status);
746: }
747:
748: void
749: process_opendir(void)
750: {
751: DIR *dirp = NULL;
752: char *path;
753: int handle, status = SSH_FX_FAILURE;
754: u_int32_t id;
755:
756: id = get_int();
757: path = get_string(NULL);
758: TRACE("opendir id %d path %s", id, path);
759: dirp = opendir(path);
760: if (dirp == NULL) {
761: status = errno_to_portable(errno);
762: } else {
763: handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
764: if (handle < 0) {
765: closedir(dirp);
766: } else {
767: send_handle(id, handle);
768: status = SSH_FX_OK;
769: }
770:
771: }
772: if (status != SSH_FX_OK)
773: send_status(id, status);
774: xfree(path);
775: }
776:
777: char *
778: ls_file(char *name, struct stat *st)
779: {
780: char buf[1024];
781: snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s",
782: st->st_mode, st->st_uid, st->st_gid, st->st_size, st->st_mtime,
783: name);
784: return xstrdup(buf);
785: }
786:
787: void
788: process_readdir(void)
789: {
790: DIR *dirp;
791: struct dirent *dp;
792: char *path;
793: int handle;
794: u_int32_t id;
795:
796: id = get_int();
797: handle = get_handle();
798: TRACE("readdir id %d handle %d", id, handle);
799: dirp = handle_to_dir(handle);
800: path = handle_to_name(handle);
801: if (dirp == NULL || path == NULL) {
802: send_status(id, SSH_FX_FAILURE);
803: } else {
804: Attrib *a;
805: struct stat st;
806: char pathname[1024];
807: Stat *stats;
808: int nstats = 10, count = 0, i;
809: stats = xmalloc(nstats * sizeof(Stat));
810: while ((dp = readdir(dirp)) != NULL) {
811: if (count >= nstats) {
812: nstats *= 2;
813: stats = xrealloc(stats, nstats * sizeof(Stat));
814: }
815: /* XXX OVERFLOW ? */
816: snprintf(pathname, sizeof pathname,
817: "%s/%s", path, dp->d_name);
818: if (lstat(pathname, &st) < 0)
819: continue;
820: a = stat_to_attrib(&st);
821: stats[count].attrib = *a;
822: stats[count].name = xstrdup(dp->d_name);
823: stats[count].long_name = ls_file(dp->d_name, &st);
824: count++;
825: /* send up to 100 entries in one message */
826: if (count == 100)
827: break;
828: }
829: send_names(id, count, stats);
830: for(i = 0; i < count; i++) {
831: xfree(stats[i].name);
832: xfree(stats[i].long_name);
833: }
834: xfree(stats);
835: }
836: }
837:
838: void
839: process_remove(void)
840: {
841: char *name;
842: u_int32_t id;
843: int status = SSH_FX_FAILURE;
844: int ret;
845:
846: id = get_int();
847: name = get_string(NULL);
848: TRACE("remove id %d name %s", id, name);
849: ret = remove(name);
850: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
851: send_status(id, status);
852: xfree(name);
853: }
854:
855: void
856: process_mkdir(void)
857: {
858: Attrib *a;
859: u_int32_t id;
860: char *name;
861: int ret, mode, status = SSH_FX_FAILURE;
862:
863: id = get_int();
864: name = get_string(NULL);
865: a = get_attrib();
866: mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777;
867: TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
868: ret = mkdir(name, mode);
869: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
870: send_status(id, status);
871: xfree(name);
872: }
873:
874: void
875: process_rmdir(void)
876: {
877: u_int32_t id;
878: char *name;
879: int ret, status;
880:
881: id = get_int();
882: name = get_string(NULL);
883: TRACE("rmdir id %d name %s", id, name);
884: ret = rmdir(name);
885: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
886: send_status(id, status);
887: xfree(name);
888: }
889:
890: void
891: process_realpath(void)
892: {
893: char resolvedname[MAXPATHLEN];
894: u_int32_t id;
895: char *path;
896:
897: id = get_int();
898: path = get_string(NULL);
899: TRACE("realpath id %d path %s", id, path);
900: if (realpath(path, resolvedname) == NULL) {
901: send_status(id, errno_to_portable(errno));
902: } else {
903: Stat s;
904: attrib_clear(&s.attrib);
905: s.name = s.long_name = resolvedname;
906: send_names(id, 1, &s);
907: }
908: xfree(path);
909: }
910:
911: void
912: process_rename(void)
913: {
914: u_int32_t id;
915: char *oldpath, *newpath;
1.2 ! markus 916: int ret, status;
1.1 markus 917:
918: id = get_int();
919: oldpath = get_string(NULL);
920: newpath = get_string(NULL);
921: TRACE("rename id %d old %s new %s", id, oldpath, newpath);
922: ret = rename(oldpath, newpath);
1.2 ! markus 923: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
1.1 markus 924: send_status(id, status);
925: xfree(oldpath);
926: xfree(newpath);
927: }
928:
929:
930: /* stolen from ssh-agent */
931:
932: void
933: process(void)
934: {
935: unsigned int msg_len;
936: unsigned int type;
937: unsigned char *cp;
938:
939: if (buffer_len(&iqueue) < 5)
940: return; /* Incomplete message. */
941: cp = (unsigned char *) buffer_ptr(&iqueue);
942: msg_len = GET_32BIT(cp);
943: if (msg_len > 256 * 1024) {
944: error("bad message ");
945: exit(11);
946: }
947: if (buffer_len(&iqueue) < msg_len + 4)
948: return;
949: buffer_consume(&iqueue, 4);
950: type = buffer_get_char(&iqueue);
951: switch (type) {
952: case SSH_FXP_INIT:
953: process_init();
954: break;
955: case SSH_FXP_OPEN:
956: process_open();
957: break;
958: case SSH_FXP_CLOSE:
959: process_close();
960: break;
961: case SSH_FXP_READ:
962: process_read();
963: break;
964: case SSH_FXP_WRITE:
965: process_write();
966: break;
967: case SSH_FXP_LSTAT:
968: process_lstat();
969: break;
970: case SSH_FXP_FSTAT:
971: process_fstat();
972: break;
973: case SSH_FXP_SETSTAT:
974: process_setstat();
975: break;
976: case SSH_FXP_FSETSTAT:
977: process_fsetstat();
978: break;
979: case SSH_FXP_OPENDIR:
980: process_opendir();
981: break;
982: case SSH_FXP_READDIR:
983: process_readdir();
984: break;
985: case SSH_FXP_REMOVE:
986: process_remove();
987: break;
988: case SSH_FXP_MKDIR:
989: process_mkdir();
990: break;
991: case SSH_FXP_RMDIR:
992: process_rmdir();
993: break;
994: case SSH_FXP_REALPATH:
995: process_realpath();
996: break;
997: case SSH_FXP_STAT:
998: process_stat();
999: break;
1000: case SSH_FXP_RENAME:
1001: process_rename();
1002: break;
1003: default:
1004: error("Unknown message %d", type);
1005: break;
1006: }
1007: }
1008:
1009: int
1010: main(int ac, char **av)
1011: {
1012: fd_set rset, wset;
1013: int in, out, max;
1014: size_t len, olen;
1015:
1016: handle_init();
1017:
1018: in = dup(STDIN_FILENO);
1019: out = dup(STDOUT_FILENO);
1020:
1021: max = 0;
1022: if (in > max)
1023: max = in;
1024: if (out > max)
1025: max = out;
1026:
1027: buffer_init(&iqueue);
1028: buffer_init(&oqueue);
1029:
1030: for (;;) {
1031: FD_ZERO(&rset);
1032: FD_ZERO(&wset);
1033:
1034: FD_SET(in, &rset);
1035: olen = buffer_len(&oqueue);
1036: if (olen > 0)
1037: FD_SET(out, &wset);
1038:
1039: if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1040: if (errno == EINTR)
1041: continue;
1042: exit(2);
1043: }
1044:
1045: /* copy stdin to iqueue */
1046: if (FD_ISSET(in, &rset)) {
1047: char buf[4*4096];
1048: len = read(in, buf, sizeof buf);
1049: if (len == 0) {
1050: debug("read eof");
1051: exit(0);
1052: } else if (len < 0) {
1053: error("read error");
1054: exit(1);
1055: } else {
1056: buffer_append(&iqueue, buf, len);
1057: }
1058: }
1059: /* send oqueue to stdout */
1060: if (FD_ISSET(out, &wset)) {
1061: len = write(out, buffer_ptr(&oqueue), olen);
1062: if (len < 0) {
1063: error("write error");
1064: exit(1);
1065: } else {
1066: buffer_consume(&oqueue, len);
1067: }
1068: }
1069: /* process requests from client */
1070: process();
1071: }
1072: }