xref: /aosp_15_r20/system/core/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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