• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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