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: }