Skip to content
Snippets Groups Projects
Select Git revision
  • f2fe3486668cb8fa5cd8db0932e14fe8d0832bbe
  • master default protected
  • android-7.1.2_r28_klist
  • pie-cts-release
  • pie-vts-release
  • pie-cts-dev
  • oreo-mr1-iot-release
  • sdk-release
  • oreo-m6-s4-release
  • oreo-m4-s12-release
  • pie-release
  • pie-r2-release
  • pie-r2-s1-release
  • oreo-vts-release
  • oreo-cts-release
  • oreo-dev
  • oreo-mr1-dev
  • pie-gsi
  • pie-platform-release
  • pie-dev
  • oreo-cts-dev
  • android-o-mr1-iot-release-1.0.4
  • android-9.0.0_r8
  • android-9.0.0_r7
  • android-9.0.0_r6
  • android-9.0.0_r5
  • android-8.1.0_r46
  • android-8.1.0_r45
  • android-n-iot-release-smart-display-r2
  • android-vts-8.1_r5
  • android-cts-8.1_r8
  • android-cts-8.0_r12
  • android-cts-7.1_r20
  • android-cts-7.0_r24
  • android-o-mr1-iot-release-1.0.3
  • android-cts-9.0_r1
  • android-8.1.0_r43
  • android-8.1.0_r42
  • android-n-iot-release-smart-display
  • android-p-preview-5
  • android-9.0.0_r3
41 results

file_contexts

Blame
  • dm.cpp 11.50 KiB
    /*
     * 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 "libdm/dm.h"
    
    #include <sys/ioctl.h>
    #include <sys/sysmacros.h>
    #include <sys/types.h>
    
    #include <android-base/logging.h>
    #include <android-base/macros.h>
    
    namespace android {
    namespace dm {
    
    DeviceMapper::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";
        }
    }
    
    DeviceMapper& DeviceMapper::Instance() {
        static DeviceMapper instance;
        return instance;
    }
    // Creates a new device mapper device
    bool DeviceMapper::CreateDevice(const std::string& name) {
        if (name.empty()) {
            LOG(ERROR) << "Unnamed device mapper device creation is not supported";
            return false;
        }
    
        if (name.size() >= DM_NAME_LEN) {
            LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
            return false;
        }
    
        struct dm_ioctl io;
        InitIo(&io, name);
    
        if (ioctl(fd_, DM_DEV_CREATE, &io)) {
            PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
            return false;
        }
    
        // Check to make sure the newly created device doesn't already have targets
        // added or opened by someone
        CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
        CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
    
        // Creates a new device mapper device with the name passed in
        return true;
    }
    
    bool DeviceMapper::DeleteDevice(const std::string& name) {
        if (name.empty()) {
            LOG(ERROR) << "Unnamed device mapper device creation is not supported";
            return false;
        }
    
        if (name.size() >= DM_NAME_LEN) {
            LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
            return false;
        }
    
        struct dm_ioctl io;
        InitIo(&io, name);
    
        if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
            PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
            return false;
        }
    
        // Check to make sure appropriate uevent is generated so ueventd will
        // do the right thing and remove the corresponding device node and symlinks.
        CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
                << "Didn't generate uevent for [" << name << "] removal";
    
        return true;
    }
    
    const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
        // TODO(b/110035986): Return the table, as read from the kernel instead
        return nullptr;
    }
    
    DmDeviceState DeviceMapper::GetState(const std::string& name) const {
        struct dm_ioctl io;
        InitIo(&io, name);
        if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
            return DmDeviceState::INVALID;
        }
        if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
            return DmDeviceState::ACTIVE;
        }
        return DmDeviceState::SUSPENDED;
    }
    
    bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
        if (!CreateDevice(name)) {
            return false;
        }
        if (!LoadTableAndActivate(name, table)) {
            DeleteDevice(name);
            return false;
        }
        return true;
    }
    
    bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
        std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
        ioctl_buffer += table.Serialize();
    
        struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
        InitIo(io, name);
        io->data_size = ioctl_buffer.size();
        io->data_start = sizeof(struct dm_ioctl);
        io->target_count = static_cast<uint32_t>(table.num_targets());
        if (table.readonly()) {
            io->flags |= DM_READONLY_FLAG;
        }
        if (ioctl(fd_, DM_TABLE_LOAD, io)) {
            PLOG(ERROR) << "DM_TABLE_LOAD failed";
            return false;
        }
    
        InitIo(io, name);
        if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
            PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
            return false;
        }
        return true;
    }
    
    // Reads all the available device mapper targets and their corresponding
    // versions from the kernel and returns in a vector
    bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* 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<void, void (*)(void*)>(calloc(1, data_size), free);
        if (buffer == nullptr) {
            LOG(ERROR) << "failed to allocate memory";
            return false;
        }
    
        // Sets appropriate data size and data_start to make sure we tell kernel
        // about the total size of the buffer we are passing and where to start
        // writing the list of targets.
        struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
        InitIo(io);
        io->data_size = data_size;
        io->data_start = sizeof(*io);
    
        if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
            PLOG(ERROR) << "DM_LIST_VERSIONS failed";
            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<struct dm_target_versions*>(static_cast<char*>(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<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
        }
    
        return true;
    }
    
    bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
        devices->clear();
    
        // calculate the space needed to read a maximum of 256 targets, each with
        // name with maximum length of 16 bytes
        uint32_t payload_size = sizeof(struct dm_name_list);
        // 128-bytes for the name
        payload_size += DM_NAME_LEN;
        // dm wants every device spec to be aligned at 8-byte boundary
        payload_size = DM_ALIGN(payload_size);
        payload_size *= kMaxPossibleDmDevices;
        uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
        auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
        if (buffer == nullptr) {
            LOG(ERROR) << "failed to allocate memory";
            return false;
        }
    
        // Sets appropriate data size and data_start to make sure we tell kernel
        // about the total size of the buffer we are passing and where to start
        // writing the list of targets.
        struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
        InitIo(io);
        io->data_size = data_size;
        io->data_start = sizeof(*io);
    
        if (ioctl(fd_, DM_LIST_DEVICES, io)) {
            PLOG(ERROR) << "DM_LIST_DEVICES failed";
            return false;
        }
    
        // If the provided buffer wasn't enough to list all devices any data
        // beyond sizeof(*io) must not be read.
        if (io->flags & DM_BUFFER_FULL_FLAG) {
            LOG(INFO) << data_size << " is not enough memory to list all dm devices";
            return false;
        }
    
        // if there are no devices created yet, return success with empty vector
        if (io->data_size == sizeof(*io)) {
            return true;
        }
    
        // Parse each device and add a new DmBlockDevice to the vector
        // created from the kernel data.
        uint32_t next = sizeof(*io);
        data_size = io->data_size - next;
        struct dm_name_list* dm_dev =
                reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
    
        while (next && data_size) {
            devices->emplace_back((dm_dev));
            if (dm_dev->next == 0) {
                break;
            }
            next += dm_dev->next;
            data_size -= dm_dev->next;
            dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(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)
    bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
        struct dm_ioctl io;
        InitIo(&io, name);
        if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
            PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
            return false;
        }
    
        uint32_t dev_num = minor(io.dev);
        *path = "/dev/block/dm-" + std::to_string(dev_num);
        return true;
    }
    
    bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
        char buffer[4096];
        struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
    
        InitIo(io, name);
        io->data_size = sizeof(buffer);
        io->data_start = sizeof(*io);
        if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
            PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
            return false;
        }
        if (io->flags & DM_BUFFER_FULL_FLAG) {
            PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
            return false;
        }
    
        uint32_t cursor = io->data_start;
        uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
        for (uint32_t i = 0; i < io->target_count; i++) {
            if (cursor + sizeof(struct dm_target_spec) > data_end) {
                break;
            }
            // After each dm_target_spec is a status string. spec->next is an
            // offset from |io->data_start|, and we clamp it to the size of our
            // buffer.
            struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
            uint32_t data_offset = cursor + sizeof(dm_target_spec);
            uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
    
            std::string data;
            if (next_cursor > data_offset) {
                // Note: we use c_str() to eliminate any extra trailing 0s.
                data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
            }
            table->emplace_back(*spec, data);
            cursor = next_cursor;
        }
        return true;
    }
    
    // private methods of DeviceMapper
    void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
        CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
        memset(io, 0, sizeof(*io));
    
        io->version[0] = DM_VERSION0;
        io->version[1] = DM_VERSION1;
        io->version[2] = DM_VERSION2;
        io->data_size = sizeof(*io);
        io->data_start = 0;
        if (!name.empty()) {
            strlcpy(io->name, name.c_str(), sizeof(io->name));
        }
    }
    
    }  // namespace dm
    }  // namespace android