sftp-client.c revision 215116
1/* $OpenBSD: sftp-client.c,v 1.92 2010/07/19 03:16:33 djm Exp $ */ 2/* 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* XXX: memleaks */ 19/* XXX: signed vs unsigned */ 20/* XXX: remove all logging, only return status codes */ 21/* XXX: copy between two remote sites */ 22 23#include "includes.h" 24 25#include <sys/types.h> 26#include <sys/param.h> 27#ifdef HAVE_SYS_STATVFS_H 28#include <sys/statvfs.h> 29#endif 30#include "openbsd-compat/sys-queue.h" 31#ifdef HAVE_SYS_STAT_H 32# include <sys/stat.h> 33#endif 34#ifdef HAVE_SYS_TIME_H 35# include <sys/time.h> 36#endif 37#include <sys/uio.h> 38 39#include <dirent.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <signal.h> 43#include <stdarg.h> 44#include <stdio.h> 45#include <string.h> 46#include <unistd.h> 47 48#include "xmalloc.h" 49#include "buffer.h" 50#include "log.h" 51#include "atomicio.h" 52#include "progressmeter.h" 53#include "misc.h" 54 55#include "sftp.h" 56#include "sftp-common.h" 57#include "sftp-client.h" 58 59extern volatile sig_atomic_t interrupted; 60extern int showprogress; 61 62/* Minimum amount of data to read at a time */ 63#define MIN_READ_SIZE 512 64 65/* Maximum depth to descend in directory trees */ 66#define MAX_DIR_DEPTH 64 67 68struct sftp_conn { 69 int fd_in; 70 int fd_out; 71 u_int transfer_buflen; 72 u_int num_requests; 73 u_int version; 74 u_int msg_id; 75#define SFTP_EXT_POSIX_RENAME 0x00000001 76#define SFTP_EXT_STATVFS 0x00000002 77#define SFTP_EXT_FSTATVFS 0x00000004 78 u_int exts; 79}; 80 81static char * 82get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) 83 __attribute__((format(printf, 4, 5))); 84 85static void 86send_msg(int fd, Buffer *m) 87{ 88 u_char mlen[4]; 89 struct iovec iov[2]; 90 91 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH) 92 fatal("Outbound message too long %u", buffer_len(m)); 93 94 /* Send length first */ 95 put_u32(mlen, buffer_len(m)); 96 iov[0].iov_base = mlen; 97 iov[0].iov_len = sizeof(mlen); 98 iov[1].iov_base = buffer_ptr(m); 99 iov[1].iov_len = buffer_len(m); 100 101 if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) 102 fatal("Couldn't send packet: %s", strerror(errno)); 103 104 buffer_clear(m); 105} 106 107static void 108get_msg(int fd, Buffer *m) 109{ 110 u_int msg_len; 111 112 buffer_append_space(m, 4); 113 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { 114 if (errno == EPIPE) 115 fatal("Connection closed"); 116 else 117 fatal("Couldn't read packet: %s", strerror(errno)); 118 } 119 120 msg_len = buffer_get_int(m); 121 if (msg_len > SFTP_MAX_MSG_LENGTH) 122 fatal("Received message too long %u", msg_len); 123 124 buffer_append_space(m, msg_len); 125 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { 126 if (errno == EPIPE) 127 fatal("Connection closed"); 128 else 129 fatal("Read packet: %s", strerror(errno)); 130 } 131} 132 133static void 134send_string_request(int fd, u_int id, u_int code, char *s, 135 u_int len) 136{ 137 Buffer msg; 138 139 buffer_init(&msg); 140 buffer_put_char(&msg, code); 141 buffer_put_int(&msg, id); 142 buffer_put_string(&msg, s, len); 143 send_msg(fd, &msg); 144 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 145 buffer_free(&msg); 146} 147 148static void 149send_string_attrs_request(int fd, u_int id, u_int code, char *s, 150 u_int len, Attrib *a) 151{ 152 Buffer msg; 153 154 buffer_init(&msg); 155 buffer_put_char(&msg, code); 156 buffer_put_int(&msg, id); 157 buffer_put_string(&msg, s, len); 158 encode_attrib(&msg, a); 159 send_msg(fd, &msg); 160 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 161 buffer_free(&msg); 162} 163 164static u_int 165get_status(int fd, u_int expected_id) 166{ 167 Buffer msg; 168 u_int type, id, status; 169 170 buffer_init(&msg); 171 get_msg(fd, &msg); 172 type = buffer_get_char(&msg); 173 id = buffer_get_int(&msg); 174 175 if (id != expected_id) 176 fatal("ID mismatch (%u != %u)", id, expected_id); 177 if (type != SSH2_FXP_STATUS) 178 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 179 SSH2_FXP_STATUS, type); 180 181 status = buffer_get_int(&msg); 182 buffer_free(&msg); 183 184 debug3("SSH2_FXP_STATUS %u", status); 185 186 return(status); 187} 188 189static char * 190get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) 191{ 192 Buffer msg; 193 u_int type, id; 194 char *handle, errmsg[256]; 195 va_list args; 196 int status; 197 198 va_start(args, errfmt); 199 if (errfmt != NULL) 200 vsnprintf(errmsg, sizeof(errmsg), errfmt, args); 201 va_end(args); 202 203 buffer_init(&msg); 204 get_msg(fd, &msg); 205 type = buffer_get_char(&msg); 206 id = buffer_get_int(&msg); 207 208 if (id != expected_id) 209 fatal("%s: ID mismatch (%u != %u)", 210 errfmt == NULL ? __func__ : errmsg, id, expected_id); 211 if (type == SSH2_FXP_STATUS) { 212 status = buffer_get_int(&msg); 213 if (errfmt != NULL) 214 error("%s: %s", errmsg, fx2txt(status)); 215 buffer_free(&msg); 216 return(NULL); 217 } else if (type != SSH2_FXP_HANDLE) 218 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", 219 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); 220 221 handle = buffer_get_string(&msg, len); 222 buffer_free(&msg); 223 224 return(handle); 225} 226 227static Attrib * 228get_decode_stat(int fd, u_int expected_id, int quiet) 229{ 230 Buffer msg; 231 u_int type, id; 232 Attrib *a; 233 234 buffer_init(&msg); 235 get_msg(fd, &msg); 236 237 type = buffer_get_char(&msg); 238 id = buffer_get_int(&msg); 239 240 debug3("Received stat reply T:%u I:%u", type, id); 241 if (id != expected_id) 242 fatal("ID mismatch (%u != %u)", id, expected_id); 243 if (type == SSH2_FXP_STATUS) { 244 int status = buffer_get_int(&msg); 245 246 if (quiet) 247 debug("Couldn't stat remote file: %s", fx2txt(status)); 248 else 249 error("Couldn't stat remote file: %s", fx2txt(status)); 250 buffer_free(&msg); 251 return(NULL); 252 } else if (type != SSH2_FXP_ATTRS) { 253 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 254 SSH2_FXP_ATTRS, type); 255 } 256 a = decode_attrib(&msg); 257 buffer_free(&msg); 258 259 return(a); 260} 261 262static int 263get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id, 264 int quiet) 265{ 266 Buffer msg; 267 u_int type, id, flag; 268 269 buffer_init(&msg); 270 get_msg(fd, &msg); 271 272 type = buffer_get_char(&msg); 273 id = buffer_get_int(&msg); 274 275 debug3("Received statvfs reply T:%u I:%u", type, id); 276 if (id != expected_id) 277 fatal("ID mismatch (%u != %u)", id, expected_id); 278 if (type == SSH2_FXP_STATUS) { 279 int status = buffer_get_int(&msg); 280 281 if (quiet) 282 debug("Couldn't statvfs: %s", fx2txt(status)); 283 else 284 error("Couldn't statvfs: %s", fx2txt(status)); 285 buffer_free(&msg); 286 return -1; 287 } else if (type != SSH2_FXP_EXTENDED_REPLY) { 288 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", 289 SSH2_FXP_EXTENDED_REPLY, type); 290 } 291 292 bzero(st, sizeof(*st)); 293 st->f_bsize = buffer_get_int64(&msg); 294 st->f_frsize = buffer_get_int64(&msg); 295 st->f_blocks = buffer_get_int64(&msg); 296 st->f_bfree = buffer_get_int64(&msg); 297 st->f_bavail = buffer_get_int64(&msg); 298 st->f_files = buffer_get_int64(&msg); 299 st->f_ffree = buffer_get_int64(&msg); 300 st->f_favail = buffer_get_int64(&msg); 301 st->f_fsid = buffer_get_int64(&msg); 302 flag = buffer_get_int64(&msg); 303 st->f_namemax = buffer_get_int64(&msg); 304 305 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; 306 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; 307 308 buffer_free(&msg); 309 310 return 0; 311} 312 313struct sftp_conn * 314do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 315{ 316 u_int type, exts = 0; 317 int version; 318 Buffer msg; 319 struct sftp_conn *ret; 320 321 buffer_init(&msg); 322 buffer_put_char(&msg, SSH2_FXP_INIT); 323 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 324 send_msg(fd_out, &msg); 325 326 buffer_clear(&msg); 327 328 get_msg(fd_in, &msg); 329 330 /* Expecting a VERSION reply */ 331 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 332 error("Invalid packet back from SSH2_FXP_INIT (type %u)", 333 type); 334 buffer_free(&msg); 335 return(NULL); 336 } 337 version = buffer_get_int(&msg); 338 339 debug2("Remote version: %d", version); 340 341 /* Check for extensions */ 342 while (buffer_len(&msg) > 0) { 343 char *name = buffer_get_string(&msg, NULL); 344 char *value = buffer_get_string(&msg, NULL); 345 int known = 0; 346 347 if (strcmp(name, "posix-rename@openssh.com") == 0 && 348 strcmp(value, "1") == 0) { 349 exts |= SFTP_EXT_POSIX_RENAME; 350 known = 1; 351 } else if (strcmp(name, "statvfs@openssh.com") == 0 && 352 strcmp(value, "2") == 0) { 353 exts |= SFTP_EXT_STATVFS; 354 known = 1; 355 } if (strcmp(name, "fstatvfs@openssh.com") == 0 && 356 strcmp(value, "2") == 0) { 357 exts |= SFTP_EXT_FSTATVFS; 358 known = 1; 359 } 360 if (known) { 361 debug2("Server supports extension \"%s\" revision %s", 362 name, value); 363 } else { 364 debug2("Unrecognised server extension \"%s\"", name); 365 } 366 xfree(name); 367 xfree(value); 368 } 369 370 buffer_free(&msg); 371 372 ret = xmalloc(sizeof(*ret)); 373 ret->fd_in = fd_in; 374 ret->fd_out = fd_out; 375 ret->transfer_buflen = transfer_buflen; 376 ret->num_requests = num_requests; 377 ret->version = version; 378 ret->msg_id = 1; 379 ret->exts = exts; 380 381 /* Some filexfer v.0 servers don't support large packets */ 382 if (version == 0) 383 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); 384 385 return(ret); 386} 387 388u_int 389sftp_proto_version(struct sftp_conn *conn) 390{ 391 return(conn->version); 392} 393 394int 395do_close(struct sftp_conn *conn, char *handle, u_int handle_len) 396{ 397 u_int id, status; 398 Buffer msg; 399 400 buffer_init(&msg); 401 402 id = conn->msg_id++; 403 buffer_put_char(&msg, SSH2_FXP_CLOSE); 404 buffer_put_int(&msg, id); 405 buffer_put_string(&msg, handle, handle_len); 406 send_msg(conn->fd_out, &msg); 407 debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 408 409 status = get_status(conn->fd_in, id); 410 if (status != SSH2_FX_OK) 411 error("Couldn't close file: %s", fx2txt(status)); 412 413 buffer_free(&msg); 414 415 return(status); 416} 417 418 419static int 420do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, 421 SFTP_DIRENT ***dir) 422{ 423 Buffer msg; 424 u_int count, type, id, handle_len, i, expected_id, ents = 0; 425 char *handle; 426 427 id = conn->msg_id++; 428 429 buffer_init(&msg); 430 buffer_put_char(&msg, SSH2_FXP_OPENDIR); 431 buffer_put_int(&msg, id); 432 buffer_put_cstring(&msg, path); 433 send_msg(conn->fd_out, &msg); 434 435 buffer_clear(&msg); 436 437 handle = get_handle(conn->fd_in, id, &handle_len, 438 "remote readdir(\"%s\")", path); 439 if (handle == NULL) 440 return(-1); 441 442 if (dir) { 443 ents = 0; 444 *dir = xmalloc(sizeof(**dir)); 445 (*dir)[0] = NULL; 446 } 447 448 for (; !interrupted;) { 449 id = expected_id = conn->msg_id++; 450 451 debug3("Sending SSH2_FXP_READDIR I:%u", id); 452 453 buffer_clear(&msg); 454 buffer_put_char(&msg, SSH2_FXP_READDIR); 455 buffer_put_int(&msg, id); 456 buffer_put_string(&msg, handle, handle_len); 457 send_msg(conn->fd_out, &msg); 458 459 buffer_clear(&msg); 460 461 get_msg(conn->fd_in, &msg); 462 463 type = buffer_get_char(&msg); 464 id = buffer_get_int(&msg); 465 466 debug3("Received reply T:%u I:%u", type, id); 467 468 if (id != expected_id) 469 fatal("ID mismatch (%u != %u)", id, expected_id); 470 471 if (type == SSH2_FXP_STATUS) { 472 int status = buffer_get_int(&msg); 473 474 debug3("Received SSH2_FXP_STATUS %d", status); 475 476 if (status == SSH2_FX_EOF) { 477 break; 478 } else { 479 error("Couldn't read directory: %s", 480 fx2txt(status)); 481 do_close(conn, handle, handle_len); 482 xfree(handle); 483 return(status); 484 } 485 } else if (type != SSH2_FXP_NAME) 486 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 487 SSH2_FXP_NAME, type); 488 489 count = buffer_get_int(&msg); 490 if (count == 0) 491 break; 492 debug3("Received %d SSH2_FXP_NAME responses", count); 493 for (i = 0; i < count; i++) { 494 char *filename, *longname; 495 Attrib *a; 496 497 filename = buffer_get_string(&msg, NULL); 498 longname = buffer_get_string(&msg, NULL); 499 a = decode_attrib(&msg); 500 501 if (printflag) 502 printf("%s\n", longname); 503 504 /* 505 * Directory entries should never contain '/' 506 * These can be used to attack recursive ops 507 * (e.g. send '../../../../etc/passwd') 508 */ 509 if (strchr(filename, '/') != NULL) { 510 error("Server sent suspect path \"%s\" " 511 "during readdir of \"%s\"", filename, path); 512 goto next; 513 } 514 515 if (dir) { 516 *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); 517 (*dir)[ents] = xmalloc(sizeof(***dir)); 518 (*dir)[ents]->filename = xstrdup(filename); 519 (*dir)[ents]->longname = xstrdup(longname); 520 memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 521 (*dir)[++ents] = NULL; 522 } 523 next: 524 xfree(filename); 525 xfree(longname); 526 } 527 } 528 529 buffer_free(&msg); 530 do_close(conn, handle, handle_len); 531 xfree(handle); 532 533 /* Don't return partial matches on interrupt */ 534 if (interrupted && dir != NULL && *dir != NULL) { 535 free_sftp_dirents(*dir); 536 *dir = xmalloc(sizeof(**dir)); 537 **dir = NULL; 538 } 539 540 return(0); 541} 542 543int 544do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) 545{ 546 return(do_lsreaddir(conn, path, 0, dir)); 547} 548 549void free_sftp_dirents(SFTP_DIRENT **s) 550{ 551 int i; 552 553 for (i = 0; s[i]; i++) { 554 xfree(s[i]->filename); 555 xfree(s[i]->longname); 556 xfree(s[i]); 557 } 558 xfree(s); 559} 560 561int 562do_rm(struct sftp_conn *conn, char *path) 563{ 564 u_int status, id; 565 566 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 567 568 id = conn->msg_id++; 569 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, 570 strlen(path)); 571 status = get_status(conn->fd_in, id); 572 if (status != SSH2_FX_OK) 573 error("Couldn't delete file: %s", fx2txt(status)); 574 return(status); 575} 576 577int 578do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) 579{ 580 u_int status, id; 581 582 id = conn->msg_id++; 583 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, 584 strlen(path), a); 585 586 status = get_status(conn->fd_in, id); 587 if (status != SSH2_FX_OK && printflag) 588 error("Couldn't create directory: %s", fx2txt(status)); 589 590 return(status); 591} 592 593int 594do_rmdir(struct sftp_conn *conn, char *path) 595{ 596 u_int status, id; 597 598 id = conn->msg_id++; 599 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, 600 strlen(path)); 601 602 status = get_status(conn->fd_in, id); 603 if (status != SSH2_FX_OK) 604 error("Couldn't remove directory: %s", fx2txt(status)); 605 606 return(status); 607} 608 609Attrib * 610do_stat(struct sftp_conn *conn, char *path, int quiet) 611{ 612 u_int id; 613 614 id = conn->msg_id++; 615 616 send_string_request(conn->fd_out, id, 617 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 618 path, strlen(path)); 619 620 return(get_decode_stat(conn->fd_in, id, quiet)); 621} 622 623Attrib * 624do_lstat(struct sftp_conn *conn, char *path, int quiet) 625{ 626 u_int id; 627 628 if (conn->version == 0) { 629 if (quiet) 630 debug("Server version does not support lstat operation"); 631 else 632 logit("Server version does not support lstat operation"); 633 return(do_stat(conn, path, quiet)); 634 } 635 636 id = conn->msg_id++; 637 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, 638 strlen(path)); 639 640 return(get_decode_stat(conn->fd_in, id, quiet)); 641} 642 643#ifdef notyet 644Attrib * 645do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 646{ 647 u_int id; 648 649 id = conn->msg_id++; 650 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, 651 handle_len); 652 653 return(get_decode_stat(conn->fd_in, id, quiet)); 654} 655#endif 656 657int 658do_setstat(struct sftp_conn *conn, char *path, Attrib *a) 659{ 660 u_int status, id; 661 662 id = conn->msg_id++; 663 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, 664 strlen(path), a); 665 666 status = get_status(conn->fd_in, id); 667 if (status != SSH2_FX_OK) 668 error("Couldn't setstat on \"%s\": %s", path, 669 fx2txt(status)); 670 671 return(status); 672} 673 674int 675do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len, 676 Attrib *a) 677{ 678 u_int status, id; 679 680 id = conn->msg_id++; 681 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, 682 handle_len, a); 683 684 status = get_status(conn->fd_in, id); 685 if (status != SSH2_FX_OK) 686 error("Couldn't fsetstat: %s", fx2txt(status)); 687 688 return(status); 689} 690 691char * 692do_realpath(struct sftp_conn *conn, char *path) 693{ 694 Buffer msg; 695 u_int type, expected_id, count, id; 696 char *filename, *longname; 697 Attrib *a; 698 699 expected_id = id = conn->msg_id++; 700 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path, 701 strlen(path)); 702 703 buffer_init(&msg); 704 705 get_msg(conn->fd_in, &msg); 706 type = buffer_get_char(&msg); 707 id = buffer_get_int(&msg); 708 709 if (id != expected_id) 710 fatal("ID mismatch (%u != %u)", id, expected_id); 711 712 if (type == SSH2_FXP_STATUS) { 713 u_int status = buffer_get_int(&msg); 714 715 error("Couldn't canonicalise: %s", fx2txt(status)); 716 buffer_free(&msg); 717 return NULL; 718 } else if (type != SSH2_FXP_NAME) 719 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 720 SSH2_FXP_NAME, type); 721 722 count = buffer_get_int(&msg); 723 if (count != 1) 724 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); 725 726 filename = buffer_get_string(&msg, NULL); 727 longname = buffer_get_string(&msg, NULL); 728 a = decode_attrib(&msg); 729 730 debug3("SSH_FXP_REALPATH %s -> %s", path, filename); 731 732 xfree(longname); 733 734 buffer_free(&msg); 735 736 return(filename); 737} 738 739int 740do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) 741{ 742 Buffer msg; 743 u_int status, id; 744 745 buffer_init(&msg); 746 747 /* Send rename request */ 748 id = conn->msg_id++; 749 if ((conn->exts & SFTP_EXT_POSIX_RENAME)) { 750 buffer_put_char(&msg, SSH2_FXP_EXTENDED); 751 buffer_put_int(&msg, id); 752 buffer_put_cstring(&msg, "posix-rename@openssh.com"); 753 } else { 754 buffer_put_char(&msg, SSH2_FXP_RENAME); 755 buffer_put_int(&msg, id); 756 } 757 buffer_put_cstring(&msg, oldpath); 758 buffer_put_cstring(&msg, newpath); 759 send_msg(conn->fd_out, &msg); 760 debug3("Sent message %s \"%s\" -> \"%s\"", 761 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : 762 "SSH2_FXP_RENAME", oldpath, newpath); 763 buffer_free(&msg); 764 765 status = get_status(conn->fd_in, id); 766 if (status != SSH2_FX_OK) 767 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 768 newpath, fx2txt(status)); 769 770 return(status); 771} 772 773int 774do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) 775{ 776 Buffer msg; 777 u_int status, id; 778 779 if (conn->version < 3) { 780 error("This server does not support the symlink operation"); 781 return(SSH2_FX_OP_UNSUPPORTED); 782 } 783 784 buffer_init(&msg); 785 786 /* Send symlink request */ 787 id = conn->msg_id++; 788 buffer_put_char(&msg, SSH2_FXP_SYMLINK); 789 buffer_put_int(&msg, id); 790 buffer_put_cstring(&msg, oldpath); 791 buffer_put_cstring(&msg, newpath); 792 send_msg(conn->fd_out, &msg); 793 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 794 newpath); 795 buffer_free(&msg); 796 797 status = get_status(conn->fd_in, id); 798 if (status != SSH2_FX_OK) 799 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 800 newpath, fx2txt(status)); 801 802 return(status); 803} 804 805#ifdef notyet 806char * 807do_readlink(struct sftp_conn *conn, char *path) 808{ 809 Buffer msg; 810 u_int type, expected_id, count, id; 811 char *filename, *longname; 812 Attrib *a; 813 814 expected_id = id = conn->msg_id++; 815 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path, 816 strlen(path)); 817 818 buffer_init(&msg); 819 820 get_msg(conn->fd_in, &msg); 821 type = buffer_get_char(&msg); 822 id = buffer_get_int(&msg); 823 824 if (id != expected_id) 825 fatal("ID mismatch (%u != %u)", id, expected_id); 826 827 if (type == SSH2_FXP_STATUS) { 828 u_int status = buffer_get_int(&msg); 829 830 error("Couldn't readlink: %s", fx2txt(status)); 831 return(NULL); 832 } else if (type != SSH2_FXP_NAME) 833 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 834 SSH2_FXP_NAME, type); 835 836 count = buffer_get_int(&msg); 837 if (count != 1) 838 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 839 840 filename = buffer_get_string(&msg, NULL); 841 longname = buffer_get_string(&msg, NULL); 842 a = decode_attrib(&msg); 843 844 debug3("SSH_FXP_READLINK %s -> %s", path, filename); 845 846 xfree(longname); 847 848 buffer_free(&msg); 849 850 return(filename); 851} 852#endif 853 854int 855do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, 856 int quiet) 857{ 858 Buffer msg; 859 u_int id; 860 861 if ((conn->exts & SFTP_EXT_STATVFS) == 0) { 862 error("Server does not support statvfs@openssh.com extension"); 863 return -1; 864 } 865 866 id = conn->msg_id++; 867 868 buffer_init(&msg); 869 buffer_clear(&msg); 870 buffer_put_char(&msg, SSH2_FXP_EXTENDED); 871 buffer_put_int(&msg, id); 872 buffer_put_cstring(&msg, "statvfs@openssh.com"); 873 buffer_put_cstring(&msg, path); 874 send_msg(conn->fd_out, &msg); 875 buffer_free(&msg); 876 877 return get_decode_statvfs(conn->fd_in, st, id, quiet); 878} 879 880#ifdef notyet 881int 882do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len, 883 struct sftp_statvfs *st, int quiet) 884{ 885 Buffer msg; 886 u_int id; 887 888 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { 889 error("Server does not support fstatvfs@openssh.com extension"); 890 return -1; 891 } 892 893 id = conn->msg_id++; 894 895 buffer_init(&msg); 896 buffer_clear(&msg); 897 buffer_put_char(&msg, SSH2_FXP_EXTENDED); 898 buffer_put_int(&msg, id); 899 buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 900 buffer_put_string(&msg, handle, handle_len); 901 send_msg(conn->fd_out, &msg); 902 buffer_free(&msg); 903 904 return get_decode_statvfs(conn->fd_in, st, id, quiet); 905} 906#endif 907 908static void 909send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, 910 char *handle, u_int handle_len) 911{ 912 Buffer msg; 913 914 buffer_init(&msg); 915 buffer_clear(&msg); 916 buffer_put_char(&msg, SSH2_FXP_READ); 917 buffer_put_int(&msg, id); 918 buffer_put_string(&msg, handle, handle_len); 919 buffer_put_int64(&msg, offset); 920 buffer_put_int(&msg, len); 921 send_msg(fd_out, &msg); 922 buffer_free(&msg); 923} 924 925int 926do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 927 Attrib *a, int pflag) 928{ 929 Attrib junk; 930 Buffer msg; 931 char *handle; 932 int local_fd, status = 0, write_error; 933 int read_error, write_errno; 934 u_int64_t offset, size; 935 u_int handle_len, mode, type, id, buflen, num_req, max_req; 936 off_t progress_counter; 937 struct request { 938 u_int id; 939 u_int len; 940 u_int64_t offset; 941 TAILQ_ENTRY(request) tq; 942 }; 943 TAILQ_HEAD(reqhead, request) requests; 944 struct request *req; 945 946 TAILQ_INIT(&requests); 947 948 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) 949 return -1; 950 951 /* Do not preserve set[ug]id here, as we do not preserve ownership */ 952 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 953 mode = a->perm & 0777; 954 else 955 mode = 0666; 956 957 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 958 (!S_ISREG(a->perm))) { 959 error("Cannot download non-regular file: %s", remote_path); 960 return(-1); 961 } 962 963 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 964 size = a->size; 965 else 966 size = 0; 967 968 buflen = conn->transfer_buflen; 969 buffer_init(&msg); 970 971 /* Send open request */ 972 id = conn->msg_id++; 973 buffer_put_char(&msg, SSH2_FXP_OPEN); 974 buffer_put_int(&msg, id); 975 buffer_put_cstring(&msg, remote_path); 976 buffer_put_int(&msg, SSH2_FXF_READ); 977 attrib_clear(&junk); /* Send empty attributes */ 978 encode_attrib(&msg, &junk); 979 send_msg(conn->fd_out, &msg); 980 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 981 982 handle = get_handle(conn->fd_in, id, &handle_len, 983 "remote open(\"%s\")", remote_path); 984 if (handle == NULL) { 985 buffer_free(&msg); 986 return(-1); 987 } 988 989 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, 990 mode | S_IWRITE); 991 if (local_fd == -1) { 992 error("Couldn't open local file \"%s\" for writing: %s", 993 local_path, strerror(errno)); 994 do_close(conn, handle, handle_len); 995 buffer_free(&msg); 996 xfree(handle); 997 return(-1); 998 } 999 1000 /* Read from remote and write to local */ 1001 write_error = read_error = write_errno = num_req = offset = 0; 1002 max_req = 1; 1003 progress_counter = 0; 1004 1005 if (showprogress && size != 0) 1006 start_progress_meter(remote_path, size, &progress_counter); 1007 1008 while (num_req > 0 || max_req > 0) { 1009 char *data; 1010 u_int len; 1011 1012 /* 1013 * Simulate EOF on interrupt: stop sending new requests and 1014 * allow outstanding requests to drain gracefully 1015 */ 1016 if (interrupted) { 1017 if (num_req == 0) /* If we haven't started yet... */ 1018 break; 1019 max_req = 0; 1020 } 1021 1022 /* Send some more requests */ 1023 while (num_req < max_req) { 1024 debug3("Request range %llu -> %llu (%d/%d)", 1025 (unsigned long long)offset, 1026 (unsigned long long)offset + buflen - 1, 1027 num_req, max_req); 1028 req = xmalloc(sizeof(*req)); 1029 req->id = conn->msg_id++; 1030 req->len = buflen; 1031 req->offset = offset; 1032 offset += buflen; 1033 num_req++; 1034 TAILQ_INSERT_TAIL(&requests, req, tq); 1035 send_read_request(conn->fd_out, req->id, req->offset, 1036 req->len, handle, handle_len); 1037 } 1038 1039 buffer_clear(&msg); 1040 get_msg(conn->fd_in, &msg); 1041 type = buffer_get_char(&msg); 1042 id = buffer_get_int(&msg); 1043 debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 1044 1045 /* Find the request in our queue */ 1046 for (req = TAILQ_FIRST(&requests); 1047 req != NULL && req->id != id; 1048 req = TAILQ_NEXT(req, tq)) 1049 ; 1050 if (req == NULL) 1051 fatal("Unexpected reply %u", id); 1052 1053 switch (type) { 1054 case SSH2_FXP_STATUS: 1055 status = buffer_get_int(&msg); 1056 if (status != SSH2_FX_EOF) 1057 read_error = 1; 1058 max_req = 0; 1059 TAILQ_REMOVE(&requests, req, tq); 1060 xfree(req); 1061 num_req--; 1062 break; 1063 case SSH2_FXP_DATA: 1064 data = buffer_get_string(&msg, &len); 1065 debug3("Received data %llu -> %llu", 1066 (unsigned long long)req->offset, 1067 (unsigned long long)req->offset + len - 1); 1068 if (len > req->len) 1069 fatal("Received more data than asked for " 1070 "%u > %u", len, req->len); 1071 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || 1072 atomicio(vwrite, local_fd, data, len) != len) && 1073 !write_error) { 1074 write_errno = errno; 1075 write_error = 1; 1076 max_req = 0; 1077 } 1078 progress_counter += len; 1079 xfree(data); 1080 1081 if (len == req->len) { 1082 TAILQ_REMOVE(&requests, req, tq); 1083 xfree(req); 1084 num_req--; 1085 } else { 1086 /* Resend the request for the missing data */ 1087 debug3("Short data block, re-requesting " 1088 "%llu -> %llu (%2d)", 1089 (unsigned long long)req->offset + len, 1090 (unsigned long long)req->offset + 1091 req->len - 1, num_req); 1092 req->id = conn->msg_id++; 1093 req->len -= len; 1094 req->offset += len; 1095 send_read_request(conn->fd_out, req->id, 1096 req->offset, req->len, handle, handle_len); 1097 /* Reduce the request size */ 1098 if (len < buflen) 1099 buflen = MAX(MIN_READ_SIZE, len); 1100 } 1101 if (max_req > 0) { /* max_req = 0 iff EOF received */ 1102 if (size > 0 && offset > size) { 1103 /* Only one request at a time 1104 * after the expected EOF */ 1105 debug3("Finish at %llu (%2d)", 1106 (unsigned long long)offset, 1107 num_req); 1108 max_req = 1; 1109 } else if (max_req <= conn->num_requests) { 1110 ++max_req; 1111 } 1112 } 1113 break; 1114 default: 1115 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 1116 SSH2_FXP_DATA, type); 1117 } 1118 } 1119 1120 if (showprogress && size) 1121 stop_progress_meter(); 1122 1123 /* Sanity check */ 1124 if (TAILQ_FIRST(&requests) != NULL) 1125 fatal("Transfer complete, but requests still in queue"); 1126 1127 if (read_error) { 1128 error("Couldn't read from remote file \"%s\" : %s", 1129 remote_path, fx2txt(status)); 1130 do_close(conn, handle, handle_len); 1131 } else if (write_error) { 1132 error("Couldn't write to \"%s\": %s", local_path, 1133 strerror(write_errno)); 1134 status = -1; 1135 do_close(conn, handle, handle_len); 1136 } else { 1137 status = do_close(conn, handle, handle_len); 1138 1139 /* Override umask and utimes if asked */ 1140#ifdef HAVE_FCHMOD 1141 if (pflag && fchmod(local_fd, mode) == -1) 1142#else 1143 if (pflag && chmod(local_path, mode) == -1) 1144#endif /* HAVE_FCHMOD */ 1145 error("Couldn't set mode on \"%s\": %s", local_path, 1146 strerror(errno)); 1147 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 1148 struct timeval tv[2]; 1149 tv[0].tv_sec = a->atime; 1150 tv[1].tv_sec = a->mtime; 1151 tv[0].tv_usec = tv[1].tv_usec = 0; 1152 if (utimes(local_path, tv) == -1) 1153 error("Can't set times on \"%s\": %s", 1154 local_path, strerror(errno)); 1155 } 1156 } 1157 close(local_fd); 1158 buffer_free(&msg); 1159 xfree(handle); 1160 1161 return(status); 1162} 1163 1164static int 1165download_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1166 Attrib *dirattrib, int pflag, int printflag, int depth) 1167{ 1168 int i, ret = 0; 1169 SFTP_DIRENT **dir_entries; 1170 char *filename, *new_src, *new_dst; 1171 mode_t mode = 0777; 1172 1173 if (depth >= MAX_DIR_DEPTH) { 1174 error("Maximum directory depth exceeded: %d levels", depth); 1175 return -1; 1176 } 1177 1178 if (dirattrib == NULL && 1179 (dirattrib = do_stat(conn, src, 1)) == NULL) { 1180 error("Unable to stat remote directory \"%s\"", src); 1181 return -1; 1182 } 1183 if (!S_ISDIR(dirattrib->perm)) { 1184 error("\"%s\" is not a directory", src); 1185 return -1; 1186 } 1187 if (printflag) 1188 printf("Retrieving %s\n", src); 1189 1190 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1191 mode = dirattrib->perm & 01777; 1192 else { 1193 debug("Server did not send permissions for " 1194 "directory \"%s\"", dst); 1195 } 1196 1197 if (mkdir(dst, mode) == -1 && errno != EEXIST) { 1198 error("mkdir %s: %s", dst, strerror(errno)); 1199 return -1; 1200 } 1201 1202 if (do_readdir(conn, src, &dir_entries) == -1) { 1203 error("%s: Failed to get directory contents", src); 1204 return -1; 1205 } 1206 1207 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { 1208 filename = dir_entries[i]->filename; 1209 1210 new_dst = path_append(dst, filename); 1211 new_src = path_append(src, filename); 1212 1213 if (S_ISDIR(dir_entries[i]->a.perm)) { 1214 if (strcmp(filename, ".") == 0 || 1215 strcmp(filename, "..") == 0) 1216 continue; 1217 if (download_dir_internal(conn, new_src, new_dst, 1218 &(dir_entries[i]->a), pflag, printflag, 1219 depth + 1) == -1) 1220 ret = -1; 1221 } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1222 if (do_download(conn, new_src, new_dst, 1223 &(dir_entries[i]->a), pflag) == -1) { 1224 error("Download of file %s to %s failed", 1225 new_src, new_dst); 1226 ret = -1; 1227 } 1228 } else 1229 logit("%s: not a regular file\n", new_src); 1230 1231 xfree(new_dst); 1232 xfree(new_src); 1233 } 1234 1235 if (pflag) { 1236 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1237 struct timeval tv[2]; 1238 tv[0].tv_sec = dirattrib->atime; 1239 tv[1].tv_sec = dirattrib->mtime; 1240 tv[0].tv_usec = tv[1].tv_usec = 0; 1241 if (utimes(dst, tv) == -1) 1242 error("Can't set times on \"%s\": %s", 1243 dst, strerror(errno)); 1244 } else 1245 debug("Server did not send times for directory " 1246 "\"%s\"", dst); 1247 } 1248 1249 free_sftp_dirents(dir_entries); 1250 1251 return ret; 1252} 1253 1254int 1255download_dir(struct sftp_conn *conn, char *src, char *dst, 1256 Attrib *dirattrib, int pflag, int printflag) 1257{ 1258 char *src_canon; 1259 int ret; 1260 1261 if ((src_canon = do_realpath(conn, src)) == NULL) { 1262 error("Unable to canonicalise path \"%s\"", src); 1263 return -1; 1264 } 1265 1266 ret = download_dir_internal(conn, src_canon, dst, 1267 dirattrib, pflag, printflag, 0); 1268 xfree(src_canon); 1269 return ret; 1270} 1271 1272int 1273do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1274 int pflag) 1275{ 1276 int local_fd; 1277 int status = SSH2_FX_OK; 1278 u_int handle_len, id, type; 1279 off_t offset; 1280 char *handle, *data; 1281 Buffer msg; 1282 struct stat sb; 1283 Attrib a; 1284 u_int32_t startid; 1285 u_int32_t ackid; 1286 struct outstanding_ack { 1287 u_int id; 1288 u_int len; 1289 off_t offset; 1290 TAILQ_ENTRY(outstanding_ack) tq; 1291 }; 1292 TAILQ_HEAD(ackhead, outstanding_ack) acks; 1293 struct outstanding_ack *ack = NULL; 1294 1295 TAILQ_INIT(&acks); 1296 1297 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 1298 error("Couldn't open local file \"%s\" for reading: %s", 1299 local_path, strerror(errno)); 1300 return(-1); 1301 } 1302 if (fstat(local_fd, &sb) == -1) { 1303 error("Couldn't fstat local file \"%s\": %s", 1304 local_path, strerror(errno)); 1305 close(local_fd); 1306 return(-1); 1307 } 1308 if (!S_ISREG(sb.st_mode)) { 1309 error("%s is not a regular file", local_path); 1310 close(local_fd); 1311 return(-1); 1312 } 1313 stat_to_attrib(&sb, &a); 1314 1315 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1316 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1317 a.perm &= 0777; 1318 if (!pflag) 1319 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1320 1321 buffer_init(&msg); 1322 1323 /* Send open request */ 1324 id = conn->msg_id++; 1325 buffer_put_char(&msg, SSH2_FXP_OPEN); 1326 buffer_put_int(&msg, id); 1327 buffer_put_cstring(&msg, remote_path); 1328 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 1329 encode_attrib(&msg, &a); 1330 send_msg(conn->fd_out, &msg); 1331 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 1332 1333 buffer_clear(&msg); 1334 1335 handle = get_handle(conn->fd_in, id, &handle_len, 1336 "remote open(\"%s\")", remote_path); 1337 if (handle == NULL) { 1338 close(local_fd); 1339 buffer_free(&msg); 1340 return -1; 1341 } 1342 1343 startid = ackid = id + 1; 1344 data = xmalloc(conn->transfer_buflen); 1345 1346 /* Read from local and write to remote */ 1347 offset = 0; 1348 if (showprogress) 1349 start_progress_meter(local_path, sb.st_size, &offset); 1350 1351 for (;;) { 1352 int len; 1353 1354 /* 1355 * Can't use atomicio here because it returns 0 on EOF, 1356 * thus losing the last block of the file. 1357 * Simulate an EOF on interrupt, allowing ACKs from the 1358 * server to drain. 1359 */ 1360 if (interrupted || status != SSH2_FX_OK) 1361 len = 0; 1362 else do 1363 len = read(local_fd, data, conn->transfer_buflen); 1364 while ((len == -1) && 1365 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); 1366 1367 if (len == -1) 1368 fatal("Couldn't read from \"%s\": %s", local_path, 1369 strerror(errno)); 1370 1371 if (len != 0) { 1372 ack = xmalloc(sizeof(*ack)); 1373 ack->id = ++id; 1374 ack->offset = offset; 1375 ack->len = len; 1376 TAILQ_INSERT_TAIL(&acks, ack, tq); 1377 1378 buffer_clear(&msg); 1379 buffer_put_char(&msg, SSH2_FXP_WRITE); 1380 buffer_put_int(&msg, ack->id); 1381 buffer_put_string(&msg, handle, handle_len); 1382 buffer_put_int64(&msg, offset); 1383 buffer_put_string(&msg, data, len); 1384 send_msg(conn->fd_out, &msg); 1385 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 1386 id, (unsigned long long)offset, len); 1387 } else if (TAILQ_FIRST(&acks) == NULL) 1388 break; 1389 1390 if (ack == NULL) 1391 fatal("Unexpected ACK %u", id); 1392 1393 if (id == startid || len == 0 || 1394 id - ackid >= conn->num_requests) { 1395 u_int r_id; 1396 1397 buffer_clear(&msg); 1398 get_msg(conn->fd_in, &msg); 1399 type = buffer_get_char(&msg); 1400 r_id = buffer_get_int(&msg); 1401 1402 if (type != SSH2_FXP_STATUS) 1403 fatal("Expected SSH2_FXP_STATUS(%d) packet, " 1404 "got %d", SSH2_FXP_STATUS, type); 1405 1406 status = buffer_get_int(&msg); 1407 debug3("SSH2_FXP_STATUS %d", status); 1408 1409 /* Find the request in our queue */ 1410 for (ack = TAILQ_FIRST(&acks); 1411 ack != NULL && ack->id != r_id; 1412 ack = TAILQ_NEXT(ack, tq)) 1413 ; 1414 if (ack == NULL) 1415 fatal("Can't find request for ID %u", r_id); 1416 TAILQ_REMOVE(&acks, ack, tq); 1417 debug3("In write loop, ack for %u %u bytes at %lld", 1418 ack->id, ack->len, (long long)ack->offset); 1419 ++ackid; 1420 xfree(ack); 1421 } 1422 offset += len; 1423 if (offset < 0) 1424 fatal("%s: offset < 0", __func__); 1425 } 1426 buffer_free(&msg); 1427 1428 if (showprogress) 1429 stop_progress_meter(); 1430 xfree(data); 1431 1432 if (status != SSH2_FX_OK) { 1433 error("Couldn't write to remote file \"%s\": %s", 1434 remote_path, fx2txt(status)); 1435 status = -1; 1436 } 1437 1438 if (close(local_fd) == -1) { 1439 error("Couldn't close local file \"%s\": %s", local_path, 1440 strerror(errno)); 1441 status = -1; 1442 } 1443 1444 /* Override umask and utimes if asked */ 1445 if (pflag) 1446 do_fsetstat(conn, handle, handle_len, &a); 1447 1448 if (do_close(conn, handle, handle_len) != SSH2_FX_OK) 1449 status = -1; 1450 xfree(handle); 1451 1452 return status; 1453} 1454 1455static int 1456upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, 1457 int pflag, int printflag, int depth) 1458{ 1459 int ret = 0, status; 1460 DIR *dirp; 1461 struct dirent *dp; 1462 char *filename, *new_src, *new_dst; 1463 struct stat sb; 1464 Attrib a; 1465 1466 if (depth >= MAX_DIR_DEPTH) { 1467 error("Maximum directory depth exceeded: %d levels", depth); 1468 return -1; 1469 } 1470 1471 if (stat(src, &sb) == -1) { 1472 error("Couldn't stat directory \"%s\": %s", 1473 src, strerror(errno)); 1474 return -1; 1475 } 1476 if (!S_ISDIR(sb.st_mode)) { 1477 error("\"%s\" is not a directory", src); 1478 return -1; 1479 } 1480 if (printflag) 1481 printf("Entering %s\n", src); 1482 1483 attrib_clear(&a); 1484 stat_to_attrib(&sb, &a); 1485 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1486 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1487 a.perm &= 01777; 1488 if (!pflag) 1489 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1490 1491 status = do_mkdir(conn, dst, &a, 0); 1492 /* 1493 * we lack a portable status for errno EEXIST, 1494 * so if we get a SSH2_FX_FAILURE back we must check 1495 * if it was created successfully. 1496 */ 1497 if (status != SSH2_FX_OK) { 1498 if (status != SSH2_FX_FAILURE) 1499 return -1; 1500 if (do_stat(conn, dst, 0) == NULL) 1501 return -1; 1502 } 1503 1504 if ((dirp = opendir(src)) == NULL) { 1505 error("Failed to open dir \"%s\": %s", src, strerror(errno)); 1506 return -1; 1507 } 1508 1509 while (((dp = readdir(dirp)) != NULL) && !interrupted) { 1510 if (dp->d_ino == 0) 1511 continue; 1512 filename = dp->d_name; 1513 new_dst = path_append(dst, filename); 1514 new_src = path_append(src, filename); 1515 1516 if (lstat(new_src, &sb) == -1) { 1517 logit("%s: lstat failed: %s", filename, 1518 strerror(errno)); 1519 ret = -1; 1520 } else if (S_ISDIR(sb.st_mode)) { 1521 if (strcmp(filename, ".") == 0 || 1522 strcmp(filename, "..") == 0) 1523 continue; 1524 1525 if (upload_dir_internal(conn, new_src, new_dst, 1526 pflag, printflag, depth + 1) == -1) 1527 ret = -1; 1528 } else if (S_ISREG(sb.st_mode)) { 1529 if (do_upload(conn, new_src, new_dst, pflag) == -1) { 1530 error("Uploading of file %s to %s failed!", 1531 new_src, new_dst); 1532 ret = -1; 1533 } 1534 } else 1535 logit("%s: not a regular file\n", filename); 1536 xfree(new_dst); 1537 xfree(new_src); 1538 } 1539 1540 do_setstat(conn, dst, &a); 1541 1542 (void) closedir(dirp); 1543 return ret; 1544} 1545 1546int 1547upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, 1548 int pflag) 1549{ 1550 char *dst_canon; 1551 int ret; 1552 1553 if ((dst_canon = do_realpath(conn, dst)) == NULL) { 1554 error("Unable to canonicalise path \"%s\"", dst); 1555 return -1; 1556 } 1557 1558 ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); 1559 xfree(dst_canon); 1560 return ret; 1561} 1562 1563char * 1564path_append(char *p1, char *p2) 1565{ 1566 char *ret; 1567 size_t len = strlen(p1) + strlen(p2) + 2; 1568 1569 ret = xmalloc(len); 1570 strlcpy(ret, p1, len); 1571 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 1572 strlcat(ret, "/", len); 1573 strlcat(ret, p2, len); 1574 1575 return(ret); 1576} 1577 1578