1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "module_loop.h"
17
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <filesystem>
21 #include <libgen.h>
22 #include <mutex>
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <sys/statfs.h>
26 #include <vector>
27 #include <linux/fs.h>
28 #include <linux/loop.h>
29 #include <linux/magic.h>
30
31 #include "module_utils.h"
32 #include "log/log.h"
33 #include "securec.h"
34 #include "string_ex.h"
35
36 namespace OHOS {
37 namespace SysInstaller {
38 namespace Loop {
39 using namespace Updater;
40 using std::string;
41
42 namespace {
43 constexpr const char *LOOP_CTL_PATH = "/dev/loop-control";
44 constexpr const char *BLOCK_DEV_PATH = "/dev/block";
45 constexpr const char *LOOP_PREFIX = "loop";
46 constexpr const char *DEVICE_PREFIX = "/dev/";
47 constexpr const char *SYSTEM_BLOCK_PATH = "/sys/block/";
48 constexpr const char *READ_AHEAD_NAME = "/queue/read_ahead_kb";
49 constexpr const char *READ_AHEAD_KB = "128";
50 constexpr const char *MODULE_LOOP_PREFIX = "module:";
51 constexpr const char *LOOP_DEV_PATH = "/dev/loop";
52 constexpr const char *LOOP_BLOCK_PATH = "/dev/block/loop";
53 const size_t LOOP_DEVICE_RETRY_ATTEMPTS = 6u;
54 const uint32_t LOOP_BLOCK_SIZE = 4096;
55 const std::chrono::milliseconds WAIT_FOR_DEVICE_TIME(50);
56 const std::chrono::seconds WAIT_FOR_LOOP_TIME(50);
57 }
58
MaybeCloseBad() const59 void LoopbackDeviceUniqueFd::MaybeCloseBad() const
60 {
61 if (deviceFd.Get() != -1) {
62 int ret = ioctl(deviceFd.Get(), LOOP_CLR_FD);
63 if (ret < 0) {
64 LOG(ERROR) << "Failed to clear fd for loopback device";
65 }
66 }
67 }
68
PreAllocateLoopDevices(const size_t num)69 bool PreAllocateLoopDevices(const size_t num)
70 {
71 if (!WaitForFile(LOOP_CTL_PATH, WAIT_FOR_LOOP_TIME)) {
72 LOG(ERROR) << "loop-control is not ready";
73 return false;
74 }
75 int fd = open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC);
76 if (fd == -1) {
77 LOG(ERROR) << "Failed to open loop-control";
78 return false;
79 }
80 UniqueFd ctlFd(fd);
81
82 bool found = false;
83 size_t startId = 0;
84 DIR *dir = opendir(BLOCK_DEV_PATH);
85 if (dir == nullptr) {
86 LOG(ERROR) << "Failed to open " << BLOCK_DEV_PATH;
87 return false;
88 }
89 struct dirent *ptr = nullptr;
90 while ((ptr = readdir(dir)) != nullptr) {
91 if (strncmp(ptr->d_name, LOOP_PREFIX, strlen(LOOP_PREFIX)) != 0) {
92 continue;
93 }
94 string idStr = ptr->d_name + strlen(LOOP_PREFIX);
95 int id = 0;
96 if (StrToInt(idStr, id)) {
97 size_t devId = static_cast<size_t>(id);
98 if (startId < devId) {
99 startId = devId;
100 found = true;
101 }
102 }
103 }
104 closedir(dir);
105 if (found) {
106 startId++;
107 }
108 LOG(INFO) << "start id is " << startId;
109
110 for (size_t id = startId; id < num + startId; ++id) {
111 int ret = ioctl(ctlFd.Get(), LOOP_CTL_ADD, id);
112 if (ret < 0 && errno != EEXIST) {
113 LOG(ERROR) << "Failed to add loop device";
114 return false;
115 }
116 }
117 LOG(INFO) << "Pre-allocated " << num << " loopback devices";
118 return true;
119 }
120
ConfigureReadAhead(const string & devicePath)121 bool ConfigureReadAhead(const string &devicePath)
122 {
123 if (!StartsWith(devicePath, DEVICE_PREFIX)) {
124 LOG(ERROR) << "invalid device path " << devicePath;
125 return false;
126 }
127 string path(devicePath);
128 string deviceName = basename(&path[0]);
129 string sysfsDevice = SYSTEM_BLOCK_PATH + deviceName + READ_AHEAD_NAME;
130 string realPath = GetRealPath(sysfsDevice);
131 if (realPath.empty()) {
132 LOG(ERROR) << "invalid device path " << sysfsDevice;
133 return false;
134 }
135 UniqueFd sysfsFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
136 if (sysfsFd.Get() == -1) {
137 LOG(ERROR) << "Failed to open " << realPath;
138 return false;
139 }
140 int writeBytes = write(sysfsFd.Get(), READ_AHEAD_KB, strlen(READ_AHEAD_KB) + 1);
141 if (writeBytes < 0) {
142 LOG(ERROR) << "Failed to write to " << realPath;
143 return false;
144 }
145 return true;
146 }
147
CheckIfSupportLoopConfigure(const int deviceFd)148 bool CheckIfSupportLoopConfigure(const int deviceFd)
149 {
150 #ifdef LOOP_CONFIGURE
151 struct loop_config config;
152 (void)memset_s(&config, sizeof(config), 0, sizeof(config));
153 config.fd = -1;
154 return ioctl(deviceFd, LOOP_CONFIGURE, &config) == -1 && errno == EBADF;
155 #else
156 return false;
157 #endif
158 }
159
ConfigureLoopDevice(const int deviceFd,const int targetFd,struct loop_info64 li,const bool useBufferedIo)160 bool ConfigureLoopDevice(const int deviceFd, const int targetFd, struct loop_info64 li, const bool useBufferedIo)
161 {
162 #ifdef LOOP_CONFIGURE
163 struct loop_config config;
164 (void)memset_s(&config, sizeof(config), 0, sizeof(config));
165 config.fd = targetFd;
166 config.info = li;
167 config.block_size = LOOP_BLOCK_SIZE;
168 if (!useBufferedIo) {
169 li.lo_flags |= LO_FLAGS_DIRECT_IO;
170 }
171 int ret = ioctl(deviceFd, LOOP_CONFIGURE, &config);
172 if (ret < 0) {
173 LOG(ERROR) << "Failed to configure loop device err=" << errno;
174 return false;
175 }
176 return true;
177 #else
178 return false;
179 #endif
180 }
181
SetLoopDeviceStatus(const int deviceFd,const int targetFd,const struct loop_info64 * li)182 bool SetLoopDeviceStatus(const int deviceFd, const int targetFd, const struct loop_info64 *li)
183 {
184 int ret = ioctl(deviceFd, LOOP_SET_FD, targetFd);
185 if (ret < 0) {
186 LOG(ERROR) << "Failed to set loop fd err=" << errno;
187 return false;
188 }
189 ret = ioctl(deviceFd, LOOP_SET_STATUS64, li);
190 if (ret < 0) {
191 LOG(ERROR) << "Failed to set loop status err=" << errno;
192 return false;
193 }
194 ret = ioctl(deviceFd, BLKFLSBUF, 0);
195 if (ret < 0) {
196 LOG(ERROR) << "Failed to flush buffers on the loop device err=" << errno;
197 return false;
198 }
199 ret = ioctl(deviceFd, LOOP_SET_BLOCK_SIZE, LOOP_BLOCK_SIZE);
200 if (ret < 0) {
201 LOG(WARNING) << "Failed to set block size err=" << errno;
202 }
203 return true;
204 }
205
SetUpLoopDevice(const int deviceFd,const string & target,const uint32_t imageOffset,const uint32_t imageSize)206 bool SetUpLoopDevice(const int deviceFd, const string &target, const uint32_t imageOffset, const uint32_t imageSize)
207 {
208 static bool useLoopConfigure = CheckIfSupportLoopConfigure(deviceFd);
209 bool useBufferedIo = false;
210 string realPath = GetRealPath(target);
211 if (realPath.empty()) {
212 LOG(ERROR) << "invalid target " << target;
213 return false;
214 }
215 UniqueFd targetFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT));
216 if (targetFd.Get() == -1) {
217 struct statfs stbuf;
218 int savedErrno = errno;
219 if (statfs(realPath.c_str(), &stbuf) != 0 ||
220 (stbuf.f_type != EROFS_SUPER_MAGIC_V1 &&
221 stbuf.f_type != SQUASHFS_MAGIC &&
222 stbuf.f_type != OVERLAYFS_SUPER_MAGIC)) {
223 LOG(ERROR) << "Failed to open " << realPath << " errno=" << savedErrno;
224 return false;
225 }
226 LOG(WARNING) << "Fallback to buffered I/O for " << realPath;
227 useBufferedIo = true;
228 targetFd = UniqueFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC));
229 if (targetFd.Get() == -1) {
230 LOG(ERROR) << "Failed to open " << realPath;
231 return false;
232 }
233 }
234
235 struct loop_info64 li;
236 (void)memset_s(&li, sizeof(li), 0, sizeof(li));
237 errno_t ret = strcpy_s(reinterpret_cast<char*>(li.lo_crypt_name), LO_NAME_SIZE, MODULE_LOOP_PREFIX);
238 if (ret != EOK) {
239 LOG(ERROR) << "Failed to copy loop prefix " << MODULE_LOOP_PREFIX;
240 return false;
241 }
242 li.lo_offset = imageOffset;
243 li.lo_sizelimit = imageSize;
244 li.lo_flags |= LO_FLAGS_AUTOCLEAR;
245 return useLoopConfigure ? ConfigureLoopDevice(deviceFd, targetFd.Get(), li, useBufferedIo)
246 : SetLoopDeviceStatus(deviceFd, targetFd.Get(), &li);
247 }
248
WaitForDevice(const int num)249 std::unique_ptr<LoopbackDeviceUniqueFd> WaitForDevice(const int num)
250 {
251 const std::vector<string> candidateDevices = {
252 LOOP_BLOCK_PATH + std::to_string(num),
253 LOOP_DEV_PATH + std::to_string(num),
254 };
255
256 UniqueFd sysfsFd;
257 for (size_t i = 0; i < LOOP_DEVICE_RETRY_ATTEMPTS; ++i) {
258 for (const auto &device : candidateDevices) {
259 string realPath = GetRealPath(device);
260 if (realPath.empty()) {
261 continue;
262 }
263 sysfsFd = UniqueFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
264 if (sysfsFd.Get() != -1) {
265 return std::make_unique<LoopbackDeviceUniqueFd>(std::move(sysfsFd), realPath);
266 }
267 }
268 LOG(WARNING) << "Loopback device " << num << " not ready. Waiting 50ms...";
269 usleep(std::chrono::duration_cast<std::chrono::microseconds>(WAIT_FOR_DEVICE_TIME).count());
270 }
271 LOG(ERROR) << "Failed to open loopback device " << num;
272 return nullptr;
273 }
274
CreateLoopDevice(const string & target,const uint32_t imageOffset,const uint32_t imageSize)275 std::unique_ptr<LoopbackDeviceUniqueFd> CreateLoopDevice(
276 const string &target, const uint32_t imageOffset, const uint32_t imageSize)
277 {
278 UniqueFd ctlFd(open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC));
279 if (ctlFd.Get() == -1) {
280 LOG(ERROR) << "Failed to open loop-control";
281 return nullptr;
282 }
283 static std::mutex mlock;
284 std::lock_guard lock(mlock);
285 int num = ioctl(ctlFd.Get(), LOOP_CTL_GET_FREE);
286 if (num < 0) {
287 LOG(ERROR) << "Failed to get free loop device err=" << errno;
288 return nullptr;
289 }
290 LOG(INFO) << "Get free loop device num " << num;
291 std::unique_ptr<LoopbackDeviceUniqueFd> loopDevice = WaitForDevice(num);
292 if (loopDevice == nullptr) {
293 LOG(ERROR) << "Failed to create loop device " << num;
294 return nullptr;
295 }
296 if (!SetUpLoopDevice(loopDevice->deviceFd.Get(), target, imageOffset, imageSize)) {
297 LOG(ERROR) << "Failed to configure device";
298 return nullptr;
299 }
300 if (!ConfigureReadAhead(loopDevice->name)) {
301 LOG(ERROR) << "Failed to configure read ahead";
302 return nullptr;
303 }
304 return loopDevice;
305 }
306 } // Loop
307 } // SysInstaller
308 } // namespace OHOS