From 8a11622bcad18ba6e26f2fbf4e5f1f4e6888b9d0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 8 Jul 2020 13:28:24 -0700 Subject: [PATCH] Add experimental daemon for handling dm-user requests. Right now this is open-coded. As the API stabilizes it will be integrated into libdm. Bug: 154536437 Test: manual test Change-Id: I07bb811b6fb80d85d55e218d788023634c8158b7 --- fs_mgr/libdm/Android.bp | 1 + fs_mgr/libsnapshot/Android.bp | 33 ++++++++ fs_mgr/libsnapshot/snapuserd.cpp | 126 +++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 fs_mgr/libsnapshot/snapuserd.cpp diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp index 58241b3f8..e4252845c 100644 --- a/fs_mgr/libdm/Android.bp +++ b/fs_mgr/libdm/Android.bp @@ -42,6 +42,7 @@ cc_library_static { enabled: false, }, }, + ramdisk_available: true, } filegroup { diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 95301ff71..eaef18037 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -310,3 +310,36 @@ cc_test { auto_gen_config: true, require_root: true, } + +cc_defaults { + name: "snapuserd_defaults", + srcs: [ + "snapuserd.cpp", + ], + + cflags: [ + "-Wall", + "-Werror" + ], + + static_libs: [ + "libbase", + "liblog", + "libdm", + ], +} + +cc_binary { + name: "snapuserd", + defaults: ["snapuserd_defaults"], +} + +cc_binary { + name: "snapuserd_ramdisk", + stem: "snapuserd", + defaults: ["snapuserd_defaults"], + + ramdisk: true, + static_executable: true, + system_shared_libs: [], +} diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp new file mode 100644 index 000000000..a6ff4fd04 --- /dev/null +++ b/fs_mgr/libsnapshot/snapuserd.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 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 + +#include +#include +#include +#include +#include + +using android::base::unique_fd; + +#define DM_USER_MAP_READ 0 +#define DM_USER_MAP_WRITE 1 + +struct dm_user_message { + __u64 seq; + __u64 type; + __u64 flags; + __u64 sector; + __u64 len; + __u8 buf[]; +}; + +using namespace android::dm; + +static int daemon_main(const std::string& device) { + unique_fd block_fd(open(device.c_str(), O_RDWR)); + if (block_fd < 0) { + PLOG(ERROR) << "Unable to open " << device; + return 1; + } + + unique_fd ctrl_fd(open("/dev/dm-user", O_RDWR)); + if (ctrl_fd < 0) { + PLOG(ERROR) << "Unable to open /dev/dm-user"; + return 1; + } + + size_t buf_size = 1UL << 16; + auto buf = std::make_unique(buf_size); + + /* Just keeps pumping messages between userspace and the kernel. We won't + * actually be doing anything, but the sequence numbers line up so it'll at + * least make forward progress. */ + while (true) { + struct dm_user_message* msg = (struct dm_user_message*)buf.get(); + + memset(buf.get(), 0, buf_size); + + ssize_t readed = read(ctrl_fd.get(), buf.get(), buf_size); + if (readed < 0) { + PLOG(ERROR) << "Control read failed, trying with more space"; + buf_size *= 2; + buf = std::make_unique(buf_size); + continue; + } + + LOG(DEBUG) << android::base::StringPrintf("read() from dm-user returned %d bytes:", + (int)readed); + LOG(DEBUG) << android::base::StringPrintf(" msg->seq: 0x%016llx", msg->seq); + LOG(DEBUG) << android::base::StringPrintf(" msg->type: 0x%016llx", msg->type); + LOG(DEBUG) << android::base::StringPrintf(" msg->flags: 0x%016llx", msg->flags); + LOG(DEBUG) << android::base::StringPrintf(" msg->sector: 0x%016llx", msg->sector); + LOG(DEBUG) << android::base::StringPrintf(" msg->len: 0x%016llx", msg->len); + + switch (msg->type) { + case DM_USER_MAP_READ: { + LOG(DEBUG) << android::base::StringPrintf( + "Responding to read of sector %lld with %lld bytes data", msg->sector, + msg->len); + + if ((sizeof(*msg) + msg->len) > buf_size) { + auto old_buf = std::move(buf); + buf_size = sizeof(*msg) + msg->len; + buf = std::make_unique(buf_size); + memcpy(buf.get(), old_buf.get(), sizeof(*msg)); + msg = (struct dm_user_message*)buf.get(); + } + + if (lseek(block_fd.get(), msg->sector * 512, SEEK_SET) < 0) { + PLOG(ERROR) << "lseek failed: " << device; + return 7; + } + if (!android::base::ReadFully(block_fd.get(), msg->buf, msg->len)) { + PLOG(ERROR) << "read failed: " << device; + return 7; + } + + if (!android::base::WriteFully(ctrl_fd.get(), buf.get(), sizeof(*msg) + msg->len)) { + PLOG(ERROR) << "write control failed"; + return 3; + } + break; + } + + case DM_USER_MAP_WRITE: + abort(); + break; + } + + LOG(DEBUG) << "read() finished, next message"; + } + + return 0; +} + +int main([[maybe_unused]] int argc, char** argv) { + android::base::InitLogging(argv, &android::base::KernelLogger); + daemon_main(argv[1]); + return 0; +}