• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "apexd"
18 
19 #include "apexd_loop.h"
20 
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <linux/fs.h>
24 #include <linux/loop.h>
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/stringprintf.h>
33 #include <android-base/strings.h>
34 
35 #include "apexd_utils.h"
36 #include "string_log.h"
37 
38 using android::base::StartsWith;
39 using android::base::StringPrintf;
40 using android::base::unique_fd;
41 
42 namespace android {
43 namespace apex {
44 namespace loop {
45 
46 static constexpr const char* kApexLoopIdPrefix = "apex:";
47 
48 // 128 kB read-ahead, which we currently use for /system as well
49 static constexpr const char* kReadAheadKb = "128";
50 
51 // TODO(b/122059364): Even though the kernel has created the loop
52 // device, we still depend on ueventd to run to actually create the
53 // device node in userspace. To solve this properly we should listen on
54 // the netlink socket for uevents, or use inotify. For now, this will
55 // have to do.
56 static constexpr size_t kLoopDeviceRetryAttempts = 3u;
57 
MaybeCloseBad()58 void LoopbackDeviceUniqueFd::MaybeCloseBad() {
59   if (device_fd.get() != -1) {
60     // Disassociate any files.
61     if (ioctl(device_fd.get(), LOOP_CLR_FD) == -1) {
62       PLOG(ERROR) << "Unable to clear fd for loopback device";
63     }
64   }
65 }
66 
configureReadAhead(const std::string & device_path)67 Status configureReadAhead(const std::string& device_path) {
68   auto pos = device_path.find("/dev/block/");
69   if (pos != 0) {
70     return Status::Fail(StringLog()
71                         << "Device path does not start with /dev/block.");
72   }
73   pos = device_path.find_last_of('/');
74   std::string device_name = device_path.substr(pos + 1, std::string::npos);
75 
76   std::string sysfs_device =
77       StringPrintf("/sys/block/%s/queue/read_ahead_kb", device_name.c_str());
78   unique_fd sysfs_fd(open(sysfs_device.c_str(), O_RDWR | O_CLOEXEC));
79   if (sysfs_fd.get() == -1) {
80     return Status::Fail(PStringLog() << "Failed to open " << sysfs_device);
81   }
82 
83   int ret = TEMP_FAILURE_RETRY(
84       write(sysfs_fd.get(), kReadAheadKb, strlen(kReadAheadKb) + 1));
85   if (ret < 0) {
86     return Status::Fail(PStringLog() << "Failed to write to " << sysfs_device);
87   }
88 
89   return Status::Success();
90 }
91 
preAllocateLoopDevices(size_t num)92 Status preAllocateLoopDevices(size_t num) {
93   Status loopReady = WaitForFile("/dev/loop-control", 20s);
94   if (!loopReady.Ok()) {
95     return loopReady;
96   }
97   unique_fd ctl_fd(
98       TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
99   if (ctl_fd.get() == -1) {
100     return Status::Fail(PStringLog() << "Failed to open loop-control");
101   }
102 
103   // Assumption: loop device ID [0..num) is valid.
104   // This is because pre-allocation happens during bootstrap.
105   // Anyway Kernel pre-allocated loop devices
106   // as many as CONFIG_BLK_DEV_LOOP_MIN_COUNT,
107   // Within the amount of kernel-pre-allocation,
108   // LOOP_CTL_ADD will fail with EEXIST
109   for (size_t id = 0ul; id < num; ++id) {
110     int ret = ioctl(ctl_fd.get(), LOOP_CTL_ADD, id);
111     if (ret < 0 && errno != EEXIST) {
112       return Status::Fail(PStringLog() << "Failed LOOP_CTL_ADD");
113     }
114   }
115 
116   // Don't wait until the dev nodes are actually created, which
117   // will delay the boot. By simply returing here, the creation of the dev
118   // nodes will be done in parallel with other boot processes, and we
119   // just optimistally hope that they are all created when we actually
120   // access them for activating APEXes. If the dev nodes are not ready
121   // even then, we wait 50ms and warning message will be printed (see below
122   // createLoopDevice()).
123   LOG(INFO) << "Pre-allocated " << num << " loopback devices";
124   return Status::Success();
125 }
126 
createLoopDevice(const std::string & target,const int32_t imageOffset,const size_t imageSize)127 StatusOr<LoopbackDeviceUniqueFd> createLoopDevice(const std::string& target,
128                                                   const int32_t imageOffset,
129                                                   const size_t imageSize) {
130   using Failed = StatusOr<LoopbackDeviceUniqueFd>;
131   unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC));
132   if (ctl_fd.get() == -1) {
133     return Failed::MakeError(PStringLog() << "Failed to open loop-control");
134   }
135 
136   int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
137   if (num == -1) {
138     return Failed::MakeError(PStringLog() << "Failed LOOP_CTL_GET_FREE");
139   }
140 
141   std::string device = StringPrintf("/dev/block/loop%d", num);
142 
143   unique_fd target_fd(open(target.c_str(), O_RDONLY | O_CLOEXEC));
144   if (target_fd.get() == -1) {
145     return Failed::MakeError(PStringLog() << "Failed to open " << target);
146   }
147   LoopbackDeviceUniqueFd device_fd;
148   {
149     // See comment on kLoopDeviceRetryAttempts.
150     unique_fd sysfs_fd;
151     for (size_t i = 0; i != kLoopDeviceRetryAttempts; ++i) {
152       sysfs_fd.reset(open(device.c_str(), O_RDWR | O_CLOEXEC));
153       if (sysfs_fd.get() != -1) {
154         break;
155       }
156       PLOG(WARNING) << "Loopback device " << device
157                     << " not ready. Waiting 50ms...";
158       usleep(50000);
159     }
160     if (sysfs_fd.get() == -1) {
161       return Failed::MakeError(PStringLog() << "Failed to open " << device);
162     }
163     device_fd = LoopbackDeviceUniqueFd(std::move(sysfs_fd), device);
164     CHECK_NE(device_fd.get(), -1);
165   }
166 
167   if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) {
168     return Failed::MakeError(PStringLog() << "Failed to LOOP_SET_FD");
169   }
170 
171   struct loop_info64 li;
172   memset(&li, 0, sizeof(li));
173   strlcpy((char*)li.lo_crypt_name, kApexLoopIdPrefix, LO_NAME_SIZE);
174   li.lo_offset = imageOffset;
175   li.lo_sizelimit = imageSize;
176   if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) {
177     return Failed::MakeError(PStringLog() << "Failed to LOOP_SET_STATUS64");
178   }
179 
180   if (ioctl(device_fd.get(), BLKFLSBUF, 0) == -1) {
181     // This works around a kernel bug where the following happens.
182     // 1) The device runs with a value of loop.max_part > 0
183     // 2) As part of LOOP_SET_FD above, we do a partition scan, which loads
184     //    the first 2 pages of the underlying file into the buffer cache
185     // 3) When we then change the offset with LOOP_SET_STATUS64, those pages
186     //    are not invalidated from the cache.
187     // 4) When we try to mount an ext4 filesystem on the loop device, the ext4
188     //    code will try to find a superblock by reading 4k at offset 0; but,
189     //    because we still have the old pages at offset 0 lying in the cache,
190     //    those pages will be returned directly. However, those pages contain
191     //    the data at offset 0 in the underlying file, not at the offset that
192     //    we configured
193     // 5) the ext4 driver fails to find a superblock in the (wrong) data, and
194     //    fails to mount the filesystem.
195     //
196     // To work around this, explicitly flush the block device, which will flush
197     // the buffer cache and make sure we actually read the data at the correct
198     // offset.
199     return Failed::MakeError(PStringLog()
200                              << "Failed to flush buffers on the loop device");
201   }
202 
203   // Direct-IO requires the loop device to have the same block size as the
204   // underlying filesystem.
205   if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096) == -1) {
206     PLOG(WARNING) << "Failed to LOOP_SET_BLOCK_SIZE";
207   } else {
208     if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1) == -1) {
209       PLOG(WARNING) << "Failed to LOOP_SET_DIRECT_IO";
210       // TODO Eventually we'll want to fail on this; right now we can't because
211       // not all devices have the necessary kernel patches.
212     }
213   }
214 
215   Status readAheadStatus = configureReadAhead(device);
216   if (!readAheadStatus.Ok()) {
217     return Failed::MakeError(StringLog() << readAheadStatus.ErrorMessage());
218   }
219   return StatusOr<LoopbackDeviceUniqueFd>(std::move(device_fd));
220 }
221 
DestroyLoopDevice(const std::string & path,const DestroyLoopFn & extra)222 void DestroyLoopDevice(const std::string& path, const DestroyLoopFn& extra) {
223   unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
224   if (fd.get() == -1) {
225     if (errno != ENOENT) {
226       PLOG(WARNING) << "Failed to open " << path;
227     }
228     return;
229   }
230 
231   struct loop_info64 li;
232   if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
233     if (errno != ENXIO) {
234       PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
235     }
236     return;
237   }
238 
239   auto id = std::string((char*)li.lo_crypt_name);
240   if (StartsWith(id, kApexLoopIdPrefix)) {
241     extra(path, id);
242 
243     if (ioctl(fd.get(), LOOP_CLR_FD, 0) < 0) {
244       PLOG(WARNING) << "Failed to LOOP_CLR_FD " << path;
245     }
246   }
247 }
248 
249 }  // namespace loop
250 }  // namespace apex
251 }  // namespace android
252