diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index bc3b04b02..923442f90 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -56,12 +56,15 @@ cc_library_static { "libselinux", "libavb", "libfstab", + "libdm", ], export_static_lib_headers: [ "libfstab", + "libdm", ], whole_static_libs: [ "liblogwrap", + "libdm", "libfstab", ], cppflags: [ diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp new file mode 100644 index 000000000..672d401cd --- /dev/null +++ b/fs_mgr/libdm/Android.bp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2018 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. +// + +cc_library_static { + name: "libdm", + recovery_available: true, + + export_include_dirs: ["include"], + cflags: [ + // TODO(b/110035986): Allows us to create a skeleton of required classes + "-Wno-unused-private-field", + ], + + srcs: [ + "dm_table.cpp", + "dm_target.cpp", + "dm.cpp" + ], + + header_libs: [ + "libbase_headers", + "liblog_headers", + ], +} diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp new file mode 100644 index 000000000..9a2910e2c --- /dev/null +++ b/fs_mgr/libdm/dm.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 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 +#include + +#include +#include + +#include +#include +#include + +#include "dm.h" + +namespace android { +namespace dm { + +DeviceMapper& DeviceMapper::Instance() { + static DeviceMapper instance; + return instance; +} +// Creates a new device mapper device +bool DeviceMapper::CreateDevice(const std::string& /* name */) { + // Creates a new device mapper device with the name passed in + return false; +} + +bool DeviceMapper::DeleteDevice(const std::string& /* name */) { + // Destroy device here first + return false; +} + +const std::unique_ptr DeviceMapper::table(const std::string& /* name */) const { + // TODO(b/110035986): Return the table, as read from the kernel instead + return nullptr; +} + +DmDeviceState DeviceMapper::state(const std::string& /* name */) const { + // TODO(b/110035986): Return the state, as read from the kernel instead + return DmDeviceState::INVALID; +} + +bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) { + return false; +} + +// Reads all the available device mapper targets and their corresponding +// versions from the kernel and returns in a vector +bool DeviceMapper::GetAvailableTargets(std::vector* targets) { + targets->clear(); + + // calculate the space needed to read a maximum of kMaxPossibleDmTargets + uint32_t payload_size = sizeof(struct dm_target_versions); + payload_size += DM_MAX_TYPE_NAME; + // device mapper wants every target spec to be aligned at 8-byte boundary + payload_size = DM_ALIGN(payload_size); + payload_size *= kMaxPossibleDmTargets; + + uint32_t data_size = sizeof(struct dm_ioctl) + payload_size; + auto buffer = std::unique_ptr(calloc(1, data_size), free); + if (buffer == nullptr) { + LOG(ERROR) << "failed to allocate memory"; + return false; + } + + struct dm_ioctl* io = reinterpret_cast(buffer.get()); + io->data_start = sizeof(*io); + io->data_size = data_size; + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + if (ioctl(fd_, DM_LIST_VERSIONS, io)) { + PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel"; + return false; + } + + // If the provided buffer wasn't enough to list all targets, note that + // any data beyond sizeof(*io) must not be read in this case + if (io->flags & DM_BUFFER_FULL_FLAG) { + LOG(INFO) << data_size << " is not enough memory to list all dm targets"; + return false; + } + + // if there are no targets registered, return success with empty vector + if (io->data_size == sizeof(*io)) { + return true; + } + + // Parse each target and list the name and version + // TODO(b/110035986): Templatize this + uint32_t next = sizeof(*io); + data_size = io->data_size - next; + struct dm_target_versions* vers = + reinterpret_cast(static_cast(buffer.get()) + next); + while (next && data_size) { + targets->emplace_back((vers)); + if (vers->next == 0) { + break; + } + next += vers->next; + data_size -= vers->next; + vers = reinterpret_cast(static_cast(buffer.get()) + next); + } + + return true; +} + +// Accepts a device mapper device name (like system_a, vendor_b etc) and +// returns the path to it's device node (or symlink to the device node) +std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) { + return ""; +} + +} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp new file mode 100644 index 000000000..14b393297 --- /dev/null +++ b/fs_mgr/libdm/dm_table.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 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 "dm_table.h" + +namespace android { +namespace dm { + +bool DmTable::AddTarget(std::unique_ptr&& /* target */) { + return true; +} + +bool DmTable::RemoveTarget(std::unique_ptr&& /* target */) { + return true; +} + +bool DmTable::valid() const { + return true; +} + +uint64_t DmTable::size() const { + return valid() ? size_ : 0; +} + +// Returns a string represnetation of the table that is ready to be passed +// down to the kernel for loading +// +// Implementation must verify there are no gaps in the table, table starts +// with sector == 0, and iterate over each target to get its table +// serialized. +std::string DmTable::Serialize() const { + return ""; +} + +} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp new file mode 100644 index 000000000..8bcd5262a --- /dev/null +++ b/fs_mgr/libdm/dm_target.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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 "dm_target.h" + +namespace android { +namespace dm {} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h new file mode 100644 index 000000000..0cd0149bc --- /dev/null +++ b/fs_mgr/libdm/include/dm.h @@ -0,0 +1,108 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#ifndef _LIBDM_DM_H_ +#define _LIBDM_DM_H_ + +#include +#include +#include + +#include + +#include + +#include + +#define DM_ALIGN_MASK (7) +#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK) + +namespace android { +namespace dm { + +enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE }; + +class DeviceMapper final { + public: + // Creates a device mapper device with given name. + // Return 'true' on success and 'false' on failure to + // create OR if a device mapper device with the same name already + // exists. + // TODO(b/110035986): Make this method private and to be only + // called through LoadTableAndActivate() below. + bool CreateDevice(const std::string& name); + + // Removes a device mapper device with the given name. + // Returns 'true' on success, false otherwise. + bool DeleteDevice(const std::string& name); + + // Reads the device mapper table from the device with given anme and + // returns it in a DmTable object. + const std::unique_ptr table(const std::string& name) const; + + // Returns the current state of the underlying device mapper device + // with given name. + // One of INVALID, SUSPENDED or ACTIVE. + DmDeviceState state(const std::string& name) const; + + // Loads the device mapper table from parameter into the underlying + // device mapper device with given name and activate / resumes the device in the process. + // If a device mapper device with the 'name', doesn't exist, it will be created. + // Returns 'true' on success, false otherwise. + bool LoadTableAndActivate(const std::string& name, const DmTable& table); + + // Returns true if a list of available device mapper targets registered in the kernel was + // successfully read and stored in 'targets'. Returns 'false' otherwise. + bool GetAvailableTargets(std::vector* targets); + + // Returns the path to the device mapper device node in '/dev' corresponding to + // 'name'. + std::string GetDmDevicePathByName(const std::string& name); + + // The only way to create a DeviceMapper object. + static DeviceMapper& Instance(); + + ~DeviceMapper() { + if (fd_ != -1) { + ::close(fd_); + } + } + + private: + // Maximum possible device mapper targets registered in the kernel. + // This is only used to read the list of targets from kernel so we allocate + // a finite amount of memory. This limit is in no way enforced by the kernel. + static constexpr uint32_t kMaxPossibleDmTargets = 256; + + DeviceMapper() : fd_(-1) { + fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)); + if (fd_ < 0) { + PLOG(ERROR) << "Failed to open device-mapper"; + } + } + + int fd_; + // Non-copyable & Non-movable + DeviceMapper(const DeviceMapper&) = delete; + DeviceMapper& operator=(const DeviceMapper&) = delete; + DeviceMapper& operator=(DeviceMapper&&) = delete; + DeviceMapper(DeviceMapper&&) = delete; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DM_H_ */ diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/dm_table.h new file mode 100644 index 000000000..0b1685db0 --- /dev/null +++ b/fs_mgr/libdm/include/dm_table.h @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#ifndef _LIBDM_DMTABLE_H_ +#define _LIBDM_DMTABLE_H_ + +#include + +#include +#include +#include + +#include "dm_target.h" + +namespace android { +namespace dm { + +class DmTable { + public: + DmTable() : size_(0){}; + + // Adds a target to the device mapper table for a range specified in the target object. + // The function will return 'true' if the target was successfully added and doesn't overlap with + // any of the existing targets in the table. Gaps are allowed. The final check, including + // overlaps and gaps are done before loading the table. Returns 'false' on failure. + bool AddTarget(std::unique_ptr&& target); + + // Removes a target from the table for the range specified in the target object. Returns 'false' + // if the target name doesn't match with the one in the table. Returns 'true' if target is + // successfully removed. + bool RemoveTarget(std::unique_ptr&& target); + + // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps + // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the + // table is malformed. + bool valid() const; + + // Returns the total size represented by the table in terms of number of 512-byte sectors. + // NOTE: This function will overlook if there are any gaps in the targets added in the table. + uint64_t size() const; + + // Returns the string represntation of the table that is ready to be passed into the kernel + // as part of the DM_TABLE_LOAD ioctl. + std::string Serialize() const; + + ~DmTable() = default; + + private: + // list of targets defined in this table sorted by + // their start and end sectors. + // Note: Overlapping targets MUST never be added in this list. + std::vector> targets_; + + // Total size in terms of # of sectors, as calculated by looking at the last and the first + // target in 'target_'. + uint64_t size_; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DMTABLE_H_ */ diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h new file mode 100644 index 000000000..31b0cb69c --- /dev/null +++ b/fs_mgr/libdm/include/dm_target.h @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Google, Inc + * + * 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. + */ + +#ifndef _LIBDM_DMTARGET_H_ +#define _LIBDM_DMTARGET_H_ + +#include +#include + +#include + +#include + +namespace android { +namespace dm { + +class DmTarget { + public: + DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0) + : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){}; + + // Creates a DmTarget object from dm_target_version as read from kernel + // with DM_LIST_VERSION ioctl. + DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) { + CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr"; + v0_ = vers->version[0]; + v1_ = vers->version[1]; + v2_ = vers->version[2]; + name_ = vers->name; + } + + virtual ~DmTarget() = default; + + // Returns name of the target. + const std::string& name() const { return name_; } + + // Returns size in number of sectors when this target is part of + // a DmTable, return 0 otherwise. + uint64_t size() const { return length_; } + + // Return string representation of the device mapper target version. + std::string version() const { + return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_); + } + + // Function that converts this object to a string of arguments that can + // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear) + // must implement this, for it to be used on a device. + virtual std::string Serialize() const { return ""; } + + private: + // Name of the target. + std::string name_; + // Target version. + uint32_t v0_, v1_, v2_; + // logical sector number start and total length (in terms of 512-byte sectors) represented + // by this target within a DmTable. + uint64_t start_, length_; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DMTARGET_H_ */ diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp new file mode 100644 index 000000000..4d4aae41a --- /dev/null +++ b/fs_mgr/tools/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2018 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. +// + +cc_binary { + name: "dmctl", + srcs: ["dmctl.cpp"], + + static_libs: [ + "libfs_mgr", + ], + + shared_libs: [ + "libbase", + "liblog", + ], + + cflags: ["-Werror"], +} diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp new file mode 100644 index 000000000..27e2a5825 --- /dev/null +++ b/fs_mgr/tools/dmctl.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 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 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using DeviceMapper = ::android::dm::DeviceMapper; +using DmTarget = ::android::dm::DmTarget; + +static int Usage(void) { + std::cerr << "usage: dmctl [command options]"; + std::cerr << "commands:"; + std::cerr << " create [-lo ] "; + std::cerr, " delete "; + std::cerr, " list"; + std::cerr, " help"; + return -EINVAL; +} + +static int DmCreateCmdHandler(int argc, char** argv) { + if (argc <= 1) { + std::cerr << "DmCreateCmdHandler: Invalid arguments"; + if (argc > 0) std::cerr << " args: " << argv[0]; + return -EINVAL; + } + + // Parse Everything first to make sure we have everything we need. + std::string devname = argv[0]; + DeviceMapper& dm = DeviceMapper::Instance(); + std::vector targets; + if (!dm.GetAvailableTargets(&targets)) { + std::cerr << "Failed to read available device mapper targets"; + return -errno; + } + + if (targets.empty()) { + std::cerr << "zero device mapper targets available"; + return -EEXIST; + } + + for (const auto& target : targets) { + if (target.name() == argv[1]) { + // TODO(b/110035986) : Create the target here, return success for now. + return 0; + } + } + + std::cerr << "Invalid or non-existing target : " << argv[1]; + return -EINVAL; +} + +static int DmDeleteCmdHandler(int argc, char** argv) { + std::cout << "DmDeleteCmdHandler:" << std::endl; + std::cout << " args:" << std::endl; + for (int i = 0; i < argc; i++) { + std::cout << " " << argv[i] << std::endl; + } + + return 0; +} + +static int DmListCmdHandler(int /* argc */, char** /* argv */) { + std::cout << "Available Device Mapper Targets:" << std::endl; + + DeviceMapper& dm = DeviceMapper::Instance(); + std::vector targets; + if (!dm.GetAvailableTargets(&targets)) { + std::cerr << "Failed to read available device mapper targets"; + return -errno; + } + + if (targets.empty()) { + std::cout << " " << std::endl; + return 0; + } + + for (const auto& target : targets) { + std::cout << std::left << std::setw(20) << target.name() << " : " << target.version() + << std::endl; + } + + return 0; +} + +static int HelpCmdHandler(int /* argc */, char** /* argv */) { + Usage(); + return 0; +} + +static std::map> cmdmap = { + {"create", DmCreateCmdHandler}, + {"delete", DmDeleteCmdHandler}, + {"list", DmListCmdHandler}, + {"help", HelpCmdHandler}, +}; + +int main(int argc, char** argv) { + android::base::InitLogging(argv, &android::base::StderrLogger); + if (argc < 2) { + return Usage(); + } + + for (const auto& cmd : cmdmap) { + if (cmd.first == argv[1]) { + return cmd.second(argc - 2, argv + 2); + } + } + + return Usage(); +}