blob: e988335025540fa6738812d0691a7e3b549aa41d [file] [log] [blame]
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <snapuserd/dm_user_block_server.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <snapuserd/snapuserd_kernel.h>
#include "snapuserd_logging.h"
namespace android {
namespace snapshot {
using android::base::unique_fd;
DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
Delegate* delegate, size_t buffer_size)
: misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
buffer_.Initialize(buffer_size);
}
bool DmUserBlockServer::ProcessRequests() {
struct dm_user_header* header = buffer_.GetHeaderPtr();
if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
if (errno != ENOTBLK) {
SNAP_PLOG(ERROR) << "Control-read failed";
}
SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
return false;
}
SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
if (!ProcessRequest(header)) {
if (header->type != DM_USER_RESP_ERROR) {
SendError();
}
return false;
}
return true;
}
bool DmUserBlockServer::ProcessRequest(dm_user_header* header) {
// Use the same header buffer as the response header.
int request_type = header->type;
header->type = DM_USER_RESP_SUCCESS;
header_response_ = true;
// Reset the output buffer.
buffer_.ResetBufferOffset();
switch (request_type) {
case DM_USER_REQ_MAP_READ:
return delegate_->RequestSectors(header->sector, header->len);
case DM_USER_REQ_MAP_WRITE:
// We should not get any write request to dm-user as we mount all
// partitions as read-only.
SNAP_LOG(ERROR) << "Unexpected write request from dm-user";
return false;
default:
SNAP_LOG(ERROR) << "Unexpected request from dm-user: " << request_type;
return false;
}
}
void* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
return buffer_.AcquireBuffer(size, to_write);
}
bool DmUserBlockServer::SendBufferedIo() {
return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());
}
void DmUserBlockServer::SendError() {
struct dm_user_header* header = buffer_.GetHeaderPtr();
header->type = DM_USER_RESP_ERROR;
// This is an issue with the dm-user interface. There
// is no way to propagate the I/O error back to dm-user
// if we have already communicated the header back. Header
// is responded once at the beginning; however I/O can
// be processed in chunks. If we encounter an I/O error
// somewhere in the middle of the processing, we can't communicate
// this back to dm-user.
//
// TODO: Fix the interface
CHECK(header_response_);
WriteDmUserPayload(0);
}
bool DmUserBlockServer::WriteDmUserPayload(size_t size) {
size_t payload_size = size;
void* buf = buffer_.GetPayloadBufPtr();
if (header_response_) {
payload_size += sizeof(struct dm_user_header);
buf = buffer_.GetBufPtr();
}
if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
return false;
}
// After the first header is sent in response to a request, we cannot
// send any additional headers.
header_response_ = false;
// Reset the buffer for use by the next request.
buffer_.ResetBufferOffset();
return true;
}
DmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,
const std::string& dm_user_path)
: misc_name_(misc_name), dm_user_path_(dm_user_path) {}
std::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,
size_t buffer_size) {
unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
SNAP_PLOG(ERROR) << "Could not open dm-user path: " << dm_user_path_;
return nullptr;
}
return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);
}
std::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(
const std::string& misc_name) {
auto dm_path = "/dev/dm-user/" + misc_name;
return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);
}
} // namespace snapshot
} // namespace android