[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.5

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