1*33f37583SAndroid Build Coastguard Worker /*
2*33f37583SAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*33f37583SAndroid Build Coastguard Worker *
4*33f37583SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*33f37583SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*33f37583SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*33f37583SAndroid Build Coastguard Worker *
8*33f37583SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*33f37583SAndroid Build Coastguard Worker *
10*33f37583SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*33f37583SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*33f37583SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*33f37583SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*33f37583SAndroid Build Coastguard Worker * limitations under the License.
15*33f37583SAndroid Build Coastguard Worker */
16*33f37583SAndroid Build Coastguard Worker
17*33f37583SAndroid Build Coastguard Worker #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
18*33f37583SAndroid Build Coastguard Worker
19*33f37583SAndroid Build Coastguard Worker #include "apexd_loop.h"
20*33f37583SAndroid Build Coastguard Worker
21*33f37583SAndroid Build Coastguard Worker #include <ApexProperties.sysprop.h>
22*33f37583SAndroid Build Coastguard Worker #include <android-base/file.h>
23*33f37583SAndroid Build Coastguard Worker #include <android-base/logging.h>
24*33f37583SAndroid Build Coastguard Worker #include <android-base/parseint.h>
25*33f37583SAndroid Build Coastguard Worker #include <android-base/properties.h>
26*33f37583SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
27*33f37583SAndroid Build Coastguard Worker #include <android-base/strings.h>
28*33f37583SAndroid Build Coastguard Worker #include <dirent.h>
29*33f37583SAndroid Build Coastguard Worker #include <fcntl.h>
30*33f37583SAndroid Build Coastguard Worker #include <libdm/dm.h>
31*33f37583SAndroid Build Coastguard Worker #include <linux/fs.h>
32*33f37583SAndroid Build Coastguard Worker #include <linux/loop.h>
33*33f37583SAndroid Build Coastguard Worker #include <string>
34*33f37583SAndroid Build Coastguard Worker #include <sys/ioctl.h>
35*33f37583SAndroid Build Coastguard Worker #include <sys/stat.h>
36*33f37583SAndroid Build Coastguard Worker #include <sys/statfs.h>
37*33f37583SAndroid Build Coastguard Worker #include <sys/sysmacros.h>
38*33f37583SAndroid Build Coastguard Worker #include <sys/types.h>
39*33f37583SAndroid Build Coastguard Worker #include <unistd.h>
40*33f37583SAndroid Build Coastguard Worker #include <utils/Trace.h>
41*33f37583SAndroid Build Coastguard Worker
42*33f37583SAndroid Build Coastguard Worker #include <array>
43*33f37583SAndroid Build Coastguard Worker #include <filesystem>
44*33f37583SAndroid Build Coastguard Worker #include <mutex>
45*33f37583SAndroid Build Coastguard Worker #include <string_view>
46*33f37583SAndroid Build Coastguard Worker
47*33f37583SAndroid Build Coastguard Worker #include "apexd_utils.h"
48*33f37583SAndroid Build Coastguard Worker
49*33f37583SAndroid Build Coastguard Worker using android::base::Basename;
50*33f37583SAndroid Build Coastguard Worker using android::base::ErrnoError;
51*33f37583SAndroid Build Coastguard Worker using android::base::Error;
52*33f37583SAndroid Build Coastguard Worker using android::base::GetBoolProperty;
53*33f37583SAndroid Build Coastguard Worker using android::base::ParseUint;
54*33f37583SAndroid Build Coastguard Worker using android::base::ReadFileToString;
55*33f37583SAndroid Build Coastguard Worker using android::base::Result;
56*33f37583SAndroid Build Coastguard Worker using android::base::StartsWith;
57*33f37583SAndroid Build Coastguard Worker using android::base::StringPrintf;
58*33f37583SAndroid Build Coastguard Worker using android::base::unique_fd;
59*33f37583SAndroid Build Coastguard Worker using android::dm::DeviceMapper;
60*33f37583SAndroid Build Coastguard Worker
61*33f37583SAndroid Build Coastguard Worker namespace android {
62*33f37583SAndroid Build Coastguard Worker namespace apex {
63*33f37583SAndroid Build Coastguard Worker namespace loop {
64*33f37583SAndroid Build Coastguard Worker
65*33f37583SAndroid Build Coastguard Worker static constexpr const char* kApexLoopIdPrefix = "apex:";
66*33f37583SAndroid Build Coastguard Worker
67*33f37583SAndroid Build Coastguard Worker // 128 kB read-ahead, which we currently use for /system as well
68*33f37583SAndroid Build Coastguard Worker static constexpr const unsigned int kReadAheadKb = 128;
69*33f37583SAndroid Build Coastguard Worker
MaybeCloseBad()70*33f37583SAndroid Build Coastguard Worker void LoopbackDeviceUniqueFd::MaybeCloseBad() {
71*33f37583SAndroid Build Coastguard Worker if (device_fd.get() != -1) {
72*33f37583SAndroid Build Coastguard Worker // Disassociate any files.
73*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd.get(), LOOP_CLR_FD) == -1) {
74*33f37583SAndroid Build Coastguard Worker PLOG(ERROR) << "Unable to clear fd for loopback device";
75*33f37583SAndroid Build Coastguard Worker }
76*33f37583SAndroid Build Coastguard Worker }
77*33f37583SAndroid Build Coastguard Worker }
78*33f37583SAndroid Build Coastguard Worker
ConfigureScheduler(const std::string & device_path)79*33f37583SAndroid Build Coastguard Worker Result<void> ConfigureScheduler(const std::string& device_path) {
80*33f37583SAndroid Build Coastguard Worker ATRACE_NAME("ConfigureScheduler");
81*33f37583SAndroid Build Coastguard Worker if (!StartsWith(device_path, "/dev/")) {
82*33f37583SAndroid Build Coastguard Worker return Error() << "Invalid argument " << device_path;
83*33f37583SAndroid Build Coastguard Worker }
84*33f37583SAndroid Build Coastguard Worker
85*33f37583SAndroid Build Coastguard Worker const std::string device_name = Basename(device_path);
86*33f37583SAndroid Build Coastguard Worker
87*33f37583SAndroid Build Coastguard Worker const std::string sysfs_path =
88*33f37583SAndroid Build Coastguard Worker StringPrintf("/sys/block/%s/queue/scheduler", device_name.c_str());
89*33f37583SAndroid Build Coastguard Worker unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
90*33f37583SAndroid Build Coastguard Worker if (sysfs_fd.get() == -1) {
91*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to open " << sysfs_path;
92*33f37583SAndroid Build Coastguard Worker }
93*33f37583SAndroid Build Coastguard Worker
94*33f37583SAndroid Build Coastguard Worker // Kernels before v4.1 only support 'noop'. Kernels [v4.1, v5.0) support
95*33f37583SAndroid Build Coastguard Worker // 'noop' and 'none'. Kernels v5.0 and later only support 'none'.
96*33f37583SAndroid Build Coastguard Worker static constexpr const std::array<std::string_view, 2> kNoScheduler = {
97*33f37583SAndroid Build Coastguard Worker "none", "noop"};
98*33f37583SAndroid Build Coastguard Worker
99*33f37583SAndroid Build Coastguard Worker int ret = 0;
100*33f37583SAndroid Build Coastguard Worker std::string cur_sched_str;
101*33f37583SAndroid Build Coastguard Worker if (!ReadFileToString(sysfs_path, &cur_sched_str)) {
102*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to read " << sysfs_path;
103*33f37583SAndroid Build Coastguard Worker }
104*33f37583SAndroid Build Coastguard Worker cur_sched_str = android::base::Trim(cur_sched_str);
105*33f37583SAndroid Build Coastguard Worker if (std::count(kNoScheduler.begin(), kNoScheduler.end(), cur_sched_str)) {
106*33f37583SAndroid Build Coastguard Worker return {};
107*33f37583SAndroid Build Coastguard Worker }
108*33f37583SAndroid Build Coastguard Worker
109*33f37583SAndroid Build Coastguard Worker for (const std::string_view& scheduler : kNoScheduler) {
110*33f37583SAndroid Build Coastguard Worker ret = write(sysfs_fd.get(), scheduler.data(), scheduler.size());
111*33f37583SAndroid Build Coastguard Worker if (ret > 0) {
112*33f37583SAndroid Build Coastguard Worker break;
113*33f37583SAndroid Build Coastguard Worker }
114*33f37583SAndroid Build Coastguard Worker }
115*33f37583SAndroid Build Coastguard Worker
116*33f37583SAndroid Build Coastguard Worker if (ret <= 0) {
117*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to write to " << sysfs_path;
118*33f37583SAndroid Build Coastguard Worker }
119*33f37583SAndroid Build Coastguard Worker
120*33f37583SAndroid Build Coastguard Worker return {};
121*33f37583SAndroid Build Coastguard Worker }
122*33f37583SAndroid Build Coastguard Worker
123*33f37583SAndroid Build Coastguard Worker // Return the parent device of a partition. Converts e.g. "sda26" into "sda".
PartitionParent(const std::string & blockdev)124*33f37583SAndroid Build Coastguard Worker static Result<std::string> PartitionParent(const std::string& blockdev) {
125*33f37583SAndroid Build Coastguard Worker if (blockdev.find('/') != std::string::npos) {
126*33f37583SAndroid Build Coastguard Worker return Error() << "Invalid argument " << blockdev;
127*33f37583SAndroid Build Coastguard Worker }
128*33f37583SAndroid Build Coastguard Worker std::error_code ec;
129*33f37583SAndroid Build Coastguard Worker for (const auto& entry :
130*33f37583SAndroid Build Coastguard Worker std::filesystem::directory_iterator("/sys/class/block", ec)) {
131*33f37583SAndroid Build Coastguard Worker const std::string path = entry.path().string();
132*33f37583SAndroid Build Coastguard Worker if (std::filesystem::exists(
133*33f37583SAndroid Build Coastguard Worker StringPrintf("%s/%s", path.c_str(), blockdev.c_str()))) {
134*33f37583SAndroid Build Coastguard Worker return Basename(path);
135*33f37583SAndroid Build Coastguard Worker }
136*33f37583SAndroid Build Coastguard Worker }
137*33f37583SAndroid Build Coastguard Worker return blockdev;
138*33f37583SAndroid Build Coastguard Worker }
139*33f37583SAndroid Build Coastguard Worker
140*33f37583SAndroid Build Coastguard Worker // Convert a major:minor pair into a block device name.
BlockdevName(dev_t dev)141*33f37583SAndroid Build Coastguard Worker static std::string BlockdevName(dev_t dev) {
142*33f37583SAndroid Build Coastguard Worker std::error_code ec;
143*33f37583SAndroid Build Coastguard Worker for (const auto& entry :
144*33f37583SAndroid Build Coastguard Worker std::filesystem::directory_iterator("/dev/block", ec)) {
145*33f37583SAndroid Build Coastguard Worker struct stat statbuf;
146*33f37583SAndroid Build Coastguard Worker if (stat(entry.path().string().c_str(), &statbuf) < 0) {
147*33f37583SAndroid Build Coastguard Worker continue;
148*33f37583SAndroid Build Coastguard Worker }
149*33f37583SAndroid Build Coastguard Worker if (dev == statbuf.st_rdev) {
150*33f37583SAndroid Build Coastguard Worker return Basename(entry.path().string());
151*33f37583SAndroid Build Coastguard Worker }
152*33f37583SAndroid Build Coastguard Worker }
153*33f37583SAndroid Build Coastguard Worker return {};
154*33f37583SAndroid Build Coastguard Worker }
155*33f37583SAndroid Build Coastguard Worker
156*33f37583SAndroid Build Coastguard Worker // For file `file_path`, retrieve the block device backing the filesystem on
157*33f37583SAndroid Build Coastguard Worker // which the file exists and return the queue depth of the block device. The
158*33f37583SAndroid Build Coastguard Worker // loop in this function may e.g. traverse the following hierarchy:
159*33f37583SAndroid Build Coastguard Worker // /dev/block/dm-9 (system-verity; dm-verity)
160*33f37583SAndroid Build Coastguard Worker // -> /dev/block/dm-1 (system_b; dm-linear)
161*33f37583SAndroid Build Coastguard Worker // -> /dev/sda26
BlockDeviceQueueDepth(const std::string & file_path)162*33f37583SAndroid Build Coastguard Worker static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
163*33f37583SAndroid Build Coastguard Worker struct stat statbuf;
164*33f37583SAndroid Build Coastguard Worker int res = stat(file_path.c_str(), &statbuf);
165*33f37583SAndroid Build Coastguard Worker if (res < 0) {
166*33f37583SAndroid Build Coastguard Worker return ErrnoErrorf("stat({})", file_path.c_str());
167*33f37583SAndroid Build Coastguard Worker }
168*33f37583SAndroid Build Coastguard Worker std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
169*33f37583SAndroid Build Coastguard Worker LOG(VERBOSE) << file_path << " -> " << blockdev;
170*33f37583SAndroid Build Coastguard Worker if (blockdev.empty()) {
171*33f37583SAndroid Build Coastguard Worker return Errorf("Failed to convert {}:{} (path {})", major(statbuf.st_dev),
172*33f37583SAndroid Build Coastguard Worker minor(statbuf.st_dev), file_path.c_str());
173*33f37583SAndroid Build Coastguard Worker }
174*33f37583SAndroid Build Coastguard Worker auto& dm = DeviceMapper::Instance();
175*33f37583SAndroid Build Coastguard Worker for (;;) {
176*33f37583SAndroid Build Coastguard Worker std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
177*33f37583SAndroid Build Coastguard Worker if (!child) {
178*33f37583SAndroid Build Coastguard Worker break;
179*33f37583SAndroid Build Coastguard Worker }
180*33f37583SAndroid Build Coastguard Worker LOG(VERBOSE) << blockdev << " -> " << *child;
181*33f37583SAndroid Build Coastguard Worker blockdev = *child;
182*33f37583SAndroid Build Coastguard Worker }
183*33f37583SAndroid Build Coastguard Worker std::optional<std::string> maybe_blockdev =
184*33f37583SAndroid Build Coastguard Worker android::dm::ExtractBlockDeviceName(blockdev);
185*33f37583SAndroid Build Coastguard Worker if (!maybe_blockdev) {
186*33f37583SAndroid Build Coastguard Worker return Error() << "Failed to remove /dev/block/ prefix from " << blockdev;
187*33f37583SAndroid Build Coastguard Worker }
188*33f37583SAndroid Build Coastguard Worker Result<std::string> maybe_parent = PartitionParent(*maybe_blockdev);
189*33f37583SAndroid Build Coastguard Worker if (!maybe_parent.ok()) {
190*33f37583SAndroid Build Coastguard Worker return Error() << "Failed to determine parent of " << *maybe_blockdev;
191*33f37583SAndroid Build Coastguard Worker }
192*33f37583SAndroid Build Coastguard Worker blockdev = *maybe_parent;
193*33f37583SAndroid Build Coastguard Worker LOG(VERBOSE) << "Partition parent: " << blockdev;
194*33f37583SAndroid Build Coastguard Worker const std::string nr_tags_path =
195*33f37583SAndroid Build Coastguard Worker StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
196*33f37583SAndroid Build Coastguard Worker std::string nr_tags;
197*33f37583SAndroid Build Coastguard Worker if (!ReadFileToString(nr_tags_path, &nr_tags)) {
198*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to read " << nr_tags_path;
199*33f37583SAndroid Build Coastguard Worker }
200*33f37583SAndroid Build Coastguard Worker nr_tags = android::base::Trim(nr_tags);
201*33f37583SAndroid Build Coastguard Worker LOG(VERBOSE) << file_path << " is backed by /dev/" << blockdev
202*33f37583SAndroid Build Coastguard Worker << " and that block device supports queue depth " << nr_tags;
203*33f37583SAndroid Build Coastguard Worker return strtol(nr_tags.c_str(), NULL, 0);
204*33f37583SAndroid Build Coastguard Worker }
205*33f37583SAndroid Build Coastguard Worker
206*33f37583SAndroid Build Coastguard Worker // Set 'nr_requests' of `loop_device_path` equal to the queue depth of
207*33f37583SAndroid Build Coastguard Worker // the block device backing `file_path`.
ConfigureQueueDepth(const std::string & loop_device_path,const std::string & file_path)208*33f37583SAndroid Build Coastguard Worker Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
209*33f37583SAndroid Build Coastguard Worker const std::string& file_path) {
210*33f37583SAndroid Build Coastguard Worker ATRACE_NAME("ConfigureQueueDepth");
211*33f37583SAndroid Build Coastguard Worker if (!StartsWith(loop_device_path, "/dev/")) {
212*33f37583SAndroid Build Coastguard Worker return Error() << "Invalid argument " << loop_device_path;
213*33f37583SAndroid Build Coastguard Worker }
214*33f37583SAndroid Build Coastguard Worker
215*33f37583SAndroid Build Coastguard Worker const std::string loop_device_name = Basename(loop_device_path);
216*33f37583SAndroid Build Coastguard Worker
217*33f37583SAndroid Build Coastguard Worker const std::string sysfs_path =
218*33f37583SAndroid Build Coastguard Worker StringPrintf("/sys/block/%s/queue/nr_requests", loop_device_name.c_str());
219*33f37583SAndroid Build Coastguard Worker std::string cur_nr_requests_str;
220*33f37583SAndroid Build Coastguard Worker if (!ReadFileToString(sysfs_path, &cur_nr_requests_str)) {
221*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to read " << sysfs_path;
222*33f37583SAndroid Build Coastguard Worker }
223*33f37583SAndroid Build Coastguard Worker cur_nr_requests_str = android::base::Trim(cur_nr_requests_str);
224*33f37583SAndroid Build Coastguard Worker uint32_t cur_nr_requests = 0;
225*33f37583SAndroid Build Coastguard Worker if (!ParseUint(cur_nr_requests_str.c_str(), &cur_nr_requests)) {
226*33f37583SAndroid Build Coastguard Worker return Error() << "Failed to parse " << cur_nr_requests_str;
227*33f37583SAndroid Build Coastguard Worker }
228*33f37583SAndroid Build Coastguard Worker
229*33f37583SAndroid Build Coastguard Worker unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
230*33f37583SAndroid Build Coastguard Worker if (sysfs_fd.get() == -1) {
231*33f37583SAndroid Build Coastguard Worker return ErrnoErrorf("Failed to open {}", sysfs_path);
232*33f37583SAndroid Build Coastguard Worker }
233*33f37583SAndroid Build Coastguard Worker
234*33f37583SAndroid Build Coastguard Worker const auto qd = BlockDeviceQueueDepth(file_path);
235*33f37583SAndroid Build Coastguard Worker if (!qd.ok()) {
236*33f37583SAndroid Build Coastguard Worker return qd.error();
237*33f37583SAndroid Build Coastguard Worker }
238*33f37583SAndroid Build Coastguard Worker if (*qd == cur_nr_requests) {
239*33f37583SAndroid Build Coastguard Worker return {};
240*33f37583SAndroid Build Coastguard Worker }
241*33f37583SAndroid Build Coastguard Worker // Only report write failures if reducing the queue depth. Attempts to
242*33f37583SAndroid Build Coastguard Worker // increase the queue depth are rejected by the kernel if no I/O scheduler
243*33f37583SAndroid Build Coastguard Worker // is associated with the request queue.
244*33f37583SAndroid Build Coastguard Worker if (!WriteStringToFd(StringPrintf("%u", *qd), sysfs_fd) &&
245*33f37583SAndroid Build Coastguard Worker *qd < cur_nr_requests) {
246*33f37583SAndroid Build Coastguard Worker return ErrnoErrorf("Failed to write {} to {}", *qd, sysfs_path);
247*33f37583SAndroid Build Coastguard Worker }
248*33f37583SAndroid Build Coastguard Worker return {};
249*33f37583SAndroid Build Coastguard Worker }
250*33f37583SAndroid Build Coastguard Worker
ConfigureReadAhead(const std::string & device_path)251*33f37583SAndroid Build Coastguard Worker Result<void> ConfigureReadAhead(const std::string& device_path) {
252*33f37583SAndroid Build Coastguard Worker ATRACE_NAME("ConfigureReadAhead");
253*33f37583SAndroid Build Coastguard Worker CHECK(StartsWith(device_path, "/dev/"));
254*33f37583SAndroid Build Coastguard Worker std::string device_name = Basename(device_path);
255*33f37583SAndroid Build Coastguard Worker
256*33f37583SAndroid Build Coastguard Worker std::string sysfs_device =
257*33f37583SAndroid Build Coastguard Worker StringPrintf("/sys/block/%s/queue/read_ahead_kb", device_name.c_str());
258*33f37583SAndroid Build Coastguard Worker unique_fd sysfs_fd(open(sysfs_device.c_str(), O_RDWR | O_CLOEXEC));
259*33f37583SAndroid Build Coastguard Worker if (sysfs_fd.get() == -1) {
260*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to open " << sysfs_device;
261*33f37583SAndroid Build Coastguard Worker }
262*33f37583SAndroid Build Coastguard Worker
263*33f37583SAndroid Build Coastguard Worker std::string readAheadKb = std::to_string(
264*33f37583SAndroid Build Coastguard Worker android::sysprop::ApexProperties::loopback_readahead().value_or(kReadAheadKb));
265*33f37583SAndroid Build Coastguard Worker
266*33f37583SAndroid Build Coastguard Worker int ret = TEMP_FAILURE_RETRY(
267*33f37583SAndroid Build Coastguard Worker write(sysfs_fd.get(), readAheadKb.c_str(), readAheadKb.length()));
268*33f37583SAndroid Build Coastguard Worker if (ret < 0) {
269*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to write to " << sysfs_device;
270*33f37583SAndroid Build Coastguard Worker }
271*33f37583SAndroid Build Coastguard Worker
272*33f37583SAndroid Build Coastguard Worker return {};
273*33f37583SAndroid Build Coastguard Worker }
274*33f37583SAndroid Build Coastguard Worker
PreAllocateLoopDevices(size_t num)275*33f37583SAndroid Build Coastguard Worker Result<void> PreAllocateLoopDevices(size_t num) {
276*33f37583SAndroid Build Coastguard Worker Result<void> loop_ready = WaitForFile("/dev/loop-control", 20s);
277*33f37583SAndroid Build Coastguard Worker if (!loop_ready.ok()) {
278*33f37583SAndroid Build Coastguard Worker return loop_ready;
279*33f37583SAndroid Build Coastguard Worker }
280*33f37583SAndroid Build Coastguard Worker unique_fd ctl_fd(
281*33f37583SAndroid Build Coastguard Worker TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
282*33f37583SAndroid Build Coastguard Worker if (ctl_fd.get() == -1) {
283*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to open loop-control";
284*33f37583SAndroid Build Coastguard Worker }
285*33f37583SAndroid Build Coastguard Worker
286*33f37583SAndroid Build Coastguard Worker int new_allocations = 0; // for logging purpose
287*33f37583SAndroid Build Coastguard Worker
288*33f37583SAndroid Build Coastguard Worker // Assumption: loop device ID [0..num) is valid.
289*33f37583SAndroid Build Coastguard Worker // This is because pre-allocation happens during bootstrap.
290*33f37583SAndroid Build Coastguard Worker // Anyway Kernel pre-allocated loop devices
291*33f37583SAndroid Build Coastguard Worker // as many as CONFIG_BLK_DEV_LOOP_MIN_COUNT,
292*33f37583SAndroid Build Coastguard Worker // Within the amount of kernel-pre-allocation,
293*33f37583SAndroid Build Coastguard Worker // LOOP_CTL_ADD will fail with EEXIST
294*33f37583SAndroid Build Coastguard Worker for (size_t id = 0ul, cnt = 0; cnt < num; ++id) {
295*33f37583SAndroid Build Coastguard Worker int ret = ioctl(ctl_fd.get(), LOOP_CTL_ADD, id);
296*33f37583SAndroid Build Coastguard Worker if (ret > 0) {
297*33f37583SAndroid Build Coastguard Worker new_allocations++;
298*33f37583SAndroid Build Coastguard Worker cnt++;
299*33f37583SAndroid Build Coastguard Worker } else if (errno == EEXIST) {
300*33f37583SAndroid Build Coastguard Worker // When LOOP_CTL_ADD failed with EEXIST, it can check
301*33f37583SAndroid Build Coastguard Worker // whether it is already in use.
302*33f37583SAndroid Build Coastguard Worker // Otherwise, the loop devices pre-allocated by the kernel can be used.
303*33f37583SAndroid Build Coastguard Worker std::string loop_device = StringPrintf("/sys/block/loop%zu/loop", id);
304*33f37583SAndroid Build Coastguard Worker if (access(loop_device.c_str(), F_OK) == 0) {
305*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << "Loop device " << id << " already in use";
306*33f37583SAndroid Build Coastguard Worker } else {
307*33f37583SAndroid Build Coastguard Worker cnt++;
308*33f37583SAndroid Build Coastguard Worker }
309*33f37583SAndroid Build Coastguard Worker } else {
310*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed LOOP_CTL_ADD id = " << id;
311*33f37583SAndroid Build Coastguard Worker }
312*33f37583SAndroid Build Coastguard Worker }
313*33f37583SAndroid Build Coastguard Worker
314*33f37583SAndroid Build Coastguard Worker // Don't wait until the dev nodes are actually created, which
315*33f37583SAndroid Build Coastguard Worker // will delay the boot. By simply returing here, the creation of the dev
316*33f37583SAndroid Build Coastguard Worker // nodes will be done in parallel with other boot processes, and we
317*33f37583SAndroid Build Coastguard Worker // just optimistally hope that they are all created when we actually
318*33f37583SAndroid Build Coastguard Worker // access them for activating APEXes. If the dev nodes are not ready
319*33f37583SAndroid Build Coastguard Worker // even then, we wait 50ms and warning message will be printed (see below
320*33f37583SAndroid Build Coastguard Worker // CreateLoopDevice()).
321*33f37583SAndroid Build Coastguard Worker LOG(INFO) << "Found " << (num - new_allocations)
322*33f37583SAndroid Build Coastguard Worker << " idle loopback devices that were "
323*33f37583SAndroid Build Coastguard Worker << "pre-allocated by kernel. Allocated " << new_allocations
324*33f37583SAndroid Build Coastguard Worker << " more.";
325*33f37583SAndroid Build Coastguard Worker return {};
326*33f37583SAndroid Build Coastguard Worker }
327*33f37583SAndroid Build Coastguard Worker
328*33f37583SAndroid Build Coastguard Worker // This is a temporary/empty object for a loop device before the backing file is
329*33f37583SAndroid Build Coastguard Worker // set.
330*33f37583SAndroid Build Coastguard Worker struct EmptyLoopDevice {
331*33f37583SAndroid Build Coastguard Worker unique_fd fd;
332*33f37583SAndroid Build Coastguard Worker std::string name;
ToOwnedandroid::apex::loop::EmptyLoopDevice333*33f37583SAndroid Build Coastguard Worker LoopbackDeviceUniqueFd ToOwned() { return {std::move(fd), std::move(name)}; }
334*33f37583SAndroid Build Coastguard Worker };
335*33f37583SAndroid Build Coastguard Worker
ConfigureLoopDevice(EmptyLoopDevice && inner,const std::string & target,const uint32_t image_offset,const size_t image_size)336*33f37583SAndroid Build Coastguard Worker static Result<LoopbackDeviceUniqueFd> ConfigureLoopDevice(
337*33f37583SAndroid Build Coastguard Worker EmptyLoopDevice&& inner, const std::string& target,
338*33f37583SAndroid Build Coastguard Worker const uint32_t image_offset, const size_t image_size) {
339*33f37583SAndroid Build Coastguard Worker static bool use_loop_configure;
340*33f37583SAndroid Build Coastguard Worker static std::once_flag once_flag;
341*33f37583SAndroid Build Coastguard Worker auto device_fd = inner.fd.get();
342*33f37583SAndroid Build Coastguard Worker std::call_once(once_flag, [&]() {
343*33f37583SAndroid Build Coastguard Worker // LOOP_CONFIGURE is a new ioctl in Linux 5.8 (and backported in Android
344*33f37583SAndroid Build Coastguard Worker // common) that allows atomically configuring a loop device. It is a lot
345*33f37583SAndroid Build Coastguard Worker // faster than the traditional LOOP_SET_FD/LOOP_SET_STATUS64 combo, but
346*33f37583SAndroid Build Coastguard Worker // it may not be available on updating devices, so try once before
347*33f37583SAndroid Build Coastguard Worker // deciding.
348*33f37583SAndroid Build Coastguard Worker struct loop_config config;
349*33f37583SAndroid Build Coastguard Worker memset(&config, 0, sizeof(config));
350*33f37583SAndroid Build Coastguard Worker config.fd = -1;
351*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, LOOP_CONFIGURE, &config) == -1 && errno == EBADF) {
352*33f37583SAndroid Build Coastguard Worker // If the IOCTL exists, it will fail with EBADF for the -1 fd
353*33f37583SAndroid Build Coastguard Worker use_loop_configure = true;
354*33f37583SAndroid Build Coastguard Worker }
355*33f37583SAndroid Build Coastguard Worker });
356*33f37583SAndroid Build Coastguard Worker
357*33f37583SAndroid Build Coastguard Worker /*
358*33f37583SAndroid Build Coastguard Worker * Using O_DIRECT will tell the kernel that we want to use Direct I/O
359*33f37583SAndroid Build Coastguard Worker * on the underlying file, which we want to do to avoid double caching.
360*33f37583SAndroid Build Coastguard Worker * Note that Direct I/O won't be enabled immediately, because the block
361*33f37583SAndroid Build Coastguard Worker * size of the underlying block device may not match the default loop
362*33f37583SAndroid Build Coastguard Worker * device block size (512); when we call LOOP_SET_BLOCK_SIZE below, the
363*33f37583SAndroid Build Coastguard Worker * kernel driver will automatically enable Direct I/O when it sees that
364*33f37583SAndroid Build Coastguard Worker * condition is now met.
365*33f37583SAndroid Build Coastguard Worker */
366*33f37583SAndroid Build Coastguard Worker bool use_buffered_io = false;
367*33f37583SAndroid Build Coastguard Worker unique_fd target_fd(open(target.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT));
368*33f37583SAndroid Build Coastguard Worker if (target_fd.get() == -1) {
369*33f37583SAndroid Build Coastguard Worker struct statfs stbuf;
370*33f37583SAndroid Build Coastguard Worker int saved_errno = errno;
371*33f37583SAndroid Build Coastguard Worker // let's give another try with buffered I/O for EROFS and squashfs
372*33f37583SAndroid Build Coastguard Worker if (statfs(target.c_str(), &stbuf) != 0 ||
373*33f37583SAndroid Build Coastguard Worker (stbuf.f_type != EROFS_SUPER_MAGIC_V1 &&
374*33f37583SAndroid Build Coastguard Worker stbuf.f_type != SQUASHFS_MAGIC &&
375*33f37583SAndroid Build Coastguard Worker stbuf.f_type != OVERLAYFS_SUPER_MAGIC)) {
376*33f37583SAndroid Build Coastguard Worker return Error(saved_errno) << "Failed to open " << target;
377*33f37583SAndroid Build Coastguard Worker }
378*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << "Fallback to buffered I/O for " << target;
379*33f37583SAndroid Build Coastguard Worker use_buffered_io = true;
380*33f37583SAndroid Build Coastguard Worker target_fd.reset(open(target.c_str(), O_RDONLY | O_CLOEXEC));
381*33f37583SAndroid Build Coastguard Worker if (target_fd.get() == -1) {
382*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to open " << target;
383*33f37583SAndroid Build Coastguard Worker }
384*33f37583SAndroid Build Coastguard Worker }
385*33f37583SAndroid Build Coastguard Worker
386*33f37583SAndroid Build Coastguard Worker struct loop_info64 li;
387*33f37583SAndroid Build Coastguard Worker memset(&li, 0, sizeof(li));
388*33f37583SAndroid Build Coastguard Worker strlcpy((char*)li.lo_crypt_name, kApexLoopIdPrefix, LO_NAME_SIZE);
389*33f37583SAndroid Build Coastguard Worker li.lo_offset = image_offset;
390*33f37583SAndroid Build Coastguard Worker li.lo_sizelimit = image_size;
391*33f37583SAndroid Build Coastguard Worker // Automatically free loop device on last close.
392*33f37583SAndroid Build Coastguard Worker li.lo_flags |= LO_FLAGS_AUTOCLEAR;
393*33f37583SAndroid Build Coastguard Worker
394*33f37583SAndroid Build Coastguard Worker if (use_loop_configure) {
395*33f37583SAndroid Build Coastguard Worker struct loop_config config;
396*33f37583SAndroid Build Coastguard Worker memset(&config, 0, sizeof(config));
397*33f37583SAndroid Build Coastguard Worker config.fd = target_fd.get();
398*33f37583SAndroid Build Coastguard Worker config.info = li;
399*33f37583SAndroid Build Coastguard Worker config.block_size = 4096;
400*33f37583SAndroid Build Coastguard Worker if (!use_buffered_io) {
401*33f37583SAndroid Build Coastguard Worker li.lo_flags |= LO_FLAGS_DIRECT_IO;
402*33f37583SAndroid Build Coastguard Worker }
403*33f37583SAndroid Build Coastguard Worker
404*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, LOOP_CONFIGURE, &config) == -1) {
405*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to LOOP_CONFIGURE";
406*33f37583SAndroid Build Coastguard Worker }
407*33f37583SAndroid Build Coastguard Worker
408*33f37583SAndroid Build Coastguard Worker return inner.ToOwned();
409*33f37583SAndroid Build Coastguard Worker } else {
410*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, LOOP_SET_FD, target_fd.get()) == -1) {
411*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to LOOP_SET_FD";
412*33f37583SAndroid Build Coastguard Worker }
413*33f37583SAndroid Build Coastguard Worker // Now, we have a fully-owned loop device.
414*33f37583SAndroid Build Coastguard Worker LoopbackDeviceUniqueFd loop_device = inner.ToOwned();
415*33f37583SAndroid Build Coastguard Worker
416*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, LOOP_SET_STATUS64, &li) == -1) {
417*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to LOOP_SET_STATUS64";
418*33f37583SAndroid Build Coastguard Worker }
419*33f37583SAndroid Build Coastguard Worker
420*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, BLKFLSBUF, 0) == -1) {
421*33f37583SAndroid Build Coastguard Worker // This works around a kernel bug where the following happens.
422*33f37583SAndroid Build Coastguard Worker // 1) The device runs with a value of loop.max_part > 0
423*33f37583SAndroid Build Coastguard Worker // 2) As part of LOOP_SET_FD above, we do a partition scan, which loads
424*33f37583SAndroid Build Coastguard Worker // the first 2 pages of the underlying file into the buffer cache
425*33f37583SAndroid Build Coastguard Worker // 3) When we then change the offset with LOOP_SET_STATUS64, those pages
426*33f37583SAndroid Build Coastguard Worker // are not invalidated from the cache.
427*33f37583SAndroid Build Coastguard Worker // 4) When we try to mount an ext4 filesystem on the loop device, the ext4
428*33f37583SAndroid Build Coastguard Worker // code will try to find a superblock by reading 4k at offset 0; but,
429*33f37583SAndroid Build Coastguard Worker // because we still have the old pages at offset 0 lying in the cache,
430*33f37583SAndroid Build Coastguard Worker // those pages will be returned directly. However, those pages contain
431*33f37583SAndroid Build Coastguard Worker // the data at offset 0 in the underlying file, not at the offset that
432*33f37583SAndroid Build Coastguard Worker // we configured
433*33f37583SAndroid Build Coastguard Worker // 5) the ext4 driver fails to find a superblock in the (wrong) data, and
434*33f37583SAndroid Build Coastguard Worker // fails to mount the filesystem.
435*33f37583SAndroid Build Coastguard Worker //
436*33f37583SAndroid Build Coastguard Worker // To work around this, explicitly flush the block device, which will
437*33f37583SAndroid Build Coastguard Worker // flush the buffer cache and make sure we actually read the data at the
438*33f37583SAndroid Build Coastguard Worker // correct offset.
439*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to flush buffers on the loop device";
440*33f37583SAndroid Build Coastguard Worker }
441*33f37583SAndroid Build Coastguard Worker
442*33f37583SAndroid Build Coastguard Worker // Direct-IO requires the loop device to have the same block size as the
443*33f37583SAndroid Build Coastguard Worker // underlying filesystem.
444*33f37583SAndroid Build Coastguard Worker if (ioctl(device_fd, LOOP_SET_BLOCK_SIZE, 4096) == -1) {
445*33f37583SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to LOOP_SET_BLOCK_SIZE";
446*33f37583SAndroid Build Coastguard Worker }
447*33f37583SAndroid Build Coastguard Worker return loop_device;
448*33f37583SAndroid Build Coastguard Worker }
449*33f37583SAndroid Build Coastguard Worker }
450*33f37583SAndroid Build Coastguard Worker
WaitForLoopDevice(int num)451*33f37583SAndroid Build Coastguard Worker static Result<EmptyLoopDevice> WaitForLoopDevice(int num) {
452*33f37583SAndroid Build Coastguard Worker std::vector<std::string> candidate_devices = {
453*33f37583SAndroid Build Coastguard Worker StringPrintf("/dev/block/loop%d", num),
454*33f37583SAndroid Build Coastguard Worker StringPrintf("/dev/loop%d", num),
455*33f37583SAndroid Build Coastguard Worker };
456*33f37583SAndroid Build Coastguard Worker
457*33f37583SAndroid Build Coastguard Worker // apexd-bootstrap runs in parallel with ueventd to optimize boot time. In
458*33f37583SAndroid Build Coastguard Worker // rare cases apexd would try attempt to mount an apex before ueventd created
459*33f37583SAndroid Build Coastguard Worker // a loop device for it. To work around this we keep polling for loop device
460*33f37583SAndroid Build Coastguard Worker // to be created until ueventd's cold boot sequence is done.
461*33f37583SAndroid Build Coastguard Worker bool cold_boot_done = GetBoolProperty("ro.cold_boot_done", false);
462*33f37583SAndroid Build Coastguard Worker
463*33f37583SAndroid Build Coastguard Worker // Even though the kernel has created the loop device, we still depend on
464*33f37583SAndroid Build Coastguard Worker // ueventd to run to actually create the device node in userspace. To solve
465*33f37583SAndroid Build Coastguard Worker // this properly we should listen on the netlink socket for uevents, or use
466*33f37583SAndroid Build Coastguard Worker // inotify. For now, this will have to do.
467*33f37583SAndroid Build Coastguard Worker size_t attempts =
468*33f37583SAndroid Build Coastguard Worker android::sysprop::ApexProperties::loop_wait_attempts().value_or(3u);
469*33f37583SAndroid Build Coastguard Worker for (size_t i = 0; i != attempts; ++i) {
470*33f37583SAndroid Build Coastguard Worker if (!cold_boot_done) {
471*33f37583SAndroid Build Coastguard Worker cold_boot_done = GetBoolProperty("ro.cold_boot_done", false);
472*33f37583SAndroid Build Coastguard Worker }
473*33f37583SAndroid Build Coastguard Worker for (const auto& device : candidate_devices) {
474*33f37583SAndroid Build Coastguard Worker unique_fd sysfs_fd(open(device.c_str(), O_RDWR | O_CLOEXEC));
475*33f37583SAndroid Build Coastguard Worker if (sysfs_fd.get() != -1) {
476*33f37583SAndroid Build Coastguard Worker return EmptyLoopDevice{std::move(sysfs_fd), std::move(device)};
477*33f37583SAndroid Build Coastguard Worker }
478*33f37583SAndroid Build Coastguard Worker }
479*33f37583SAndroid Build Coastguard Worker PLOG(WARNING) << "Loopback device " << num << " not ready. Waiting 50ms...";
480*33f37583SAndroid Build Coastguard Worker usleep(50000);
481*33f37583SAndroid Build Coastguard Worker if (!cold_boot_done) {
482*33f37583SAndroid Build Coastguard Worker // ueventd hasn't finished cold boot yet, keep trying.
483*33f37583SAndroid Build Coastguard Worker i = 0;
484*33f37583SAndroid Build Coastguard Worker }
485*33f37583SAndroid Build Coastguard Worker }
486*33f37583SAndroid Build Coastguard Worker
487*33f37583SAndroid Build Coastguard Worker return Error() << "Failed to open loopback device " << num;
488*33f37583SAndroid Build Coastguard Worker }
489*33f37583SAndroid Build Coastguard Worker
CreateLoopDevice(const std::string & target,uint32_t image_offset,size_t image_size)490*33f37583SAndroid Build Coastguard Worker static Result<LoopbackDeviceUniqueFd> CreateLoopDevice(
491*33f37583SAndroid Build Coastguard Worker const std::string& target, uint32_t image_offset, size_t image_size) {
492*33f37583SAndroid Build Coastguard Worker ATRACE_NAME("CreateLoopDevice");
493*33f37583SAndroid Build Coastguard Worker
494*33f37583SAndroid Build Coastguard Worker unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC));
495*33f37583SAndroid Build Coastguard Worker if (ctl_fd.get() == -1) {
496*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed to open loop-control";
497*33f37583SAndroid Build Coastguard Worker }
498*33f37583SAndroid Build Coastguard Worker
499*33f37583SAndroid Build Coastguard Worker static std::mutex mtx;
500*33f37583SAndroid Build Coastguard Worker std::lock_guard lock(mtx);
501*33f37583SAndroid Build Coastguard Worker int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
502*33f37583SAndroid Build Coastguard Worker if (num == -1) {
503*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Failed LOOP_CTL_GET_FREE";
504*33f37583SAndroid Build Coastguard Worker }
505*33f37583SAndroid Build Coastguard Worker
506*33f37583SAndroid Build Coastguard Worker auto loop_device = OR_RETURN(WaitForLoopDevice(num));
507*33f37583SAndroid Build Coastguard Worker CHECK_NE(loop_device.fd.get(), -1);
508*33f37583SAndroid Build Coastguard Worker
509*33f37583SAndroid Build Coastguard Worker return ConfigureLoopDevice(std::move(loop_device), target, image_offset,
510*33f37583SAndroid Build Coastguard Worker image_size);
511*33f37583SAndroid Build Coastguard Worker }
512*33f37583SAndroid Build Coastguard Worker
CreateAndConfigureLoopDevice(const std::string & target,uint32_t image_offset,size_t image_size)513*33f37583SAndroid Build Coastguard Worker Result<LoopbackDeviceUniqueFd> CreateAndConfigureLoopDevice(
514*33f37583SAndroid Build Coastguard Worker const std::string& target, uint32_t image_offset, size_t image_size) {
515*33f37583SAndroid Build Coastguard Worker ATRACE_NAME("CreateAndConfigureLoopDevice");
516*33f37583SAndroid Build Coastguard Worker // Do minimal amount of work while holding a mutex. We need it because
517*33f37583SAndroid Build Coastguard Worker // acquiring + configuring a loop device is not atomic. Ideally we should
518*33f37583SAndroid Build Coastguard Worker // pre-acquire all the loop devices in advance, so that when we run APEX
519*33f37583SAndroid Build Coastguard Worker // activation in-parallel, we can do it without holding any lock.
520*33f37583SAndroid Build Coastguard Worker // Unfortunately, this will require some refactoring of how we manage loop
521*33f37583SAndroid Build Coastguard Worker // devices, and probably some new loop-control ioctls, so for the time being
522*33f37583SAndroid Build Coastguard Worker // we just limit the scope that requires locking.
523*33f37583SAndroid Build Coastguard Worker android::base::Timer timer;
524*33f37583SAndroid Build Coastguard Worker Result<LoopbackDeviceUniqueFd> loop_device;
525*33f37583SAndroid Build Coastguard Worker while (timer.duration() < 1s) {
526*33f37583SAndroid Build Coastguard Worker loop_device = CreateLoopDevice(target, image_offset, image_size);
527*33f37583SAndroid Build Coastguard Worker if (loop_device.ok()) {
528*33f37583SAndroid Build Coastguard Worker break;
529*33f37583SAndroid Build Coastguard Worker }
530*33f37583SAndroid Build Coastguard Worker std::this_thread::sleep_for(5ms);
531*33f37583SAndroid Build Coastguard Worker }
532*33f37583SAndroid Build Coastguard Worker
533*33f37583SAndroid Build Coastguard Worker if (!loop_device.ok()) {
534*33f37583SAndroid Build Coastguard Worker return loop_device.error();
535*33f37583SAndroid Build Coastguard Worker }
536*33f37583SAndroid Build Coastguard Worker
537*33f37583SAndroid Build Coastguard Worker Result<void> sched_status = ConfigureScheduler(loop_device->name);
538*33f37583SAndroid Build Coastguard Worker if (!sched_status.ok()) {
539*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << "Configuring I/O scheduler failed: "
540*33f37583SAndroid Build Coastguard Worker << sched_status.error();
541*33f37583SAndroid Build Coastguard Worker }
542*33f37583SAndroid Build Coastguard Worker
543*33f37583SAndroid Build Coastguard Worker Result<void> qd_status = ConfigureQueueDepth(loop_device->name, target);
544*33f37583SAndroid Build Coastguard Worker if (!qd_status.ok()) {
545*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << qd_status.error();
546*33f37583SAndroid Build Coastguard Worker }
547*33f37583SAndroid Build Coastguard Worker
548*33f37583SAndroid Build Coastguard Worker Result<void> read_ahead_status = ConfigureReadAhead(loop_device->name);
549*33f37583SAndroid Build Coastguard Worker if (!read_ahead_status.ok()) {
550*33f37583SAndroid Build Coastguard Worker return read_ahead_status.error();
551*33f37583SAndroid Build Coastguard Worker }
552*33f37583SAndroid Build Coastguard Worker
553*33f37583SAndroid Build Coastguard Worker return loop_device;
554*33f37583SAndroid Build Coastguard Worker }
555*33f37583SAndroid Build Coastguard Worker
DestroyLoopDevice(const std::string & path,const DestroyLoopFn & extra)556*33f37583SAndroid Build Coastguard Worker void DestroyLoopDevice(const std::string& path, const DestroyLoopFn& extra) {
557*33f37583SAndroid Build Coastguard Worker unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
558*33f37583SAndroid Build Coastguard Worker if (fd.get() == -1) {
559*33f37583SAndroid Build Coastguard Worker if (errno != ENOENT) {
560*33f37583SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to open " << path;
561*33f37583SAndroid Build Coastguard Worker }
562*33f37583SAndroid Build Coastguard Worker return;
563*33f37583SAndroid Build Coastguard Worker }
564*33f37583SAndroid Build Coastguard Worker
565*33f37583SAndroid Build Coastguard Worker struct loop_info64 li;
566*33f37583SAndroid Build Coastguard Worker if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
567*33f37583SAndroid Build Coastguard Worker if (errno != ENXIO) {
568*33f37583SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
569*33f37583SAndroid Build Coastguard Worker }
570*33f37583SAndroid Build Coastguard Worker return;
571*33f37583SAndroid Build Coastguard Worker }
572*33f37583SAndroid Build Coastguard Worker
573*33f37583SAndroid Build Coastguard Worker auto id = std::string((char*)li.lo_crypt_name);
574*33f37583SAndroid Build Coastguard Worker if (StartsWith(id, kApexLoopIdPrefix)) {
575*33f37583SAndroid Build Coastguard Worker extra(path, id);
576*33f37583SAndroid Build Coastguard Worker
577*33f37583SAndroid Build Coastguard Worker if (ioctl(fd.get(), LOOP_CLR_FD, 0) < 0) {
578*33f37583SAndroid Build Coastguard Worker PLOG(WARNING) << "Failed to LOOP_CLR_FD " << path;
579*33f37583SAndroid Build Coastguard Worker }
580*33f37583SAndroid Build Coastguard Worker }
581*33f37583SAndroid Build Coastguard Worker }
582*33f37583SAndroid Build Coastguard Worker
583*33f37583SAndroid Build Coastguard Worker } // namespace loop
584*33f37583SAndroid Build Coastguard Worker } // namespace apex
585*33f37583SAndroid Build Coastguard Worker } // namespace android
586