| 536 | } |
| 537 | |
| 538 | bool SFTPClient::putFile(const std::string& path, io::BaseStream& input, bool overwrite, int64_t expected_size /*= -1*/) { |
| 539 | int flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | (overwrite ? LIBSSH2_FXF_TRUNC : LIBSSH2_FXF_EXCL); |
| 540 | logger_->log_trace("Opening remote file \"%s\"", path.c_str()); |
| 541 | LIBSSH2_SFTP_HANDLE *file_handle = libssh2_sftp_open(sftp_session_, path.c_str(), flags, 0644); |
| 542 | if (file_handle == nullptr) { |
| 543 | int ssh_errno = libssh2_session_last_errno(ssh_session_); |
| 544 | /* We can only get the sftp error in this case if the ssh error is a protocol error */ |
| 545 | if (ssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL) { |
| 546 | last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_)); |
| 547 | logger_->log_error("Failed to open remote file \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_)); |
| 548 | } else { |
| 549 | last_error_.setSftpError(SFTPError::IoError); |
| 550 | char *err_msg = nullptr; |
| 551 | libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0); |
| 552 | logger_->log_error("Failed to open remote file \"%s\" due to an underlying SSH error: %s", path.c_str(), err_msg); |
| 553 | } |
| 554 | } |
| 555 | const auto guard = gsl::finally([this, &file_handle, &path]() { |
| 556 | logger_->log_trace("Closing remote file \"%s\"", path.c_str()); |
| 557 | libssh2_sftp_close(file_handle); |
| 558 | }); |
| 559 | |
| 560 | /* If they just want a zero byte file, we are done */ |
| 561 | if (expected_size == 0) { |
| 562 | return true; |
| 563 | } |
| 564 | |
| 565 | const size_t buf_size = expected_size < 0 ? MAX_BUFFER_SIZE : std::min<size_t>(expected_size, MAX_BUFFER_SIZE); |
| 566 | std::vector<uint8_t> buf(buf_size); |
| 567 | uint64_t total_read = 0U; |
| 568 | do { |
| 569 | int read_ret = input.read(buf.data(), buf.size()); |
| 570 | if (read_ret < 0) { |
| 571 | last_error_.setLibssh2Error(LIBSSH2_FX_OK); |
| 572 | logger_->log_error("Error while reading input"); |
| 573 | return false; |
| 574 | } else if (read_ret == 0) { |
| 575 | logger_->log_trace("EOF while reading input"); |
| 576 | break; |
| 577 | } |
| 578 | logger_->log_trace("Read %d bytes", read_ret); |
| 579 | total_read += read_ret; |
| 580 | ssize_t remaining = read_ret; |
| 581 | while (remaining > 0) { |
| 582 | int write_ret = libssh2_sftp_write(file_handle, reinterpret_cast<char*>(buf.data() + (read_ret - remaining)), remaining); |
| 583 | if (write_ret < 0) { |
| 584 | last_error_.setSftpError(SFTPError::IoError); |
| 585 | logger_->log_error("Failed to write remote file \"%s\"", path.c_str()); |
| 586 | return false; |
| 587 | } |
| 588 | logger_->log_trace("Wrote %d bytes to remote file \"%s\"", write_ret, path.c_str()); |
| 589 | remaining -= write_ret; |
| 590 | } |
| 591 | } while (true); |
| 592 | |
| 593 | if (expected_size >= 0 && total_read != expected_size) { |
| 594 | last_error_.setLibssh2Error(LIBSSH2_FX_OK); |
| 595 | logger_->log_error("Input has unexpected size, expected: %ld, actual: %lu", path.c_str(), expected_size, total_read); |