1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/blkdev_utils/loop_device_fake.h>
6
7 #include <linux/loop.h>
8 #include <memory>
9 #include <string>
10 #include <vector>
11
12 #include <base/strings/string_number_conversions.h>
13 #include <base/strings/string_split.h>
14 #include <base/strings/string_util.h>
15 #include <base/strings/stringprintf.h>
16 #include <brillo/blkdev_utils/loop_device.h>
17
18 // Not a loop ioctl: we only use this to get the backing file from
19 // the stubbed function. All loop device ioctls start with 0x4c.
20 #define LOOP_GET_DEV 0x4cff
21
22 namespace brillo {
23 namespace fake {
24
25 namespace {
26
ParseLoopDeviceNumber(const base::FilePath & device_path)27 int ParseLoopDeviceNumber(const base::FilePath& device_path) {
28 int device_number;
29 std::string path_string = device_path.value();
30 return base::StartsWith(path_string, "/dev/loop",
31 base::CompareCase::SENSITIVE) &&
32 base::StringToInt(path_string.substr(9), &device_number)
33 ? device_number
34 : -1;
35 }
36
GetLoopDevicePath(int device_number)37 base::FilePath GetLoopDevicePath(int device_number) {
38 return base::FilePath(base::StringPrintf("/dev/loop%d", device_number));
39 }
40
StubIoctlRunner(const base::FilePath & path,int type,uint64_t arg,int flag)41 int StubIoctlRunner(const base::FilePath& path,
42 int type,
43 uint64_t arg,
44 int flag) {
45 int device_number = ParseLoopDeviceNumber(path);
46 struct loop_info64* info;
47 struct LoopDev* device;
48 static std::vector<struct LoopDev>& loop_device_vector =
49 *new std::vector<struct LoopDev>();
50
51 switch (type) {
52 case LOOP_GET_STATUS64:
53 if (loop_device_vector.size() <= device_number ||
54 loop_device_vector[device_number].valid == false)
55 return -1;
56 info = reinterpret_cast<struct loop_info64*>(arg);
57 memcpy(info, &loop_device_vector[device_number].info,
58 sizeof(struct loop_info64));
59 return 0;
60 case LOOP_SET_STATUS64:
61 if (loop_device_vector.size() <= device_number ||
62 loop_device_vector[device_number].valid == false)
63 return -1;
64 info = reinterpret_cast<struct loop_info64*>(arg);
65 memcpy(&loop_device_vector[device_number].info, info,
66 sizeof(struct loop_info64));
67 return 0;
68 case LOOP_CLR_FD:
69 if (loop_device_vector.size() <= device_number ||
70 loop_device_vector[device_number].valid == false)
71 return -1;
72 loop_device_vector[device_number].valid = false;
73 return 0;
74 case LOOP_CTL_GET_FREE:
75 device_number = loop_device_vector.size();
76 loop_device_vector.push_back({true, base::FilePath(), {0}});
77 return device_number;
78 // Instead of passing the fd here, we pass the FilePath of the backing
79 // file.
80 case LOOP_SET_FD:
81 if (loop_device_vector.size() <= device_number)
82 return -1;
83 loop_device_vector[device_number].backing_file =
84 *reinterpret_cast<const base::FilePath*>(arg);
85 return 0;
86 // Not a loop ioctl; Only used for conveniently checking the
87 // validity of the loop devices.
88 case LOOP_GET_DEV:
89 if (device_number >= loop_device_vector.size())
90 return -1;
91 device = reinterpret_cast<struct LoopDev*>(arg);
92 device->valid = loop_device_vector[device_number].valid;
93 device->backing_file = loop_device_vector[device_number].backing_file;
94 memset(&(device->info), 0, sizeof(struct loop_info64));
95 return 0;
96 default:
97 return -1;
98 }
99 }
100
101 } // namespace
102
FakeLoopDeviceManager()103 FakeLoopDeviceManager::FakeLoopDeviceManager()
104 : LoopDeviceManager(base::Bind(&StubIoctlRunner)) {}
105
AttachDeviceToFile(const base::FilePath & backing_file)106 std::unique_ptr<LoopDevice> FakeLoopDeviceManager::AttachDeviceToFile(
107 const base::FilePath& backing_file) {
108 int device_number = StubIoctlRunner(base::FilePath("/dev/loop-control"),
109 LOOP_CTL_GET_FREE, 0, 0);
110
111 if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_SET_FD,
112 reinterpret_cast<uint64_t>(&backing_file), 0) < 0)
113 return std::make_unique<LoopDevice>(-1, base::FilePath(),
114 base::Bind(&StubIoctlRunner));
115
116 return std::make_unique<LoopDevice>(device_number, backing_file,
117 base::Bind(&StubIoctlRunner));
118 }
119
120 std::vector<std::unique_ptr<LoopDevice>>
SearchLoopDevicePaths(int device_number)121 FakeLoopDeviceManager::SearchLoopDevicePaths(int device_number) {
122 std::vector<std::unique_ptr<LoopDevice>> devices;
123 struct LoopDev device;
124
125 if (device_number != -1) {
126 if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_GET_DEV,
127 reinterpret_cast<uint64_t>(&device), 0) < 0)
128 return devices;
129
130 if (device.valid)
131 devices.push_back(std::make_unique<LoopDevice>(
132 device_number, device.backing_file, base::Bind(&StubIoctlRunner)));
133 return devices;
134 }
135
136 int i = 0;
137 while (StubIoctlRunner(GetLoopDevicePath(i), LOOP_GET_DEV,
138 reinterpret_cast<uint64_t>(&device), 0) == 0) {
139 if (device.valid)
140 devices.push_back(std::make_unique<LoopDevice>(
141 i, device.backing_file, base::Bind(&StubIoctlRunner)));
142 i++;
143 }
144 return devices;
145 }
146
147 } // namespace fake
148 } // namespace brillo
149