Annotation of src/usr.bin/ssh/sftp-server.c, Revision 1.7
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: *
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"
1.7 ! markus 25: RCSID("$OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp $");
1.1 markus 26:
27: #include "ssh.h"
28: #include "buffer.h"
29: #include "bufaux.h"
30: #include "getput.h"
31: #include "xmalloc.h"
32:
33: /* version */
34: #define SSH_FILEXFER_VERSION 2
35:
36: /* client to server */
37: #define SSH_FXP_INIT 1
38: #define SSH_FXP_OPEN 3
39: #define SSH_FXP_CLOSE 4
40: #define SSH_FXP_READ 5
41: #define SSH_FXP_WRITE 6
42: #define SSH_FXP_LSTAT 7
43: #define SSH_FXP_FSTAT 8
44: #define SSH_FXP_SETSTAT 9
45: #define SSH_FXP_FSETSTAT 10
46: #define SSH_FXP_OPENDIR 11
47: #define SSH_FXP_READDIR 12
48: #define SSH_FXP_REMOVE 13
49: #define SSH_FXP_MKDIR 14
50: #define SSH_FXP_RMDIR 15
51: #define SSH_FXP_REALPATH 16
52: #define SSH_FXP_STAT 17
53: #define SSH_FXP_RENAME 18
54:
55: /* server to client */
56: #define SSH_FXP_VERSION 2
57: #define SSH_FXP_STATUS 101
58: #define SSH_FXP_HANDLE 102
59: #define SSH_FXP_DATA 103
60: #define SSH_FXP_NAME 104
61: #define SSH_FXP_ATTRS 105
62:
63: /* portable open modes */
64: #define SSH_FXF_READ 0x01
65: #define SSH_FXF_WRITE 0x02
66: #define SSH_FXF_APPEND 0x04
67: #define SSH_FXF_CREAT 0x08
68: #define SSH_FXF_TRUNC 0x10
69: #define SSH_FXF_EXCL 0x20
70:
71: /* attributes */
72: #define SSH_FXA_HAVE_SIZE 0x01
73: #define SSH_FXA_HAVE_UGID 0x02
74: #define SSH_FXA_HAVE_PERM 0x04
75: #define SSH_FXA_HAVE_TIME 0x08
76:
77: /* status messages */
78: #define SSH_FX_OK 0x00
79: #define SSH_FX_EOF 0x01
80: #define SSH_FX_NO_SUCH_FILE 0x02
81: #define SSH_FX_PERMISSION_DENIED 0x03
82: #define SSH_FX_FAILURE 0x04
83: #define SSH_FX_BAD_MESSAGE 0x05
84: #define SSH_FX_NO_CONNECTION 0x06
85: #define SSH_FX_CONNECTION_LOST 0x07
86:
87:
88: /* helper */
89: #define get_int() buffer_get_int(&iqueue);
90: #define get_string(lenp) buffer_get_string(&iqueue, lenp);
91: #define TRACE log
92:
93: /* input and output queue */
94: Buffer iqueue;
95: Buffer oqueue;
96:
97: /* portable attibutes, etc. */
98:
99: typedef struct Attrib Attrib;
100: typedef struct Stat Stat;
101:
102: struct Attrib
103: {
104: u_int32_t flags;
105: u_int32_t size_high;
106: u_int32_t size_low;
107: u_int64_t size;
108: u_int32_t uid;
109: u_int32_t gid;
110: u_int32_t perm;
111: u_int32_t atime;
112: u_int32_t mtime;
113: };
114:
115: struct Stat
116: {
117: char *name;
118: char *long_name;
119: Attrib attrib;
120: };
121:
122: int
1.2 markus 123: errno_to_portable(int unixerrno)
1.1 markus 124: {
125: int ret = 0;
1.2 markus 126: switch (unixerrno) {
1.1 markus 127: case 0:
128: ret = SSH_FX_OK;
129: break;
130: case ENOENT:
131: case ENOTDIR:
132: case EBADF:
133: case ELOOP:
134: ret = SSH_FX_NO_SUCH_FILE;
135: break;
136: case EPERM:
137: case EACCES:
138: case EFAULT:
139: ret = SSH_FX_PERMISSION_DENIED;
140: break;
141: case ENAMETOOLONG:
142: case EINVAL:
143: ret = SSH_FX_BAD_MESSAGE;
144: break;
145: default:
146: ret = SSH_FX_FAILURE;
147: break;
148: }
149: return ret;
150: }
151:
152: int
153: flags_from_portable(int pflags)
154: {
155: int flags = 0;
156: if (pflags & SSH_FXF_READ &&
157: pflags & SSH_FXF_WRITE) {
158: flags = O_RDWR;
159: } else if (pflags & SSH_FXF_READ) {
160: flags = O_RDONLY;
161: } else if (pflags & SSH_FXF_WRITE) {
162: flags = O_WRONLY;
163: }
164: if (pflags & SSH_FXF_CREAT)
165: flags |= O_CREAT;
166: if (pflags & SSH_FXF_TRUNC)
167: flags |= O_TRUNC;
168: if (pflags & SSH_FXF_EXCL)
169: flags |= O_EXCL;
170: return flags;
171: }
172:
173: void
174: attrib_clear(Attrib *a)
175: {
176: a->flags = 0;
177: a->size_low = 0;
178: a->size_high = 0;
179: a->size = 0;
180: a->uid = 0;
181: a->gid = 0;
182: a->perm = 0;
183: a->atime = 0;
184: a->mtime = 0;
185: }
186:
187: Attrib *
188: decode_attrib(Buffer *b)
189: {
190: static Attrib a;
191: attrib_clear(&a);
1.5 markus 192: a.flags = buffer_get_int(b);
1.1 markus 193: if (a.flags & SSH_FXA_HAVE_SIZE) {
1.5 markus 194: a.size_high = buffer_get_int(b);
195: a.size_low = buffer_get_int(b);
1.4 markus 196: a.size = (((u_int64_t) a.size_high) << 32) + a.size_low;
1.1 markus 197: }
198: if (a.flags & SSH_FXA_HAVE_UGID) {
1.5 markus 199: a.uid = buffer_get_int(b);
200: a.gid = buffer_get_int(b);
1.1 markus 201: }
202: if (a.flags & SSH_FXA_HAVE_PERM) {
1.5 markus 203: a.perm = buffer_get_int(b);
1.1 markus 204: }
205: if (a.flags & SSH_FXA_HAVE_TIME) {
1.5 markus 206: a.atime = buffer_get_int(b);
207: a.mtime = buffer_get_int(b);
1.1 markus 208: }
209: return &a;
210: }
211:
212: void
213: encode_attrib(Buffer *b, Attrib *a)
214: {
215: buffer_put_int(b, a->flags);
216: if (a->flags & SSH_FXA_HAVE_SIZE) {
217: buffer_put_int(b, a->size_high);
218: buffer_put_int(b, a->size_low);
219: }
220: if (a->flags & SSH_FXA_HAVE_UGID) {
221: buffer_put_int(b, a->uid);
222: buffer_put_int(b, a->gid);
223: }
224: if (a->flags & SSH_FXA_HAVE_PERM) {
225: buffer_put_int(b, a->perm);
226: }
227: if (a->flags & SSH_FXA_HAVE_TIME) {
228: buffer_put_int(b, a->atime);
229: buffer_put_int(b, a->mtime);
230: }
231: }
232:
233: Attrib *
234: stat_to_attrib(struct stat *st)
235: {
236: static Attrib a;
237: attrib_clear(&a);
238: a.flags = 0;
239: a.flags |= SSH_FXA_HAVE_SIZE;
240: a.size = st->st_size;
1.2 markus 241: a.size_low = a.size;
1.3 markus 242: a.size_high = (u_int32_t) (a.size >> 32);
1.1 markus 243: a.flags |= SSH_FXA_HAVE_UGID;
244: a.uid = st->st_uid;
245: a.gid = st->st_gid;
246: a.flags |= SSH_FXA_HAVE_PERM;
247: a.perm = st->st_mode;
248: a.flags |= SSH_FXA_HAVE_TIME;
249: a.atime = st->st_atime;
250: a.mtime = st->st_mtime;
251: return &a;
252: }
253:
254: Attrib *
255: get_attrib(void)
256: {
257: return decode_attrib(&iqueue);
258: }
259:
260: /* handle handles */
261:
262: typedef struct Handle Handle;
263: struct Handle {
264: int use;
265: DIR *dirp;
266: int fd;
267: char *name;
268: };
269: enum {
270: HANDLE_UNUSED,
271: HANDLE_DIR,
272: HANDLE_FILE
273: };
274: Handle handles[100];
275:
276: void
277: handle_init(void)
278: {
279: int i;
280: for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
281: handles[i].use = HANDLE_UNUSED;
282: }
283:
284: int
285: handle_new(int use, char *name, int fd, DIR *dirp)
286: {
287: int i;
288: for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
289: if (handles[i].use == HANDLE_UNUSED) {
290: handles[i].use = use;
291: handles[i].dirp = dirp;
292: handles[i].fd = fd;
293: handles[i].name = name;
294: return i;
295: }
296: }
297: return -1;
298: }
299:
300: int
301: handle_is_ok(int i, int type)
302: {
303: return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type;
304: }
305:
306: int
307: handle_to_string(int handle, char **stringp, int *hlenp)
308: {
309: char buf[1024];
310: if (stringp == NULL || hlenp == NULL)
311: return -1;
312: snprintf(buf, sizeof buf, "%d", handle);
313: *stringp = xstrdup(buf);
314: *hlenp = strlen(*stringp);
315: return 0;
316: }
317:
318: int
1.5 markus 319: handle_from_string(char *handle, u_int hlen)
1.1 markus 320: {
321: /* XXX OVERFLOW ? */
322: char *ep;
323: long lval = strtol(handle, &ep, 10);
324: int val = lval;
325: if (*ep != '\0')
326: return -1;
327: if (handle_is_ok(val, HANDLE_FILE) ||
328: handle_is_ok(val, HANDLE_DIR))
329: return val;
330: return -1;
331: }
332:
333: char *
334: handle_to_name(int handle)
335: {
336: if (handle_is_ok(handle, HANDLE_DIR)||
337: handle_is_ok(handle, HANDLE_FILE))
338: return handles[handle].name;
339: return NULL;
340: }
341:
342: DIR *
343: handle_to_dir(int handle)
344: {
345: if (handle_is_ok(handle, HANDLE_DIR))
346: return handles[handle].dirp;
347: return NULL;
348: }
349:
350: int
351: handle_to_fd(int handle)
352: {
353: if (handle_is_ok(handle, HANDLE_FILE))
354: return handles[handle].fd;
355: return -1;
356: }
357:
358: int
359: handle_close(int handle)
360: {
361: int ret = -1;
362: if (handle_is_ok(handle, HANDLE_FILE)) {
363: ret = close(handles[handle].fd);
364: handles[handle].use = HANDLE_UNUSED;
365: } else if (handle_is_ok(handle, HANDLE_DIR)) {
366: ret = closedir(handles[handle].dirp);
367: handles[handle].use = HANDLE_UNUSED;
368: } else {
369: errno = ENOENT;
370: }
371: return ret;
372: }
373:
374: int
375: get_handle(void)
376: {
377: char *handle;
1.5 markus 378: int val;
379: u_int hlen;
1.1 markus 380: handle = get_string(&hlen);
381: val = handle_from_string(handle, hlen);
382: xfree(handle);
383: return val;
384: }
385:
386: /* send replies */
387:
388: void
389: send_msg(Buffer *m)
390: {
391: int mlen = buffer_len(m);
392: buffer_put_int(&oqueue, mlen);
393: buffer_append(&oqueue, buffer_ptr(m), mlen);
394: buffer_consume(m, mlen);
395: }
396:
397: void
398: send_status(u_int32_t id, u_int32_t error)
399: {
400: Buffer msg;
401: TRACE("sent status id %d error %d", id, error);
402: buffer_init(&msg);
403: buffer_put_char(&msg, SSH_FXP_STATUS);
404: buffer_put_int(&msg, id);
405: buffer_put_int(&msg, error);
406: send_msg(&msg);
407: buffer_free(&msg);
408: }
409: void
410: send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
411: {
412: Buffer msg;
413: buffer_init(&msg);
414: buffer_put_char(&msg, type);
415: buffer_put_int(&msg, id);
416: buffer_put_string(&msg, data, dlen);
417: send_msg(&msg);
418: buffer_free(&msg);
419: }
420:
421: void
422: send_data(u_int32_t id, char *data, int dlen)
423: {
424: TRACE("sent data id %d len %d", id, dlen);
425: send_data_or_handle(SSH_FXP_DATA, id, data, dlen);
426: }
427:
428: void
429: send_handle(u_int32_t id, int handle)
430: {
431: char *string;
432: int hlen;
433: handle_to_string(handle, &string, &hlen);
434: TRACE("sent handle id %d handle %d", id, handle);
435: send_data_or_handle(SSH_FXP_HANDLE, id, string, hlen);
436: xfree(string);
437: }
438:
439: void
440: send_names(u_int32_t id, int count, Stat *stats)
441: {
442: Buffer msg;
443: int i;
444: buffer_init(&msg);
445: buffer_put_char(&msg, SSH_FXP_NAME);
446: buffer_put_int(&msg, id);
447: buffer_put_int(&msg, count);
448: TRACE("sent names id %d count %d", id, count);
449: for (i = 0; i < count; i++) {
450: buffer_put_cstring(&msg, stats[i].name);
451: buffer_put_cstring(&msg, stats[i].long_name);
452: encode_attrib(&msg, &stats[i].attrib);
453: }
454: send_msg(&msg);
455: buffer_free(&msg);
456: }
457:
458: void
459: send_attrib(u_int32_t id, Attrib *a)
460: {
461: Buffer msg;
462: TRACE("sent attrib id %d have 0x%x", id, a->flags);
463: buffer_init(&msg);
464: buffer_put_char(&msg, SSH_FXP_ATTRS);
465: buffer_put_int(&msg, id);
466: encode_attrib(&msg, a);
467: send_msg(&msg);
468: buffer_free(&msg);
469: }
470:
471: /* parse incoming */
472:
473: void
474: process_init(void)
475: {
476: Buffer msg;
477: int version = buffer_get_int(&iqueue);
478:
479: TRACE("client version %d", version);
480: buffer_init(&msg);
481: buffer_put_char(&msg, SSH_FXP_VERSION);
482: buffer_put_int(&msg, SSH_FILEXFER_VERSION);
483: send_msg(&msg);
484: buffer_free(&msg);
485: }
486:
487: void
488: process_open(void)
489: {
490: u_int32_t id, pflags;
491: Attrib *a;
492: char *name;
493: int handle, fd, flags, mode, status = SSH_FX_FAILURE;
494:
495: id = get_int();
496: name = get_string(NULL);
497: pflags = get_int();
498: a = get_attrib();
499: flags = flags_from_portable(pflags);
500: mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666;
501: TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
502: fd = open(name, flags, mode);
503: if (fd < 0) {
504: status = errno_to_portable(errno);
505: } else {
506: handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
507: if (handle < 0) {
508: close(fd);
509: } else {
510: send_handle(id, handle);
511: status = SSH_FX_OK;
512: }
513: }
514: if (status != SSH_FX_OK)
515: send_status(id, status);
516: xfree(name);
517: }
518:
519: void
520: process_close(void)
521: {
522: u_int32_t id;
523: int handle, ret, status = SSH_FX_FAILURE;
524:
525: id = get_int();
526: handle = get_handle();
527: TRACE("close id %d handle %d", id, handle);
528: ret = handle_close(handle);
529: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
530: send_status(id, status);
531: }
532:
533: void
534: process_read(void)
535: {
536: char buf[64*1024];
537: u_int32_t id, off_high, off_low, len;
538: int handle, fd, ret, status = SSH_FX_FAILURE;
539: u_int64_t off;
540:
541: id = get_int();
542: handle = get_handle();
543: off_high = get_int();
544: off_low = get_int();
545: len = get_int();
546:
1.4 markus 547: off = (((u_int64_t) off_high) << 32) + off_low;
1.1 markus 548: TRACE("read id %d handle %d off %qd len %d", id, handle, off, len);
549: if (len > sizeof buf) {
550: len = sizeof buf;
551: log("read change len %d", len);
552: }
553: fd = handle_to_fd(handle);
554: if (fd >= 0) {
555: if (lseek(fd, off, SEEK_SET) < 0) {
556: error("process_read: seek failed");
557: status = errno_to_portable(errno);
558: } else {
559: ret = read(fd, buf, len);
560: if (ret < 0) {
561: status = errno_to_portable(errno);
562: } else if (ret == 0) {
563: status = SSH_FX_EOF;
564: } else {
565: send_data(id, buf, ret);
566: status = SSH_FX_OK;
567: }
568: }
569: }
570: if (status != SSH_FX_OK)
571: send_status(id, status);
572: }
573:
574: void
575: process_write(void)
576: {
577: u_int32_t id, off_high, off_low;
578: u_int64_t off;
1.5 markus 579: u_int len;
1.1 markus 580: int handle, fd, ret, status = SSH_FX_FAILURE;
581: char *data;
582:
583: id = get_int();
584: handle = get_handle();
585: off_high = get_int();
586: off_low = get_int();
587: data = get_string(&len);
588:
1.4 markus 589: off = (((u_int64_t) off_high) << 32) + off_low;
1.1 markus 590: TRACE("write id %d handle %d off %qd len %d", id, handle, off, len);
591: fd = handle_to_fd(handle);
592: if (fd >= 0) {
593: if (lseek(fd, off, SEEK_SET) < 0) {
594: status = errno_to_portable(errno);
595: error("process_write: seek failed");
596: } else {
597: /* XXX ATOMICIO ? */
598: ret = write(fd, data, len);
599: if (ret == -1) {
600: error("process_write: write failed");
601: status = errno_to_portable(errno);
602: } else if (ret == len) {
603: status = SSH_FX_OK;
604: } else {
605: log("nothing at all written");
606: }
607: }
608: }
609: send_status(id, status);
610: xfree(data);
611: }
612:
613: void
614: process_do_stat(int do_lstat)
615: {
616: Attrib *a;
617: struct stat st;
618: u_int32_t id;
619: char *name;
620: int ret, status = SSH_FX_FAILURE;
621:
622: id = get_int();
623: name = get_string(NULL);
624: TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
625: ret = do_lstat ? lstat(name, &st) : stat(name, &st);
626: if (ret < 0) {
627: status = errno_to_portable(errno);
628: } else {
629: a = stat_to_attrib(&st);
630: send_attrib(id, a);
631: status = SSH_FX_OK;
632: }
633: if (status != SSH_FX_OK)
634: send_status(id, status);
635: xfree(name);
636: }
637:
638: void
639: process_stat(void)
640: {
641: process_do_stat(0);
642: }
643:
644: void
645: process_lstat(void)
646: {
647: process_do_stat(1);
648: }
649:
650: void
651: process_fstat(void)
652: {
653: Attrib *a;
654: struct stat st;
655: u_int32_t id;
656: int fd, ret, handle, status = SSH_FX_FAILURE;
657:
658: id = get_int();
659: handle = get_handle();
660: TRACE("fstat id %d handle %d", id, handle);
661: fd = handle_to_fd(handle);
662: if (fd >= 0) {
663: ret = fstat(fd, &st);
664: if (ret < 0) {
665: status = errno_to_portable(errno);
666: } else {
667: a = stat_to_attrib(&st);
668: send_attrib(id, a);
669: status = SSH_FX_OK;
670: }
671: }
672: if (status != SSH_FX_OK)
673: send_status(id, status);
674: }
675:
676: struct timeval *
677: attrib_to_tv(Attrib *a)
678: {
679: static struct timeval tv[2];
680: tv[0].tv_sec = a->atime;
681: tv[0].tv_usec = 0;
682: tv[1].tv_sec = a->mtime;
683: tv[1].tv_usec = 0;
684: return tv;
685: }
686:
687: void
688: process_setstat(void)
689: {
690: Attrib *a;
691: u_int32_t id;
692: char *name;
693: int ret;
694: int status = SSH_FX_OK;
695:
696: id = get_int();
697: name = get_string(NULL);
698: a = get_attrib();
699: TRACE("setstat id %d name %s", id, name);
700: if (a->flags & SSH_FXA_HAVE_PERM) {
701: ret = chmod(name, a->perm & 0777);
702: if (ret == -1)
703: status = errno_to_portable(errno);
704: }
705: if (a->flags & SSH_FXA_HAVE_TIME) {
706: ret = utimes(name, attrib_to_tv(a));
707: if (ret == -1)
708: status = errno_to_portable(errno);
709: }
710: send_status(id, status);
711: xfree(name);
712: }
713:
714: void
715: process_fsetstat(void)
716: {
717: Attrib *a;
718: u_int32_t id;
719: int handle, fd, ret;
720: int status = SSH_FX_OK;
721:
722: id = get_int();
723: handle = get_handle();
724: a = get_attrib();
725: TRACE("fsetstat id %d handle %d", id, handle);
726: fd = handle_to_fd(handle);
727: if (fd < 0) {
728: status = SSH_FX_FAILURE;
729: } else {
730: if (a->flags & SSH_FXA_HAVE_PERM) {
731: ret = fchmod(fd, a->perm & 0777);
732: if (ret == -1)
733: status = errno_to_portable(errno);
734: }
735: if (a->flags & SSH_FXA_HAVE_TIME) {
736: ret = futimes(fd, attrib_to_tv(a));
737: if (ret == -1)
738: status = errno_to_portable(errno);
739: }
740: }
741: send_status(id, status);
742: }
743:
744: void
745: process_opendir(void)
746: {
747: DIR *dirp = NULL;
748: char *path;
749: int handle, status = SSH_FX_FAILURE;
750: u_int32_t id;
751:
752: id = get_int();
753: path = get_string(NULL);
754: TRACE("opendir id %d path %s", id, path);
755: dirp = opendir(path);
756: if (dirp == NULL) {
757: status = errno_to_portable(errno);
758: } else {
759: handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
760: if (handle < 0) {
761: closedir(dirp);
762: } else {
763: send_handle(id, handle);
764: status = SSH_FX_OK;
765: }
766:
767: }
768: if (status != SSH_FX_OK)
769: send_status(id, status);
770: xfree(path);
771: }
772:
773: char *
774: ls_file(char *name, struct stat *st)
775: {
776: char buf[1024];
777: snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s",
1.4 markus 778: st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,(int) st->st_mtime,
1.1 markus 779: name);
780: return xstrdup(buf);
781: }
782:
783: void
784: process_readdir(void)
785: {
786: DIR *dirp;
787: struct dirent *dp;
788: char *path;
789: int handle;
790: u_int32_t id;
791:
792: id = get_int();
793: handle = get_handle();
794: TRACE("readdir id %d handle %d", id, handle);
795: dirp = handle_to_dir(handle);
796: path = handle_to_name(handle);
797: if (dirp == NULL || path == NULL) {
798: send_status(id, SSH_FX_FAILURE);
799: } else {
800: Attrib *a;
801: struct stat st;
802: char pathname[1024];
803: Stat *stats;
804: int nstats = 10, count = 0, i;
805: stats = xmalloc(nstats * sizeof(Stat));
806: while ((dp = readdir(dirp)) != NULL) {
807: if (count >= nstats) {
808: nstats *= 2;
809: stats = xrealloc(stats, nstats * sizeof(Stat));
810: }
811: /* XXX OVERFLOW ? */
812: snprintf(pathname, sizeof pathname,
813: "%s/%s", path, dp->d_name);
814: if (lstat(pathname, &st) < 0)
815: continue;
816: a = stat_to_attrib(&st);
817: stats[count].attrib = *a;
818: stats[count].name = xstrdup(dp->d_name);
819: stats[count].long_name = ls_file(dp->d_name, &st);
820: count++;
821: /* send up to 100 entries in one message */
822: if (count == 100)
823: break;
824: }
825: send_names(id, count, stats);
826: for(i = 0; i < count; i++) {
827: xfree(stats[i].name);
828: xfree(stats[i].long_name);
829: }
830: xfree(stats);
831: }
832: }
833:
834: void
835: process_remove(void)
836: {
837: char *name;
838: u_int32_t id;
839: int status = SSH_FX_FAILURE;
840: int ret;
841:
842: id = get_int();
843: name = get_string(NULL);
844: TRACE("remove id %d name %s", id, name);
845: ret = remove(name);
846: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
847: send_status(id, status);
848: xfree(name);
849: }
850:
851: void
852: process_mkdir(void)
853: {
854: Attrib *a;
855: u_int32_t id;
856: char *name;
857: int ret, mode, status = SSH_FX_FAILURE;
858:
859: id = get_int();
860: name = get_string(NULL);
861: a = get_attrib();
862: mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777;
863: TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
864: ret = mkdir(name, mode);
865: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
866: send_status(id, status);
867: xfree(name);
868: }
869:
870: void
871: process_rmdir(void)
872: {
873: u_int32_t id;
874: char *name;
875: int ret, status;
876:
877: id = get_int();
878: name = get_string(NULL);
879: TRACE("rmdir id %d name %s", id, name);
880: ret = rmdir(name);
881: status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK;
882: send_status(id, status);
883: xfree(name);
884: }
885:
886: void
887: process_realpath(void)
888: {
889: char resolvedname[MAXPATHLEN];
890: u_int32_t id;
891: char *path;
892:
893: id = get_int();
894: path = get_string(NULL);
1.7 ! markus 895: if (path[0] == '\0') {
! 896: xfree(path);
! 897: path = xstrdup(".");
! 898: }
1.1 markus 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;
1.5 markus 1014: ssize_t len, olen;
1.1 markus 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: }