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/statfs.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/strings.h>
35 
36 #include "apexd_utils.h"
37 #include "string_log.h"
38 
39 using android::base::Error;
40 using android::base::Result;
41 using android::base::StartsWith;
42 using android::base::StringPrintf;
43 using android::base::unique_fd;
44 
45 namespace android {
46 namespace apex {
47 namespace loop {
48 
49 static constexpr const char* kApexLoopIdPrefix = "apex:";
50 
51 // 128 kB read-ahead, which we currently use for /system as well
52 static constexpr const char* kReadAheadKb = "128";
53 
54 // TODO(b/122059364): Even though the kernel has created the loop
55 // device, we still depend on ueventd to run to actually create the
56 // device node in userspace. To solve this properly we should listen on
57 // the netlink socket for uevents, or use inotify. For now, this will
58 // have to do.
59 static constexpr size_t kLoopDeviceRetryAttempts = 3u;
60 
MaybeCloseBad()61 void LoopbackDeviceUniqueFd::MaybeCloseBad() {
62   if (device_fd.get() != -1) {
63     // Disassociate any files.
64     if (ioctl(device_fd.get(), LOOP_CLR_FD) == -1) {
65       PLOG(ERROR) << "Unable to clear fd for loopback device";
66     }
67   }
68 }
69 
configureReadAhead(const std::string & device_path)70 Result<void> configureReadAhead(const std::string& device_path) {
71   auto pos = device_path.find("/dev/block/");
72   if (pos != 0) {
73     return Error() << "Device path does not start with /dev/block.";
74   }
75   pos = device_path.find_last_of('/');
76   std::string device_name = device_path.substr(pos + 1, std::string::npos);
77 
78   std::string sysfs_device =
79       StringPrintf("/sys/block/%s/queue/read_ahead_kb", device_name.c_str());
80   unique_fd sysfs_fd(open(sysfs_device.c_str(), O_RDWR | O_CLOEXEC));
81   if (sysfs_fd.get() == -1) {
82     return ErrnoError() << "Failed to open " << sysfs_device;
83   }
84 
85   int ret = TEMP_FAILURE_RETRY(
86       write(sysfs_fd.get(), kReadAheadKb, strlen(kReadAheadKb) + 1));
87   if (ret < 0) {
88     return ErrnoError() << "Failed to write to " << sysfs_device;
89   }
90 
91   return {};
92 }
93 
preAllocateLoopDevices(size_t num)94 Result<void> preAllocateLoopDevices(size_t num) {
95   Result<void> loopReady = WaitForFile("/dev/loop-control", 20s);
96   if (!loopReady.ok()) {
97     return loopReady;
98   }
99   unique_fd ctl_fd(
100       TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
101   if (ctl_fd.get() == -1) {
102     return ErrnoError() << "Failed to open loop-control";
103   }
104 
105   // Assumption: loop device ID [0..num) is valid.
106   // This is because pre-allocation happens during bootstrap.
107   // Anyway Kernel pre-allocated loop devices
108   // as many as CONFIG_BLK_DEV_LOOP_MIN_COUNT,
109   // Within the amount of kernel-pre-allocation,
110   // LOOP_CTL_ADD will fail with EEXIST
111   for (size_t id = 0ul; id < num; ++id) {
112     int ret = ioctl(ctl_fd.get(), LOOP_CTL_ADD, id);
113     if (ret < 0 && errno != EEXIST) {
114       return ErrnoError() << "Failed LOOP_CTL_ADD";
115     }
116   }
117 
118   // Don't wait until the dev nodes are actually created, which
119   // will delay the boot. By simply returing here, the creation of the dev
120   // nodes will be done in parallel with other boot processes, and we
121   // just optimistally hope that they are all created when we actually
122   // access them for activating APEXes. If the dev nodes are not ready
123   // even then, we wait 50ms and warning message will be printed (see below
124   // createLoopDevice()).
125   LOG(INFO) << "Pre-allocated " << num << " loopback devices";
126   return {};
127 }
128 
createLoopDevice(const std::string & target,const int32_t imageOffset,const size_t imageSize)129 Result<LoopbackDeviceUniqueFd> createLoopDevice(const std::string& target,
130                                                 const int32_t imageOffset,
131                                                 const size_t imageSize) {
132   unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC));
133   if (ctl_fd.get() == -1) {
134     return ErrnoError() << "Failed to open loop-control";
135   }
136 
137   int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE);
138   if (num == -1) {
139     return ErrnoError() << "Failed LOOP_CTL_GET_FREE";
140   }
141 
142   std::string device = StringPrintf("/dev/block/loop%d", num);
143 
144   /*
145    * Using O_DIRECT will tell the kernel that we want to use Direct I/O
146    * on the underlying file, which we want to do to avoid double caching.
147    * Note that Direct I/O won't be enabled immediately, because the block
148    * size of the underlying block device may not match the default loop
149    * device block size (512); when we call LOOP_SET_BLOCK_SIZE below, the
150    * kernel driver will automatically enable Direct I/O when it sees that
151    * condition is now met.
152    */
153   unique_fd target_fd(open(target.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT));
154   if (target_fd.get() == -1) {
155     struct statfs stbuf;
156     int saved_errno = errno;
157     // let's give another try with buffered I/O for EROFS
158     if (statfs(target.c_str(), &stbuf) != 0 ||
159         stbuf.f_type != EROFS_SUPER_MAGIC_V1) {
160         return Error(saved_errno) << "Failed to open " << target;
161     }
162     LOG(WARNING) << "Fallback to buffered I/O for " << target;
163     target_fd.reset(open(target.c_str(), O_RDONLY | O_CLOEXEC));
164     if (target_fd.get() == -1) {
165       return ErrnoError() << "Failed to open " << target;
166     }
167   }
168   LoopbackDeviceUniqueFd device_fd;
169   {
170     // See comment on kLoopDeviceRetryAttempts.
171     unique_fd sysfs_fd;
172     for (size_t i = 0; i != kLoopDeviceRetryAttempts; ++i) {
173       sysfs_fd.reset(open(device.c_str(), O_RDWR | O_CLOEXEC));
174       if (sysfs_fd.get() != -1) {
175         break;
176       }
177       PLOG(WARNING) << "Loopback device " << device
178                     << " not ready. Waiting 50ms...";
179       usleep(50000);
180     }
181     if (sysfs_fd.get() == -1) {
182       return ErrnoError() << "Failed to open " << device;
183     }
184     device_fd = LoopbackDeviceUniqueFd(std::move(sysfs_fd), device);
185     CHECK_NE(device_fd.get(), -1);
186   }
187 
188   if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) {
189     return ErrnoError() << "Failed to LOOP_SET_FD";
190   }
191 
192   struct loop_info64 li;
193   memset(&li, 0, sizeof(li));
194   strlcpy((char*)li.lo_crypt_name, kApexLoopIdPrefix, LO_NAME_SIZE);
195   li.lo_offset = imageOffset;
196   li.lo_sizelimit = imageSize;
197   if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) {
198     return ErrnoError() << "Failed to LOOP_SET_STATUS64";
199   }
200 
201   if (ioctl(device_fd.get(), BLKFLSBUF, 0) == -1) {
202     // This works around a kernel bug where the following happens.
203     // 1) The device runs with a value of loop.max_part > 0
204     // 2) As part of LOOP_SET_FD above, we do a partition scan, which loads
205     //    the first 2 pages of the underlying file into the buffer cache
206     // 3) When we then change the offset with LOOP_SET_STATUS64, those pages
207     //    are not invalidated from the cache.
208     // 4) When we try to mount an ext4 filesystem on the loop device, the ext4
209     //    code will try to find a superblock by reading 4k at offset 0; but,
210     //    because we still have the old pages at offset 0 lying in the cache,
211     //    those pages will be returned directly. However, those pages contain
212     //    the data at offset 0 in the underlying file, not at the offset that
213     //    we configured
214     // 5) the ext4 driver fails to find a superblock in the (wrong) data, and
215     //    fails to mount the filesystem.
216     //
217     // To work around this, explicitly flush the block device, which will flush
218     // the buffer cache and make sure we actually read the data at the correct
219     // offset.
220     return ErrnoError() << "Failed to flush buffers on the loop device";
221   }
222 
223   // Direct-IO requires the loop device to have the same block size as the
224   // underlying filesystem.
225   if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096) == -1) {
226     PLOG(WARNING) << "Failed to LOOP_SET_BLOCK_SIZE";
227   }
228 
229   Result<void> readAheadStatus = configureReadAhead(device);
230   if (!readAheadStatus.ok()) {
231     return readAheadStatus.error();
232   }
233   return device_fd;
234 }
235 
DestroyLoopDevice(const std::string & path,const DestroyLoopFn & extra)236 void DestroyLoopDevice(const std::string& path, const DestroyLoopFn& extra) {
237   unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
238   if (fd.get() == -1) {
239     if (errno != ENOENT) {
240       PLOG(WARNING) << "Failed to open " << path;
241     }
242     return;
243   }
244 
245   struct loop_info64 li;
246   if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
247     if (errno != ENXIO) {
248       PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
249     }
250     return;
251   }
252 
253   auto id = std::string((char*)li.lo_crypt_name);
254   if (StartsWith(id, kApexLoopIdPrefix)) {
255     extra(path, id);
256 
257     if (ioctl(fd.get(), LOOP_CLR_FD, 0) < 0) {
258       PLOG(WARNING) << "Failed to LOOP_CLR_FD " << path;
259     }
260   }
261 }
262 
263 }  // namespace loop
264 }  // namespace apex
265 }  // namespace android
266