// 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 #include #include #include #include #include #include #include #include #include // 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& loop_device_vector = *new std::vector(); 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(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(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(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(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 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(&backing_file), 0) < 0) return std::make_unique(-1, base::FilePath(), base::Bind(&StubIoctlRunner)); return std::make_unique(device_number, backing_file, base::Bind(&StubIoctlRunner)); } std::vector> FakeLoopDeviceManager::SearchLoopDevicePaths(int device_number) { std::vector> devices; struct LoopDev device; if (device_number != -1) { if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_GET_DEV, reinterpret_cast(&device), 0) < 0) return devices; if (device.valid) devices.push_back(std::make_unique( device_number, device.backing_file, base::Bind(&StubIoctlRunner))); return devices; } int i = 0; while (StubIoctlRunner(GetLoopDevicePath(i), LOOP_GET_DEV, reinterpret_cast(&device), 0) == 0) { if (device.valid) devices.push_back(std::make_unique( i, device.backing_file, base::Bind(&StubIoctlRunner))); i++; } return devices; } } // namespace fake } // namespace brillo