xref: /aosp_15_r20/external/libbrillo/brillo/blkdev_utils/loop_device.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1*1a96fba6SXin Li // Copyright 2018 The Chromium OS Authors. All rights reserved.
2*1a96fba6SXin Li // Use of this source code is governed by a BSD-style license that can be
3*1a96fba6SXin Li // found in the LICENSE file.
4*1a96fba6SXin Li 
5*1a96fba6SXin Li #include <brillo/blkdev_utils/loop_device.h>
6*1a96fba6SXin Li 
7*1a96fba6SXin Li #include <fcntl.h>
8*1a96fba6SXin Li #include <linux/major.h>
9*1a96fba6SXin Li #include <sys/ioctl.h>
10*1a96fba6SXin Li #include <sys/types.h>
11*1a96fba6SXin Li #include <unistd.h>
12*1a96fba6SXin Li 
13*1a96fba6SXin Li #include <memory>
14*1a96fba6SXin Li #include <string>
15*1a96fba6SXin Li #include <utility>
16*1a96fba6SXin Li #include <vector>
17*1a96fba6SXin Li 
18*1a96fba6SXin Li #include <base/files/file_enumerator.h>
19*1a96fba6SXin Li #include <base/files/file_util.h>
20*1a96fba6SXin Li #include <base/files/scoped_file.h>
21*1a96fba6SXin Li #include <base/posix/eintr_wrapper.h>
22*1a96fba6SXin Li #include <base/strings/string_number_conversions.h>
23*1a96fba6SXin Li #include <base/strings/string_split.h>
24*1a96fba6SXin Li #include <base/strings/string_util.h>
25*1a96fba6SXin Li #include <base/strings/stringprintf.h>
26*1a96fba6SXin Li 
27*1a96fba6SXin Li namespace brillo {
28*1a96fba6SXin Li 
29*1a96fba6SXin Li namespace {
30*1a96fba6SXin Li 
31*1a96fba6SXin Li constexpr char kLoopControl[] = "/dev/loop-control";
32*1a96fba6SXin Li constexpr char kSysBlockPath[] = "/sys/block";
33*1a96fba6SXin Li // File containing device id in /sys/block/loopX/.
34*1a96fba6SXin Li constexpr char kDeviceIdPath[] = "dev";
35*1a96fba6SXin Li constexpr char kLoopBackingFile[] = "loop/backing_file";
36*1a96fba6SXin Li constexpr int kLoopDeviceIoctlFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
37*1a96fba6SXin Li constexpr int kLoopControlIoctlFlags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC;
38*1a96fba6SXin Li 
39*1a96fba6SXin Li // ioctl runner for LoopDevice and LoopDeviceManager
LoopDeviceIoctl(const base::FilePath & device,int type,uint64_t arg,int open_flag)40*1a96fba6SXin Li int LoopDeviceIoctl(const base::FilePath& device,
41*1a96fba6SXin Li                     int type,
42*1a96fba6SXin Li                     uint64_t arg,
43*1a96fba6SXin Li                     int open_flag) {
44*1a96fba6SXin Li   base::ScopedFD device_fd(
45*1a96fba6SXin Li       HANDLE_EINTR(open(device.value().c_str(), open_flag)));
46*1a96fba6SXin Li 
47*1a96fba6SXin Li   if (!device_fd.is_valid()) {
48*1a96fba6SXin Li     PLOG(ERROR) << "Unable to open loop device";
49*1a96fba6SXin Li     return -EINVAL;
50*1a96fba6SXin Li   }
51*1a96fba6SXin Li 
52*1a96fba6SXin Li   int rc = ioctl(device_fd.get(), type, arg);
53*1a96fba6SXin Li 
54*1a96fba6SXin Li   if (rc < 0)
55*1a96fba6SXin Li     PLOG(ERROR) << "ioctl failed.";
56*1a96fba6SXin Li 
57*1a96fba6SXin Li   return rc;
58*1a96fba6SXin Li }
59*1a96fba6SXin Li 
60*1a96fba6SXin Li // Parse the device number for a valid /sys/block/loopX path
61*1a96fba6SXin Li // or symlink to such a path.
62*1a96fba6SXin Li // Returns -1 if invalid.
GetDeviceNumber(const base::FilePath & sys_block_loopdev_path)63*1a96fba6SXin Li int GetDeviceNumber(const base::FilePath& sys_block_loopdev_path) {
64*1a96fba6SXin Li   std::string device_string;
65*1a96fba6SXin Li   int device_number = -1;
66*1a96fba6SXin Li 
67*1a96fba6SXin Li   base::FilePath device_file = sys_block_loopdev_path.Append(kDeviceIdPath);
68*1a96fba6SXin Li 
69*1a96fba6SXin Li   if (!base::ReadFileToString(device_file, &device_string))
70*1a96fba6SXin Li     return -1;
71*1a96fba6SXin Li 
72*1a96fba6SXin Li   std::vector<std::string> device_ids = base::SplitString(
73*1a96fba6SXin Li       device_string, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
74*1a96fba6SXin Li 
75*1a96fba6SXin Li   if (device_ids.size() != 2 ||
76*1a96fba6SXin Li       device_ids[0] != base::NumberToString(LOOP_MAJOR))
77*1a96fba6SXin Li     return -1;
78*1a96fba6SXin Li 
79*1a96fba6SXin Li   base::StringToInt(device_ids[1], &device_number);
80*1a96fba6SXin Li   return device_number;
81*1a96fba6SXin Li }
82*1a96fba6SXin Li 
83*1a96fba6SXin Li // For a validated loop device path, return the backing file path.
84*1a96fba6SXin Li // Note that a pre-populated loop device path would return an empty
85*1a96fba6SXin Li // backing file.
GetBackingFile(const base::FilePath & loopdev_path)86*1a96fba6SXin Li base::FilePath GetBackingFile(const base::FilePath& loopdev_path) {
87*1a96fba6SXin Li   // Backing file contains path to associated source for loop devices.
88*1a96fba6SXin Li   base::FilePath backing_file = loopdev_path.Append(kLoopBackingFile);
89*1a96fba6SXin Li   std::string backing_file_content;
90*1a96fba6SXin Li   // If the backing file doesn't exist, it's not an attached loop device.
91*1a96fba6SXin Li   if (!base::ReadFileToString(backing_file, &backing_file_content))
92*1a96fba6SXin Li     return base::FilePath();
93*1a96fba6SXin Li   base::FilePath backing_file_path(
94*1a96fba6SXin Li       base::TrimWhitespaceASCII(backing_file_content, base::TRIM_ALL));
95*1a96fba6SXin Li 
96*1a96fba6SXin Li   return backing_file_path;
97*1a96fba6SXin Li }
98*1a96fba6SXin Li 
CreateDevicePath(int device_number)99*1a96fba6SXin Li base::FilePath CreateDevicePath(int device_number) {
100*1a96fba6SXin Li   return base::FilePath(base::StringPrintf("/dev/loop%d", device_number));
101*1a96fba6SXin Li }
102*1a96fba6SXin Li 
103*1a96fba6SXin Li }  // namespace
104*1a96fba6SXin Li 
LoopDevice(int device_number,const base::FilePath & backing_file,const LoopIoctl & ioctl_runner)105*1a96fba6SXin Li LoopDevice::LoopDevice(int device_number,
106*1a96fba6SXin Li                        const base::FilePath& backing_file,
107*1a96fba6SXin Li                        const LoopIoctl& ioctl_runner)
108*1a96fba6SXin Li     : device_number_(device_number),
109*1a96fba6SXin Li       backing_file_(backing_file),
110*1a96fba6SXin Li       loop_ioctl_(ioctl_runner) {}
111*1a96fba6SXin Li 
SetStatus(struct loop_info64 info)112*1a96fba6SXin Li bool LoopDevice::SetStatus(struct loop_info64 info) {
113*1a96fba6SXin Li   if (loop_ioctl_.Run(GetDevicePath(), LOOP_SET_STATUS64,
114*1a96fba6SXin Li                       reinterpret_cast<uint64_t>(&info),
115*1a96fba6SXin Li                       kLoopDeviceIoctlFlags) < 0) {
116*1a96fba6SXin Li     LOG(ERROR) << "ioctl(LOOP_SET_STATUS64) failed";
117*1a96fba6SXin Li     return false;
118*1a96fba6SXin Li   }
119*1a96fba6SXin Li   return true;
120*1a96fba6SXin Li }
121*1a96fba6SXin Li 
GetStatus(struct loop_info64 * info)122*1a96fba6SXin Li bool LoopDevice::GetStatus(struct loop_info64* info) {
123*1a96fba6SXin Li   if (loop_ioctl_.Run(GetDevicePath(), LOOP_GET_STATUS64,
124*1a96fba6SXin Li                       reinterpret_cast<uint64_t>(info),
125*1a96fba6SXin Li                       kLoopDeviceIoctlFlags) < 0) {
126*1a96fba6SXin Li     LOG(ERROR) << "ioctl(LOOP_GET_STATUS64) failed";
127*1a96fba6SXin Li     return false;
128*1a96fba6SXin Li   }
129*1a96fba6SXin Li   return true;
130*1a96fba6SXin Li }
131*1a96fba6SXin Li 
SetName(const std::string & name)132*1a96fba6SXin Li bool LoopDevice::SetName(const std::string& name) {
133*1a96fba6SXin Li   struct loop_info64 info;
134*1a96fba6SXin Li 
135*1a96fba6SXin Li   memset(&info, 0, sizeof(info));
136*1a96fba6SXin Li   strncpy(reinterpret_cast<char*>(info.lo_file_name), name.c_str(),
137*1a96fba6SXin Li           LO_NAME_SIZE);
138*1a96fba6SXin Li   return SetStatus(info);
139*1a96fba6SXin Li }
140*1a96fba6SXin Li 
Detach()141*1a96fba6SXin Li bool LoopDevice::Detach() {
142*1a96fba6SXin Li   if (loop_ioctl_.Run(GetDevicePath(), LOOP_CLR_FD, 0, kLoopDeviceIoctlFlags) !=
143*1a96fba6SXin Li       0) {
144*1a96fba6SXin Li     LOG(ERROR) << "ioctl(LOOP_CLR_FD) failed";
145*1a96fba6SXin Li     return false;
146*1a96fba6SXin Li   }
147*1a96fba6SXin Li 
148*1a96fba6SXin Li   return true;
149*1a96fba6SXin Li }
150*1a96fba6SXin Li 
GetDevicePath()151*1a96fba6SXin Li base::FilePath LoopDevice::GetDevicePath() {
152*1a96fba6SXin Li   return CreateDevicePath(device_number_);
153*1a96fba6SXin Li }
154*1a96fba6SXin Li 
IsValid()155*1a96fba6SXin Li bool LoopDevice::IsValid() {
156*1a96fba6SXin Li   return device_number_ >= 0;
157*1a96fba6SXin Li }
158*1a96fba6SXin Li 
LoopDeviceManager()159*1a96fba6SXin Li LoopDeviceManager::LoopDeviceManager()
160*1a96fba6SXin Li     : loop_ioctl_(base::Bind(&LoopDeviceIoctl)) {}
161*1a96fba6SXin Li 
LoopDeviceManager(LoopIoctl ioctl_runner)162*1a96fba6SXin Li LoopDeviceManager::LoopDeviceManager(LoopIoctl ioctl_runner)
163*1a96fba6SXin Li     : loop_ioctl_(ioctl_runner) {}
164*1a96fba6SXin Li 
AttachDeviceToFile(const base::FilePath & backing_file)165*1a96fba6SXin Li std::unique_ptr<LoopDevice> LoopDeviceManager::AttachDeviceToFile(
166*1a96fba6SXin Li     const base::FilePath& backing_file) {
167*1a96fba6SXin Li   int device_number = -1;
168*1a96fba6SXin Li   while (true) {
169*1a96fba6SXin Li     device_number =
170*1a96fba6SXin Li         loop_ioctl_.Run(base::FilePath(kLoopControl), LOOP_CTL_GET_FREE, 0,
171*1a96fba6SXin Li                         kLoopControlIoctlFlags);
172*1a96fba6SXin Li 
173*1a96fba6SXin Li     if (device_number < 0) {
174*1a96fba6SXin Li       LOG(ERROR) << "ioctl(LOOP_CTL_GET_FREE) failed";
175*1a96fba6SXin Li       return CreateLoopDevice(-1, base::FilePath());
176*1a96fba6SXin Li     }
177*1a96fba6SXin Li 
178*1a96fba6SXin Li     base::ScopedFD backing_file_fd(
179*1a96fba6SXin Li         HANDLE_EINTR(open(backing_file.value().c_str(), O_RDWR)));
180*1a96fba6SXin Li 
181*1a96fba6SXin Li     if (!backing_file_fd.is_valid()) {
182*1a96fba6SXin Li       LOG(ERROR) << "Failed to open backing file.";
183*1a96fba6SXin Li       return CreateLoopDevice(-1, base::FilePath());
184*1a96fba6SXin Li     }
185*1a96fba6SXin Li 
186*1a96fba6SXin Li     base::FilePath device_path = CreateDevicePath(device_number);
187*1a96fba6SXin Li 
188*1a96fba6SXin Li     if (loop_ioctl_.Run(device_path, LOOP_SET_FD, backing_file_fd.get(),
189*1a96fba6SXin Li                         kLoopDeviceIoctlFlags) == 0)
190*1a96fba6SXin Li       break;
191*1a96fba6SXin Li 
192*1a96fba6SXin Li     if (errno != EBUSY) {
193*1a96fba6SXin Li       LOG(ERROR) << "ioctl(LOOP_SET_FD) failed";
194*1a96fba6SXin Li       return CreateLoopDevice(-1, base::FilePath());
195*1a96fba6SXin Li     }
196*1a96fba6SXin Li   }
197*1a96fba6SXin Li   // All steps of setting up the loop device succeeded.
198*1a96fba6SXin Li   return CreateLoopDevice(device_number, backing_file);
199*1a96fba6SXin Li }
200*1a96fba6SXin Li 
201*1a96fba6SXin Li std::vector<std::unique_ptr<LoopDevice>>
GetAttachedDevices()202*1a96fba6SXin Li LoopDeviceManager::GetAttachedDevices() {
203*1a96fba6SXin Li   return SearchLoopDevicePaths();
204*1a96fba6SXin Li }
205*1a96fba6SXin Li 
GetAttachedDeviceByNumber(int device_number)206*1a96fba6SXin Li std::unique_ptr<LoopDevice> LoopDeviceManager::GetAttachedDeviceByNumber(
207*1a96fba6SXin Li     int device_number) {
208*1a96fba6SXin Li   auto devices = SearchLoopDevicePaths(device_number);
209*1a96fba6SXin Li 
210*1a96fba6SXin Li   if (devices.empty())
211*1a96fba6SXin Li     return CreateLoopDevice(-1, base::FilePath());
212*1a96fba6SXin Li 
213*1a96fba6SXin Li   return std::move(devices[0]);
214*1a96fba6SXin Li }
215*1a96fba6SXin Li 
GetAttachedDeviceByName(const std::string & name)216*1a96fba6SXin Li std::unique_ptr<LoopDevice> LoopDeviceManager::GetAttachedDeviceByName(
217*1a96fba6SXin Li     const std::string& name) {
218*1a96fba6SXin Li   std::vector<std::unique_ptr<LoopDevice>> devices = GetAttachedDevices();
219*1a96fba6SXin Li 
220*1a96fba6SXin Li   for (auto& attached_device : devices) {
221*1a96fba6SXin Li     struct loop_info64 device_info;
222*1a96fba6SXin Li 
223*1a96fba6SXin Li     if (!attached_device->GetStatus(&device_info)) {
224*1a96fba6SXin Li       LOG(ERROR) << "GetStatus failed";
225*1a96fba6SXin Li       continue;
226*1a96fba6SXin Li     }
227*1a96fba6SXin Li 
228*1a96fba6SXin Li     if (strcmp(reinterpret_cast<char*>(device_info.lo_file_name),
229*1a96fba6SXin Li                name.c_str()) == 0)
230*1a96fba6SXin Li       return std::move(attached_device);
231*1a96fba6SXin Li   }
232*1a96fba6SXin Li 
233*1a96fba6SXin Li   return CreateLoopDevice(-1, base::FilePath());
234*1a96fba6SXin Li }
235*1a96fba6SXin Li 
236*1a96fba6SXin Li // virtual
237*1a96fba6SXin Li std::vector<std::unique_ptr<LoopDevice>>
SearchLoopDevicePaths(int device_number)238*1a96fba6SXin Li LoopDeviceManager::SearchLoopDevicePaths(int device_number) {
239*1a96fba6SXin Li   std::vector<std::unique_ptr<LoopDevice>> devices;
240*1a96fba6SXin Li   base::FilePath rootdir(kSysBlockPath);
241*1a96fba6SXin Li 
242*1a96fba6SXin Li   if (device_number != -1) {
243*1a96fba6SXin Li     auto loopdev_path =
244*1a96fba6SXin Li         rootdir.Append(base::StringPrintf("loop%d", device_number));
245*1a96fba6SXin Li     if (base::PathExists(loopdev_path))
246*1a96fba6SXin Li       devices.push_back(
247*1a96fba6SXin Li           CreateLoopDevice(device_number, GetBackingFile(loopdev_path)));
248*1a96fba6SXin Li   } else {
249*1a96fba6SXin Li     // Read /sys/block to discover all loop devices.
250*1a96fba6SXin Li     base::FileEnumerator loopdev_enum(
251*1a96fba6SXin Li         rootdir, false /*recursive*/,
252*1a96fba6SXin Li         base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS,
253*1a96fba6SXin Li         "loop*");
254*1a96fba6SXin Li 
255*1a96fba6SXin Li     for (auto loopdev = loopdev_enum.Next(); !loopdev.empty();
256*1a96fba6SXin Li          loopdev = loopdev_enum.Next()) {
257*1a96fba6SXin Li       int dev_number = GetDeviceNumber(loopdev);
258*1a96fba6SXin Li       if (dev_number != -1)
259*1a96fba6SXin Li         devices.push_back(
260*1a96fba6SXin Li             CreateLoopDevice(dev_number, GetBackingFile(loopdev)));
261*1a96fba6SXin Li     }
262*1a96fba6SXin Li   }
263*1a96fba6SXin Li   return devices;
264*1a96fba6SXin Li }
265*1a96fba6SXin Li 
CreateLoopDevice(int device_number,const base::FilePath & backing_file)266*1a96fba6SXin Li std::unique_ptr<LoopDevice> LoopDeviceManager::CreateLoopDevice(
267*1a96fba6SXin Li     int device_number, const base::FilePath& backing_file) {
268*1a96fba6SXin Li   return std::make_unique<LoopDevice>(device_number, backing_file, loop_ioctl_);
269*1a96fba6SXin Li }
270*1a96fba6SXin Li 
271*1a96fba6SXin Li }  // namespace brillo
272