1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <snapuserd/dm_user_block_server.h>
16
17 #include <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <snapuserd/snapuserd_kernel.h>
20 #include "snapuserd_logging.h"
21
22 namespace android {
23 namespace snapshot {
24
25 using android::base::unique_fd;
26
DmUserBlockServer(const std::string & misc_name,unique_fd && ctrl_fd,Delegate * delegate,size_t buffer_size)27 DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
28 Delegate* delegate, size_t buffer_size)
29 : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
30 buffer_.Initialize(sizeof(struct dm_user_header), buffer_size);
31 }
32
ProcessRequests()33 bool DmUserBlockServer::ProcessRequests() {
34 struct dm_user_header* header =
35 reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
36 if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
37 if (errno != ENOTBLK) {
38 SNAP_PLOG(ERROR) << "Control-read failed";
39 }
40
41 SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
42 return false;
43 }
44
45 SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
46 SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
47 SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
48 SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
49 SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
50
51 if (!ProcessRequest(header)) {
52 if (header->type != DM_USER_RESP_ERROR) {
53 SendError();
54 }
55 return false;
56 }
57 return true;
58 }
59
ProcessRequest(dm_user_header * header)60 bool DmUserBlockServer::ProcessRequest(dm_user_header* header) {
61 // Use the same header buffer as the response header.
62 int request_type = header->type;
63 header->type = DM_USER_RESP_SUCCESS;
64 header_response_ = true;
65
66 // Reset the output buffer.
67 buffer_.ResetBufferOffset();
68
69 switch (request_type) {
70 case DM_USER_REQ_MAP_READ:
71 return delegate_->RequestSectors(header->sector, header->len);
72
73 case DM_USER_REQ_MAP_WRITE:
74 // We should not get any write request to dm-user as we mount all
75 // partitions as read-only.
76 SNAP_LOG(ERROR) << "Unexpected write request from dm-user";
77 return false;
78
79 default:
80 SNAP_LOG(ERROR) << "Unexpected request from dm-user: " << request_type;
81 return false;
82 }
83 }
84
GetResponseBuffer(size_t size,size_t to_write)85 void* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
86 return buffer_.AcquireBuffer(size, to_write);
87 }
88
SendBufferedIo()89 bool DmUserBlockServer::SendBufferedIo() {
90 return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());
91 }
92
SendError()93 void DmUserBlockServer::SendError() {
94 struct dm_user_header* header =
95 reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());
96 header->type = DM_USER_RESP_ERROR;
97 // This is an issue with the dm-user interface. There
98 // is no way to propagate the I/O error back to dm-user
99 // if we have already communicated the header back. Header
100 // is responded once at the beginning; however I/O can
101 // be processed in chunks. If we encounter an I/O error
102 // somewhere in the middle of the processing, we can't communicate
103 // this back to dm-user.
104 //
105 // TODO: Fix the interface
106 CHECK(header_response_);
107
108 WriteDmUserPayload(0);
109 }
110
WriteDmUserPayload(size_t size)111 bool DmUserBlockServer::WriteDmUserPayload(size_t size) {
112 size_t payload_size = size;
113 void* buf = buffer_.GetPayloadBufPtr();
114 if (header_response_) {
115 payload_size += sizeof(struct dm_user_header);
116 buf = buffer_.GetBufPtr();
117 }
118
119 if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
120 SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
121 return false;
122 }
123
124 // After the first header is sent in response to a request, we cannot
125 // send any additional headers.
126 header_response_ = false;
127
128 // Reset the buffer for use by the next request.
129 buffer_.ResetBufferOffset();
130 return true;
131 }
132
DmUserBlockServerOpener(const std::string & misc_name,const std::string & dm_user_path)133 DmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,
134 const std::string& dm_user_path)
135 : misc_name_(misc_name), dm_user_path_(dm_user_path) {}
136
Open(IBlockServer::Delegate * delegate,size_t buffer_size)137 std::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,
138 size_t buffer_size) {
139 unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));
140 if (fd < 0) {
141 SNAP_PLOG(ERROR) << "Could not open dm-user path: " << dm_user_path_;
142 return nullptr;
143 }
144 return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);
145 }
146
CreateOpener(const std::string & misc_name)147 std::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(
148 const std::string& misc_name) {
149 auto dm_path = "/dev/dm-user/" + misc_name;
150 return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);
151 }
152
153 } // namespace snapshot
154 } // namespace android
155