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