0b627d92c4
Existing code has transport memory leaks. Use smart pointers for transport to get rid of those cases and manual memory management Test: atest fastboot_test Test: manually checked transport isn't leaking anymore Bug: 296629925 Change-Id: Ifdf162d5084f61ae5c1d2b56a897464af58100da Signed-off-by: Dmitrii Merkurev <dimorinny@google.com>
635 lines
22 KiB
C++
635 lines
22 KiB
C++
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fastboot_driver.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include <regex>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/mapped_file.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <android-base/unique_fd.h>
|
|
#include <storage_literals/storage_literals.h>
|
|
|
|
#include "constants.h"
|
|
#include "transport.h"
|
|
|
|
using android::base::StringPrintf;
|
|
using namespace android::storage_literals;
|
|
|
|
namespace fastboot {
|
|
|
|
/*************************** PUBLIC *******************************/
|
|
FastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,
|
|
DriverCallbacks driver_callbacks,
|
|
bool no_checks)
|
|
: transport_(std::move(transport)),
|
|
prolog_(std::move(driver_callbacks.prolog)),
|
|
epilog_(std::move(driver_callbacks.epilog)),
|
|
info_(std::move(driver_callbacks.info)),
|
|
text_(std::move(driver_callbacks.text)),
|
|
disable_checks_(no_checks) {}
|
|
|
|
FastBootDriver::~FastBootDriver() {
|
|
}
|
|
|
|
RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_BOOT, "Booting", response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_CONTINUE, "Resuming boot", response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {
|
|
return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
|
|
"Creating '" + partition + "'");
|
|
}
|
|
|
|
RetCode FastBootDriver::DeletePartition(const std::string& partition) {
|
|
return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
|
|
}
|
|
|
|
RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
|
|
std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
|
|
std::string tmp;
|
|
return GetVar("all", &tmp, response);
|
|
}
|
|
|
|
RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
|
|
return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
|
|
"Resizing '" + partition + "'");
|
|
}
|
|
|
|
RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
|
|
response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
prolog_(StringPrintf("Snapshot %s", command.c_str()));
|
|
std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
|
|
auto result = RawCommand(raw, response, info);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
RetCode FastBootDriver::FlashPartition(const std::string& partition,
|
|
const std::vector<char>& data) {
|
|
RetCode ret;
|
|
if ((ret = Download(partition, data))) {
|
|
return ret;
|
|
}
|
|
return Flash(partition);
|
|
}
|
|
|
|
RetCode FastBootDriver::FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
|
|
uint32_t size) {
|
|
RetCode ret;
|
|
if ((ret = Download(partition, fd, size))) {
|
|
return ret;
|
|
}
|
|
return Flash(partition);
|
|
}
|
|
|
|
RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
|
|
size_t current, size_t total) {
|
|
RetCode ret;
|
|
if ((ret = Download(partition, s, size, current, total, false))) {
|
|
return ret;
|
|
}
|
|
return Flash(partition);
|
|
}
|
|
|
|
RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
|
|
std::vector<std::string> all;
|
|
RetCode ret;
|
|
if ((ret = GetVarAll(&all))) {
|
|
return ret;
|
|
}
|
|
|
|
std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)");
|
|
std::smatch sm;
|
|
|
|
for (auto& s : all) {
|
|
if (std::regex_match(s, sm, reg)) {
|
|
std::string m1(sm[1]);
|
|
std::string m2(sm[2]);
|
|
uint64_t tmp = strtoll(m2.c_str(), 0, 16);
|
|
partitions->push_back(std::make_tuple(m1, tmp));
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(const std::string& name, android::base::borrowed_fd fd,
|
|
size_t size, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
|
|
auto result = Download(fd, size, response, info);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(android::base::borrowed_fd fd, size_t size, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
RetCode ret;
|
|
|
|
if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
|
|
error_ = "File is too large to download";
|
|
return BAD_ARG;
|
|
}
|
|
|
|
uint32_t u32size = static_cast<uint32_t>(size);
|
|
if ((ret = DownloadCommand(u32size, response, info))) {
|
|
return ret;
|
|
}
|
|
|
|
// Write the buffer
|
|
if ((ret = SendBuffer(fd, size))) {
|
|
return ret;
|
|
}
|
|
|
|
// Wait for response
|
|
return HandleResponse(response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,
|
|
std::string* response, std::vector<std::string>* info) {
|
|
prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
|
|
auto result = Download(buf, response, info);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
RetCode ret;
|
|
error_ = "";
|
|
if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
|
|
error_ = "Buffer is too large or 0 bytes";
|
|
return BAD_ARG;
|
|
}
|
|
|
|
if ((ret = DownloadCommand(buf.size(), response, info))) {
|
|
return ret;
|
|
}
|
|
|
|
// Write the buffer
|
|
if ((ret = SendBuffer(buf))) {
|
|
return ret;
|
|
}
|
|
|
|
// Wait for response
|
|
return HandleResponse(response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
|
|
size_t current, size_t total, bool use_crc, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
|
|
size / 1024));
|
|
auto result = Download(s, use_crc, response, info);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
error_ = "";
|
|
int64_t size = sparse_file_len(s, true, use_crc);
|
|
if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
|
|
error_ = "Sparse file is too large or invalid";
|
|
return BAD_ARG;
|
|
}
|
|
|
|
RetCode ret;
|
|
uint32_t u32size = static_cast<uint32_t>(size);
|
|
if ((ret = DownloadCommand(u32size, response, info))) {
|
|
return ret;
|
|
}
|
|
|
|
struct SparseCBPrivate {
|
|
FastBootDriver* self;
|
|
std::vector<char> tpbuf;
|
|
} cb_priv;
|
|
cb_priv.self = this;
|
|
|
|
auto cb = [](void* priv, const void* buf, size_t len) -> int {
|
|
SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
|
|
const char* cbuf = static_cast<const char*>(buf);
|
|
return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
|
|
};
|
|
|
|
if (sparse_file_callback(s, true, use_crc, cb, &cb_priv) < 0) {
|
|
error_ = "Error reading sparse file";
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// Now flush
|
|
if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
|
|
return ret;
|
|
}
|
|
|
|
return HandleResponse(response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
prolog_("Uploading '" + outfile + "'");
|
|
auto result = UploadInner(outfile, response, info);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
// This function executes cmd, then expect a "DATA" response with a number N, followed
|
|
// by N bytes, and another response.
|
|
// This is the common way for the device to send data to the driver used by upload and fetch.
|
|
RetCode FastBootDriver::RunAndReadBuffer(
|
|
const std::string& cmd, std::string* response, std::vector<std::string>* info,
|
|
const std::function<RetCode(const char* data, uint64_t size)>& write_fn) {
|
|
RetCode ret;
|
|
int dsize = 0;
|
|
if ((ret = RawCommand(cmd, response, info, &dsize))) {
|
|
error_ = android::base::StringPrintf("%s request failed: %s", cmd.c_str(), error_.c_str());
|
|
return ret;
|
|
}
|
|
|
|
if (dsize <= 0) {
|
|
error_ = android::base::StringPrintf("%s request failed, device reports %d bytes available",
|
|
cmd.c_str(), dsize);
|
|
return BAD_DEV_RESP;
|
|
}
|
|
|
|
const uint64_t total_size = dsize;
|
|
const uint64_t buf_size = std::min<uint64_t>(total_size, 1_MiB);
|
|
std::vector<char> data(buf_size);
|
|
uint64_t current_offset = 0;
|
|
while (current_offset < total_size) {
|
|
uint64_t remaining = total_size - current_offset;
|
|
uint64_t chunk_size = std::min(buf_size, remaining);
|
|
if ((ret = ReadBuffer(data.data(), chunk_size)) != SUCCESS) {
|
|
return ret;
|
|
}
|
|
if ((ret = write_fn(data.data(), chunk_size)) != SUCCESS) {
|
|
return ret;
|
|
}
|
|
current_offset += chunk_size;
|
|
}
|
|
return HandleResponse(response, info);
|
|
}
|
|
|
|
RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
std::ofstream ofs;
|
|
ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
|
|
if (ofs.fail()) {
|
|
error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
|
|
return IO_ERROR;
|
|
}
|
|
auto write_fn = [&](const char* data, uint64_t size) {
|
|
ofs.write(data, size);
|
|
if (ofs.fail() || ofs.bad()) {
|
|
error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
|
|
return IO_ERROR;
|
|
}
|
|
return SUCCESS;
|
|
};
|
|
RetCode ret = RunAndReadBuffer(FB_CMD_UPLOAD, response, info, write_fn);
|
|
ofs.close();
|
|
return ret;
|
|
}
|
|
|
|
RetCode FastBootDriver::FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
|
|
int64_t offset, int64_t size, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
prolog_(android::base::StringPrintf("Fetching %s (offset=%" PRIx64 ", size=%" PRIx64 ")",
|
|
partition.c_str(), offset, size));
|
|
std::string cmd = FB_CMD_FETCH ":" + partition;
|
|
if (offset >= 0) {
|
|
cmd += android::base::StringPrintf(":0x%08" PRIx64, offset);
|
|
if (size >= 0) {
|
|
cmd += android::base::StringPrintf(":0x%08" PRIx64, size);
|
|
}
|
|
}
|
|
RetCode ret = RunAndReadBuffer(cmd, response, info, [&](const char* data, uint64_t size) {
|
|
if (!android::base::WriteFully(fd, data, size)) {
|
|
error_ = android::base::StringPrintf("Cannot write: %s", strerror(errno));
|
|
return IO_ERROR;
|
|
}
|
|
return SUCCESS;
|
|
});
|
|
epilog_(ret);
|
|
return ret;
|
|
}
|
|
|
|
// Helpers
|
|
void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
|
|
info_ = info;
|
|
}
|
|
|
|
const std::string FastBootDriver::RCString(RetCode rc) {
|
|
switch (rc) {
|
|
case SUCCESS:
|
|
return std::string("Success");
|
|
|
|
case BAD_ARG:
|
|
return std::string("Invalid Argument");
|
|
|
|
case IO_ERROR:
|
|
return std::string("I/O Error");
|
|
|
|
case BAD_DEV_RESP:
|
|
return std::string("Invalid Device Response");
|
|
|
|
case DEVICE_FAIL:
|
|
return std::string("Device Error");
|
|
|
|
case TIMEOUT:
|
|
return std::string("Timeout");
|
|
|
|
default:
|
|
return std::string("Unknown Error");
|
|
}
|
|
}
|
|
|
|
std::string FastBootDriver::Error() {
|
|
return error_;
|
|
}
|
|
|
|
RetCode FastBootDriver::WaitForDisconnect() {
|
|
return transport_->WaitForDisconnect() ? IO_ERROR : SUCCESS;
|
|
}
|
|
|
|
/****************************** PROTECTED *************************************/
|
|
RetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,
|
|
std::string* response, std::vector<std::string>* info,
|
|
int* dsize) {
|
|
prolog_(message);
|
|
auto result = RawCommand(cmd, response, info, dsize);
|
|
epilog_(result);
|
|
return result;
|
|
}
|
|
|
|
RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
|
|
std::vector<std::string>* info, int* dsize) {
|
|
error_ = ""; // Clear any pending error
|
|
if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
|
|
error_ = "Command length to RawCommand() is too long";
|
|
return BAD_ARG;
|
|
}
|
|
|
|
if (transport_->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
|
|
error_ = ErrnoStr("Write to device failed");
|
|
return IO_ERROR;
|
|
}
|
|
|
|
// Read the response
|
|
return HandleResponse(response, info, dsize);
|
|
}
|
|
|
|
RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
|
|
std::vector<std::string>* info) {
|
|
std::string cmd(android::base::StringPrintf("%s:%08" PRIx32, FB_CMD_DOWNLOAD, size));
|
|
RetCode ret;
|
|
if ((ret = RawCommand(cmd, response, info))) {
|
|
return ret;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
|
|
int* dsize) {
|
|
char status[FB_RESPONSE_SZ + 1];
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
auto set_response = [response](std::string s) {
|
|
if (response) *response = std::move(s);
|
|
};
|
|
auto add_info = [info](std::string s) {
|
|
if (info) info->push_back(std::move(s));
|
|
};
|
|
|
|
// erase response
|
|
set_response("");
|
|
while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
|
|
int r = transport_->Read(status, FB_RESPONSE_SZ);
|
|
if (r < 0) {
|
|
error_ = ErrnoStr("Status read failed");
|
|
return IO_ERROR;
|
|
}
|
|
|
|
status[r] = '\0'; // Need the null terminator
|
|
std::string input(status);
|
|
if (android::base::StartsWith(input, "INFO")) {
|
|
std::string tmp = input.substr(strlen("INFO"));
|
|
info_(tmp);
|
|
add_info(std::move(tmp));
|
|
// We may receive one or more INFO packets during long operations,
|
|
// e.g. flash/erase if they are back by slow media like NAND/NOR
|
|
// flash. In that case, reset the timer since it's not a real
|
|
// timeout.
|
|
start = std::chrono::steady_clock::now();
|
|
} else if (android::base::StartsWith(input, "OKAY")) {
|
|
set_response(input.substr(strlen("OKAY")));
|
|
return SUCCESS;
|
|
} else if (android::base::StartsWith(input, "FAIL")) {
|
|
error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
|
|
set_response(input.substr(strlen("FAIL")));
|
|
return DEVICE_FAIL;
|
|
} else if (android::base::StartsWith(input, "TEXT")) {
|
|
text_(input.substr(strlen("TEXT")));
|
|
// Reset timeout as many more TEXT may come
|
|
start = std::chrono::steady_clock::now();
|
|
} else if (android::base::StartsWith(input, "DATA")) {
|
|
std::string tmp = input.substr(strlen("DATA"));
|
|
uint32_t num = strtol(tmp.c_str(), 0, 16);
|
|
if (num > MAX_DOWNLOAD_SIZE) {
|
|
error_ = android::base::StringPrintf("Data size too large (%d)", num);
|
|
return BAD_DEV_RESP;
|
|
}
|
|
if (dsize) *dsize = num;
|
|
set_response(std::move(tmp));
|
|
return SUCCESS;
|
|
} else {
|
|
error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
|
|
return BAD_DEV_RESP;
|
|
}
|
|
|
|
} // End of while loop
|
|
|
|
return TIMEOUT;
|
|
}
|
|
|
|
std::string FastBootDriver::ErrnoStr(const std::string& msg) {
|
|
return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
|
|
}
|
|
|
|
/******************************* PRIVATE **************************************/
|
|
RetCode FastBootDriver::SendBuffer(android::base::borrowed_fd fd, size_t size) {
|
|
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
|
|
off64_t offset = 0;
|
|
uint32_t remaining = size;
|
|
RetCode ret;
|
|
|
|
while (remaining) {
|
|
// Memory map the file
|
|
size_t len = std::min(remaining, MAX_MAP_SIZE);
|
|
auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};
|
|
if (!mapping) {
|
|
error_ = "Creating filemap failed";
|
|
return IO_ERROR;
|
|
}
|
|
|
|
if ((ret = SendBuffer(mapping->data(), mapping->size()))) {
|
|
return ret;
|
|
}
|
|
|
|
remaining -= len;
|
|
offset += len;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
|
|
// Write the buffer
|
|
return SendBuffer(buf.data(), buf.size());
|
|
}
|
|
|
|
RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
|
|
// ioctl on 0-length buffer causes freezing
|
|
if (!size) {
|
|
return BAD_ARG;
|
|
}
|
|
// Write the buffer
|
|
ssize_t tmp = transport_->Write(buf, size);
|
|
|
|
if (tmp < 0) {
|
|
error_ = ErrnoStr("Write to device failed in SendBuffer()");
|
|
return IO_ERROR;
|
|
} else if (static_cast<size_t>(tmp) != size) {
|
|
error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
|
|
|
|
return IO_ERROR;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
|
|
// Read the buffer
|
|
ssize_t tmp = transport_->Read(buf, size);
|
|
|
|
if (tmp < 0) {
|
|
error_ = ErrnoStr("Read from device failed in ReadBuffer()");
|
|
return IO_ERROR;
|
|
} else if (static_cast<size_t>(tmp) != size) {
|
|
error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
|
|
return IO_ERROR;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
|
|
size_t total = 0;
|
|
size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
|
|
|
|
// Handle the residual
|
|
tpbuf.insert(tpbuf.end(), data, data + to_write);
|
|
if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) { // Nothing enough to send rn
|
|
return 0;
|
|
}
|
|
|
|
if (SendBuffer(tpbuf)) {
|
|
error_ = ErrnoStr("Send failed in SparseWriteCallback()");
|
|
return -1;
|
|
}
|
|
tpbuf.clear();
|
|
total += to_write;
|
|
|
|
// Now we need to send a multiple of chunk size
|
|
size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
|
|
size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
|
|
if (nbytes && SendBuffer(data + total, nbytes)) { // Don't send a ZLP
|
|
error_ = ErrnoStr("Send failed in SparseWriteCallback()");
|
|
return -1;
|
|
}
|
|
total += nbytes;
|
|
|
|
if (len - total > 0) { // We have residual data to save for next time
|
|
tpbuf.assign(data + total, data + len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {
|
|
transport_ = std::move(transport);
|
|
}
|
|
|
|
} // End namespace fastboot
|