blob: 0abdee77ba2796cee5bb0e192dd53f3ad9012b22 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/bringup/bin/netsvc/file-api.h"
#include <errno.h>
#include <fcntl.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/netboot/netboot.h>
#include <stdio.h>
#include <string.h>
#include "board-info.h"
#include "netboot.h"
namespace netsvc {
namespace {
size_t NETBOOT_IMAGE_PREFIX_LEN() { return strlen(NETBOOT_IMAGE_PREFIX); }
size_t NETBOOT_FILENAME_PREFIX_LEN() { return strlen(NETBOOT_FILENAME_PREFIX); }
} // namespace
FileApi::FileApi(bool is_zedboot, std::unique_ptr<NetCopyInterface> netcp,
fidl::ClientEnd<fuchsia_sysinfo::SysInfo> sysinfo, PaverInterface& paver)
: is_zedboot_(is_zedboot),
sysinfo_(std::move(sysinfo)),
netcp_(std::move(netcp)),
paver_(paver) {
if (!sysinfo_) {
zx::result client_end = component::Connect<fuchsia_sysinfo::SysInfo>();
if (client_end.is_ok()) {
sysinfo_ = std::move(client_end.value());
}
}
}
ssize_t FileApi::OpenRead(const char* filename, zx::duration) {
// Make sure all in-progress paving operations have completed
std::shared_future fut = paver_.exit_code();
if (fut.wait_for(std::chrono::nanoseconds::zero()) != std::future_status::ready) {
return TFTP_ERR_SHOULD_WAIT;
}
zx_status_t exit_code = fut.get();
if (exit_code != ZX_OK) {
fprintf(stderr, "paver exited with error: %s\n", zx_status_get_string(exit_code));
return TFTP_ERR_IO;
}
is_write_ = false;
strlcpy(filename_, filename, sizeof(filename_));
netboot_file_ = nullptr;
size_t file_size;
if (is_zedboot_ && !strcmp(filename_, NETBOOT_BOARD_INFO_FILENAME)) {
type_ = NetfileType::kBoardInfo;
return BoardInfoSize();
}
type_ = NetfileType::kNetCopy;
if (netcp_->Open(filename, O_RDONLY, &file_size) == 0) {
return static_cast<ssize_t>(file_size);
}
return TFTP_ERR_NOT_FOUND;
}
tftp_status FileApi::OpenWrite(const char* filename, size_t size, zx::duration timeout) {
// Make sure all in-progress paving operations have completed
std::shared_future fut = paver_.exit_code();
if (fut.wait_for(std::chrono::nanoseconds::zero()) != std::future_status::ready) {
return TFTP_ERR_SHOULD_WAIT;
}
zx_status_t exit_code = fut.get();
if (exit_code != ZX_OK) {
fprintf(stderr, "paver exited with error: %s\n", zx_status_get_string(exit_code));
return TFTP_ERR_IO;
}
is_write_ = true;
strlcpy(filename_, filename, sizeof(filename_));
if (is_zedboot_ && !strncmp(filename_, NETBOOT_FILENAME_PREFIX, NETBOOT_FILENAME_PREFIX_LEN())) {
type_ = NetfileType::kNetboot;
netboot_file_ = netboot_get_buffer(filename_, size);
if (netboot_file_ != nullptr) {
return TFTP_NO_ERROR;
}
} else if (is_zedboot_ && !strcmp(filename_, NETBOOT_BOARD_NAME_FILENAME)) {
printf("netsvc: Running board name validation\n");
type_ = NetfileType::kBoardInfo;
return TFTP_NO_ERROR;
} else if (is_zedboot_ && !strncmp(filename_, NETBOOT_IMAGE_PREFIX, NETBOOT_IMAGE_PREFIX_LEN())) {
type_ = NetfileType::kPaver;
tftp_status status = paver_.OpenWrite(filename_, size, timeout);
if (status != TFTP_NO_ERROR) {
filename_[0] = '\0';
}
return status;
} else {
type_ = NetfileType::kNetCopy;
if (netcp_->Open(filename_, O_WRONLY, nullptr) == 0) {
return TFTP_NO_ERROR;
}
}
return TFTP_ERR_INVALID_ARGS;
}
tftp_status FileApi::Read(void* data, size_t* length, off_t offset) {
if (length == nullptr) {
return TFTP_ERR_INVALID_ARGS;
}
switch (type_) {
case NetfileType::kBoardInfo: {
zx::result<> status = ReadBoardInfo(sysinfo_, data, offset, length);
if (status.is_error()) {
printf("netsvc: Failed to read board information: %s\n", status.status_string());
return TFTP_ERR_BAD_STATE;
}
return TFTP_NO_ERROR;
}
case NetfileType::kNetCopy: {
ssize_t read_len = netcp_->Read(data, offset, *length);
if (read_len < 0) {
return TFTP_ERR_IO;
}
*length = static_cast<size_t>(read_len);
return TFTP_NO_ERROR;
}
default:
return ZX_ERR_BAD_STATE;
}
return TFTP_ERR_BAD_STATE;
}
tftp_status FileApi::Write(const void* data, size_t* length, off_t offset) {
if (length == nullptr) {
return TFTP_ERR_INVALID_ARGS;
}
switch (type_) {
case NetfileType::kNetboot: {
nbfile_t* nb_file = netboot_file_;
if ((static_cast<size_t>(offset) > nb_file->size) || (offset + *length) > nb_file->size) {
return TFTP_ERR_INVALID_ARGS;
}
memcpy(nb_file->data + offset, data, *length);
nb_file->offset = offset + *length;
return TFTP_NO_ERROR;
}
case NetfileType::kPaver:
return paver_.Write(data, length, offset);
case NetfileType::kBoardInfo: {
zx::result status = CheckBoardName(sysinfo_, reinterpret_cast<const char*>(data), *length);
if (status.is_error()) {
printf("netsvc: Failed to check board name: %s\n", status.status_string());
return TFTP_ERR_BAD_STATE;
}
if (!status.value()) {
printf("netsvc: Board name validation failed\n");
return TFTP_ERR_BAD_STATE;
}
printf("netsvc: Board name validation succeeded\n");
return TFTP_NO_ERROR;
}
case NetfileType::kNetCopy: {
ssize_t write_result = netcp_->Write(reinterpret_cast<const char*>(data), offset, *length);
if (static_cast<size_t>(write_result) == *length) {
return TFTP_NO_ERROR;
}
if (write_result == -EBADF) {
return TFTP_ERR_BAD_STATE;
}
return TFTP_ERR_IO;
}
default:
return ZX_ERR_BAD_STATE;
}
return TFTP_ERR_BAD_STATE;
}
void FileApi::Close() {
if (type_ == NetfileType::kNetCopy) {
netcp_->Close();
} else if (type_ == NetfileType::kPaver) {
paver_.Close();
}
type_ = NetfileType::kUnknown;
}
void FileApi::Abort() {
if (is_write_) {
switch (type_) {
case NetfileType::kNetCopy:
netcp_->AbortWrite();
break;
case NetfileType::kPaver:
paver_.Abort();
break;
default:
break;
}
}
Close();
}
} // namespace netsvc