Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 516ec4c2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "libdm: Add helper classes for loop control."

parents fc1cf907 1f428ea0
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@ cc_library_static {
    srcs: [
        "dm_table.cpp",
        "dm_target.cpp",
        "dm.cpp"
        "dm.cpp",
        "loop_control.cpp",
    ],

    header_libs: [
@@ -47,5 +48,6 @@ cc_test {
    ],
    srcs: [
        "dm_test.cpp",
        "loop_control_test.cpp",
    ]
}
+82 −0
Original line number Diff line number Diff line
/*
 *  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_LOOP_CONTROL_H_
#define _LIBDM_LOOP_CONTROL_H_

#include <string>

#include <android-base/unique_fd.h>

namespace android {
namespace dm {

class LoopControl final {
  public:
    LoopControl();

    // Attaches the file specified by 'file_fd' to the loop device specified
    // by 'loopdev'
    bool Attach(int file_fd, std::string* loopdev) const;

    // Detach the loop device given by 'loopdev' from the attached backing file.
    bool Detach(const std::string& loopdev) const;

    LoopControl(const LoopControl&) = delete;
    LoopControl& operator=(const LoopControl&) = delete;
    LoopControl& operator=(LoopControl&&) = default;
    LoopControl(LoopControl&&) = default;

  private:
    bool FindFreeLoopDevice(std::string* loopdev) const;

    static constexpr const char* kLoopControlDevice = "/dev/loop-control";

    android::base::unique_fd control_fd_;
};

// Create a temporary loop device around a file descriptor or path.
class LoopDevice {
  public:
    // Create a loop device for the given file descriptor. It is closed when
    // LoopDevice is destroyed only if auto_close is true.
    LoopDevice(int fd, bool auto_close = false);
    // Create a loop device for the given file path. It will be opened for
    // reading and writing and closed when the loop device is detached.
    explicit LoopDevice(const std::string& path);
    ~LoopDevice();

    bool valid() const { return fd_ != -1 && !device_.empty(); }
    const std::string& device() const { return device_; }

    LoopDevice(const LoopDevice&) = delete;
    LoopDevice& operator=(const LoopDevice&) = delete;
    LoopDevice& operator=(LoopDevice&&) = default;
    LoopDevice(LoopDevice&&) = default;

  private:
    void Init();

    android::base::unique_fd fd_;
    bool owns_fd_;
    std::string device_;
    LoopControl control_;
};

}  // namespace dm
}  // namespace android

#endif /* _LIBDM_LOOP_CONTROL_H_ */
+121 −0
Original line number Diff line number Diff line
/*
 * 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/loop_control.h"

#include <fcntl.h>
#include <linux/loop.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>

#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>

namespace android {
namespace dm {

LoopControl::LoopControl() : control_fd_(-1) {
    control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));
    if (control_fd_ < 0) {
        PLOG(ERROR) << "Failed to open loop-control";
    }
}

bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
    if (!FindFreeLoopDevice(loopdev)) {
        LOG(ERROR) << "Failed to attach, no free loop devices";
        return false;
    }

    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
    if (loop_fd < 0) {
        PLOG(ERROR) << "Failed to open: " << *loopdev;
        return false;
    }

    int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
    if (rc < 0) {
        PLOG(ERROR) << "Failed LOOP_SET_FD";
        return false;
    }
    return true;
}

bool LoopControl::Detach(const std::string& loopdev) const {
    if (loopdev.empty()) {
        LOG(ERROR) << "Must provide a loop device";
        return false;
    }

    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));
    if (loop_fd < 0) {
        PLOG(ERROR) << "Failed to open: " << loopdev;
        return false;
    }

    int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);
    if (rc) {
        PLOG(ERROR) << "Failed LOOP_CLR_FD for '" << loopdev << "'";
        return false;
    }
    return true;
}

bool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {
    int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);
    if (rc < 0) {
        PLOG(ERROR) << "Failed to get free loop device";
        return false;
    }

    // Ueventd on android creates all loop devices as /dev/block/loopX
    // The total number of available devices is determined by 'loop.max_part'
    // kernel command line argument.
    *loopdev = ::android::base::StringPrintf("/dev/block/loop%d", rc);
    return true;
}

LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
    Init();
}

LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
    if (fd_ < -1) {
        PLOG(ERROR) << "open failed for " << path;
        return;
    }
    Init();
}

LoopDevice::~LoopDevice() {
    if (valid()) {
        control_.Detach(device_);
    }
    if (!owns_fd_) {
        (void)fd_.release();
    }
}

void LoopDevice::Init() {
    control_.Attach(fd_, &device_);
}

}  // namespace dm
}  // namespace android
+65 −0
Original line number Diff line number Diff line
/*
 * 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/loop_control.h"

#include <fcntl.h>
#include <linux/memfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>

using namespace std;
using namespace android::dm;
using unique_fd = android::base::unique_fd;

static unique_fd TempFile() {
    // A loop device needs to be at least one sector to actually work, so fill
    // up the file with a message.
    unique_fd fd(syscall(__NR_memfd_create, "fake_disk", 0));
    if (fd < 0) {
        return {};
    }
    char buffer[] = "Hello";
    for (size_t i = 0; i < 1000; i++) {
        if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {
            perror("write");
            return {};
        }
    }
    return fd;
}

TEST(libdm, LoopControl) {
    unique_fd fd = TempFile();
    ASSERT_GE(fd, 0);

    LoopDevice loop(fd);
    ASSERT_TRUE(loop.valid());

    char buffer[6];
    unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));
    ASSERT_GE(loop_fd, 0);
    ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));
    ASSERT_EQ(memcmp(buffer, "Hello", 6), 0);
}