[BACK]Return to sftp-server.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/sftp-server.c, Revision 1.1

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