blob: a181aad67099aa8081ae2f22a1e7743cc5aa2815 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <brillo/blkdev_utils/loop_device_fake.h>
#include <linux/loop.h>
#include <memory>
#include <string>
#include <vector>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/blkdev_utils/loop_device.h>
// Not a loop ioctl: we only use this to get the backing file from
// the stubbed function. All loop device ioctls start with 0x4c.
#define LOOP_GET_DEV 0x4cff
namespace brillo {
namespace fake {
namespace {
int ParseLoopDeviceNumber(const base::FilePath& device_path) {
int device_number;
std::string path_string = device_path.value();
return base::StartsWith(path_string, "/dev/loop",
base::CompareCase::SENSITIVE) &&
base::StringToInt(path_string.substr(9), &device_number)
? device_number
: -1;
}
base::FilePath GetLoopDevicePath(int device_number) {
return base::FilePath(base::StringPrintf("/dev/loop%d", device_number));
}
int StubIoctlRunner(const base::FilePath& path,
int type,
uint64_t arg,
int flag) {
int device_number = ParseLoopDeviceNumber(path);
struct loop_info64* info;
struct LoopDev* device;
static std::vector<struct LoopDev>& loop_device_vector =
*new std::vector<struct LoopDev>();
switch (type) {
case LOOP_GET_STATUS64:
if (loop_device_vector.size() <= device_number ||
loop_device_vector[device_number].valid == false)
return -1;
info = reinterpret_cast<struct loop_info64*>(arg);
memcpy(info, &loop_device_vector[device_number].info,
sizeof(struct loop_info64));
return 0;
case LOOP_SET_STATUS64:
if (loop_device_vector.size() <= device_number ||
loop_device_vector[device_number].valid == false)
return -1;
info = reinterpret_cast<struct loop_info64*>(arg);
memcpy(&loop_device_vector[device_number].info, info,
sizeof(struct loop_info64));
return 0;
case LOOP_CLR_FD:
if (loop_device_vector.size() <= device_number ||
loop_device_vector[device_number].valid == false)
return -1;
loop_device_vector[device_number].valid = false;
return 0;
case LOOP_CTL_GET_FREE:
device_number = loop_device_vector.size();
loop_device_vector.push_back({true, base::FilePath(), {0}});
return device_number;
// Instead of passing the fd here, we pass the FilePath of the backing
// file.
case LOOP_SET_FD:
if (loop_device_vector.size() <= device_number)
return -1;
loop_device_vector[device_number].backing_file =
*reinterpret_cast<const base::FilePath*>(arg);
return 0;
// Not a loop ioctl; Only used for conveniently checking the
// validity of the loop devices.
case LOOP_GET_DEV:
if (device_number >= loop_device_vector.size())
return -1;
device = reinterpret_cast<struct LoopDev*>(arg);
device->valid = loop_device_vector[device_number].valid;
device->backing_file = loop_device_vector[device_number].backing_file;
memset(&(device->info), 0, sizeof(struct loop_info64));
return 0;
default:
return -1;
}
}
} // namespace
FakeLoopDeviceManager::FakeLoopDeviceManager()
: LoopDeviceManager(base::Bind(&StubIoctlRunner)) {}
std::unique_ptr<LoopDevice> FakeLoopDeviceManager::AttachDeviceToFile(
const base::FilePath& backing_file) {
int device_number = StubIoctlRunner(base::FilePath("/dev/loop-control"),
LOOP_CTL_GET_FREE, 0, 0);
if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_SET_FD,
reinterpret_cast<uint64_t>(&backing_file), 0) < 0)
return std::make_unique<LoopDevice>(-1, base::FilePath(),
base::Bind(&StubIoctlRunner));
return std::make_unique<LoopDevice>(device_number, backing_file,
base::Bind(&StubIoctlRunner));
}
std::vector<std::unique_ptr<LoopDevice>>
FakeLoopDeviceManager::SearchLoopDevicePaths(int device_number) {
std::vector<std::unique_ptr<LoopDevice>> devices;
struct LoopDev device;
if (device_number != -1) {
if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_GET_DEV,
reinterpret_cast<uint64_t>(&device), 0) < 0)
return devices;
if (device.valid)
devices.push_back(std::make_unique<LoopDevice>(
device_number, device.backing_file, base::Bind(&StubIoctlRunner)));
return devices;
}
int i = 0;
while (StubIoctlRunner(GetLoopDevicePath(i), LOOP_GET_DEV,
reinterpret_cast<uint64_t>(&device), 0) == 0) {
if (device.valid)
devices.push_back(std::make_unique<LoopDevice>(
i, device.backing_file, base::Bind(&StubIoctlRunner)));
i++;
}
return devices;
}
} // namespace fake
} // namespace brillo