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 #include <dirent.h>
18 #include <fcntl.h>
19 #include <filesystem>
20 #include <libgen.h>
21 #include <mutex>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/statfs.h>
25 #include <sys/mount.h>
26 #include <sys/types.h>
27 #include <vector>
28 #include <linux/fs.h>
29 #include <linux/loop.h>
30 #include <linux/magic.h>
31 #include <cerrno>
32 #include "securec.h"
33 #include "string_ex.h"
34 #include "log/log.h"
35 #include "module_dm.h"
36 #include "module_utils.h"
37 #include "module_constants.h"
38 #include "sys/sysmacros.h"
39 #include "scope_guard.h"
40 
41 namespace OHOS {
42 namespace SysInstaller {
43 namespace Loop {
44 using namespace Updater;
45 using std::string;
46 
47 namespace {
48 const int LOOP_LENGTH = 4;
49 const int LOOP_CTL_LENGTH = 12;
50 constexpr const char *LOOP_CTL_PATH = "/dev/loop-control";
51 constexpr const char *BLOCK_DEV_PATH = "/dev/block";
52 constexpr const char *LOOP_PREFIX = "loop";
53 constexpr const char *DEVICE_PREFIX = "/dev/";
54 constexpr const char *SYSTEM_BLOCK_PATH = "/sys/block/";
55 constexpr const char *READ_AHEAD_NAME = "/queue/read_ahead_kb";
56 constexpr const char *READ_AHEAD_KB = "128";
57 constexpr const char *MODULE_LOOP_PREFIX = "module:";
58 constexpr const char *LOOP_DEV_PATH = "/dev/loop";
59 constexpr const char *LOOP_BLOCK_PATH = "/dev/block/loop";
60 const size_t LOOP_DEVICE_RETRY_ATTEMPTS = 6u;
61 const uint32_t LOOP_BLOCK_SIZE = 4096;
62 const std::chrono::milliseconds WAIT_FOR_DEVICE_TIME(50);
63 const std::chrono::seconds WAIT_FOR_LOOP_TIME(50);
64 }
65 
IsRealPath(std::string path)66 static bool IsRealPath(std::string path)
67 {
68     char buf[PATH_MAX] = { 0 };
69     if (realpath(path.c_str(), buf) == nullptr) {
70         return false;
71     }
72     std::string tmpPath = buf;
73     return (path == tmpPath);
74 }
75 
MaybeCloseBad() const76 void LoopbackDeviceUniqueFd::MaybeCloseBad() const
77 {
78     if (deviceFd.Get() != -1) {
79         int ret = ioctl(deviceFd.Get(), LOOP_CLR_FD);
80         if (ret < 0) {
81             LOG(ERROR) << "Failed to clear fd for loopback device";
82         }
83     }
84 }
85 
PreAllocateLoopDevices(const size_t num)86 bool PreAllocateLoopDevices(const size_t num)
87 {
88     if (!WaitForFile(LOOP_CTL_PATH, WAIT_FOR_LOOP_TIME)) {
89         LOG(ERROR) << "loop-control is not ready";
90         return false;
91     }
92     int fd = open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC);
93     if (fd == -1) {
94         LOG(ERROR) << "Failed to open loop-control";
95         return false;
96     }
97     UniqueFd ctlFd(fd);
98 
99     bool found = false;
100     size_t startId = 0;
101     DIR *dir = opendir(BLOCK_DEV_PATH);
102     if (dir == nullptr) {
103         LOG(ERROR) << "Failed to open " << BLOCK_DEV_PATH;
104         return false;
105     }
106     struct dirent *ptr = nullptr;
107     while ((ptr = readdir(dir)) != nullptr) {
108         if (strncmp(ptr->d_name, LOOP_PREFIX, strlen(LOOP_PREFIX)) != 0) {
109             continue;
110         }
111         string idStr = ptr->d_name + strlen(LOOP_PREFIX);
112         int id = 0;
113         if (StrToInt(idStr, id)) {
114             size_t devId = static_cast<size_t>(id);
115             if (startId < devId) {
116                 startId = devId;
117                 found = true;
118             }
119         }
120     }
121     closedir(dir);
122     if (found) {
123         startId++;
124     }
125     LOG(INFO) << "start id is " << startId;
126 
127     for (size_t id = startId; id < num + startId; ++id) {
128         int ret = ioctl(ctlFd.Get(), LOOP_CTL_ADD, id);
129         if (ret < 0 && errno != EEXIST) {
130             LOG(ERROR) << "Failed to add loop device";
131             return false;
132         }
133     }
134     LOG(INFO) << "Pre-allocated " << num << " loopback devices";
135     return true;
136 }
137 
ConfigureReadAhead(const string & devicePath)138 bool ConfigureReadAhead(const string &devicePath)
139 {
140     if (!StartsWith(devicePath, DEVICE_PREFIX)) {
141         LOG(ERROR) << "invalid device path " << devicePath;
142         return false;
143     }
144     string path(devicePath);
145     string deviceName = basename(&path[0]);
146     string sysfsDevice = SYSTEM_BLOCK_PATH + deviceName + READ_AHEAD_NAME;
147     string realPath = GetRealPath(sysfsDevice);
148     if (realPath.empty()) {
149         LOG(ERROR) << "invalid device path " << sysfsDevice;
150         return false;
151     }
152 
153     struct stat fileState;
154     if (stat(realPath.c_str(), &fileState) != 0) {
155         LOG(ERROR) << "Fail to Stat file: " << realPath << ", errno=" << errno;
156         return false;
157     }
158     ON_SCOPE_EXIT(recoveryMode) {
159         (void)chmod(realPath.c_str(), fileState.st_mode);
160     };
161     UniqueFd sysfsFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
162     if (sysfsFd.Get() == -1) {
163         // 0644: give permission to write
164         if (chmod(realPath.c_str(), 0644) != 0) {
165             LOG(WARNING) << "Fail to chmod file: " << realPath << ", errno=" << errno;
166         }
167         sysfsFd = UniqueFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
168         if (sysfsFd.Get() == -1) {
169             LOG(ERROR) << "Fail to open file: " << realPath << ", errno=" << errno;
170             return false;
171         }
172     }
173     int writeBytes = write(sysfsFd.Get(), READ_AHEAD_KB, strlen(READ_AHEAD_KB) + 1);
174     if (writeBytes < 0) {
175         LOG(ERROR) << "Failed to write to " << realPath;
176         return false;
177     }
178     return true;
179 }
180 
CheckIfSupportLoopConfigure(const int deviceFd)181 bool CheckIfSupportLoopConfigure(const int deviceFd)
182 {
183 #ifdef LOOP_CONFIGURE
184     struct loop_config config;
185     (void)memset_s(&config, sizeof(config), 0, sizeof(config));
186     config.fd = -1;
187     return ioctl(deviceFd, LOOP_CONFIGURE, &config) == -1 && errno == EBADF;
188 #else
189     return false;
190 #endif
191 }
192 
ConfigureLoopDevice(const int deviceFd,const int targetFd,struct loop_info64 li,const bool useBufferedIo)193 bool ConfigureLoopDevice(const int deviceFd, const int targetFd, struct loop_info64 li, const bool useBufferedIo)
194 {
195 #ifdef LOOP_CONFIGURE
196     struct loop_config config;
197     (void)memset_s(&config, sizeof(config), 0, sizeof(config));
198     config.fd = targetFd;
199     config.info = li;
200     config.block_size = LOOP_BLOCK_SIZE;
201     if (!useBufferedIo) {
202         li.lo_flags |= LO_FLAGS_DIRECT_IO;
203     }
204     int ret = ioctl(deviceFd, LOOP_CONFIGURE, &config);
205     if (ret < 0) {
206         LOG(ERROR) << "Failed to configure loop device err=" << errno;
207         return false;
208     }
209     return true;
210 #else
211     return false;
212 #endif
213 }
214 
SetLoopDeviceStatus(const int deviceFd,const int targetFd,const struct loop_info64 * li)215 bool SetLoopDeviceStatus(const int deviceFd, const int targetFd, const struct loop_info64 *li)
216 {
217     int ret = ioctl(deviceFd, LOOP_SET_FD, targetFd);
218     if (ret < 0) {
219         LOG(ERROR) << "Failed to set loop fd err=" << errno;
220         return false;
221     }
222     ret = ioctl(deviceFd, LOOP_SET_STATUS64, li);
223     if (ret < 0) {
224         LOG(ERROR) << "Failed to set loop status err=" << errno;
225         return false;
226     }
227     ret = ioctl(deviceFd, BLKFLSBUF, 0);
228     if (ret < 0) {
229         LOG(WARNING) << "Failed to flush buffers on the loop device err=" << errno;
230     }
231     ret = ioctl(deviceFd, LOOP_SET_BLOCK_SIZE, LOOP_BLOCK_SIZE);
232     if (ret < 0) {
233         LOG(WARNING) << "Failed to set block size err=" << errno;
234     }
235     return true;
236 }
237 
SetUpLoopDevice(const int deviceFd,const string & target,const uint32_t imageOffset,const uint32_t imageSize)238 bool SetUpLoopDevice(const int deviceFd, const string &target, const uint32_t imageOffset, const uint32_t imageSize)
239 {
240     static bool useLoopConfigure = CheckIfSupportLoopConfigure(deviceFd);
241     bool useBufferedIo = false;
242     string realPath = GetRealPath(target);
243     if (realPath.empty()) {
244         LOG(ERROR) << "invalid target " << target;
245         return false;
246     }
247     UniqueFd targetFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT));
248     if (targetFd.Get() == -1) {
249         struct statfs stbuf;
250         int savedErrno = errno;
251         if (statfs(realPath.c_str(), &stbuf) != 0 ||
252             (stbuf.f_type != EROFS_SUPER_MAGIC_V1 &&
253              stbuf.f_type != SQUASHFS_MAGIC &&
254              stbuf.f_type != OVERLAYFS_SUPER_MAGIC &&
255              stbuf.f_type != EXT4_SUPER_MAGIC)) {
256             LOG(ERROR) << "Failed to open " << realPath << " errno=" << savedErrno;
257             return false;
258         }
259         LOG(WARNING) << "Fallback to buffered I/O for " << realPath;
260         useBufferedIo = true;
261         targetFd = UniqueFd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC));
262         if (targetFd.Get() == -1) {
263             LOG(ERROR) << "Failed to open " << realPath;
264             return false;
265         }
266     }
267 
268     struct loop_info64 li;
269     (void)memset_s(&li, sizeof(li), 0, sizeof(li));
270     errno_t ret = strcpy_s(reinterpret_cast<char*>(li.lo_crypt_name), LO_NAME_SIZE, MODULE_LOOP_PREFIX);
271     if (ret != EOK) {
272         LOG(ERROR) << "Failed to copy loop prefix " << MODULE_LOOP_PREFIX;
273         return false;
274     }
275     li.lo_offset = imageOffset;
276     li.lo_sizelimit = imageSize;
277     li.lo_flags |= LO_FLAGS_AUTOCLEAR;
278     return useLoopConfigure ? ConfigureLoopDevice(deviceFd, targetFd.Get(), li, useBufferedIo)
279         : SetLoopDeviceStatus(deviceFd, targetFd.Get(), &li);
280 }
281 
WaitForDevice(const int num)282 std::unique_ptr<LoopbackDeviceUniqueFd> WaitForDevice(const int num)
283 {
284     const std::vector<string> candidateDevices = {
285         LOOP_BLOCK_PATH + std::to_string(num),
286         LOOP_DEV_PATH + std::to_string(num),
287     };
288 
289     UniqueFd sysfsFd;
290     for (size_t i = 0; i < LOOP_DEVICE_RETRY_ATTEMPTS; ++i) {
291         for (const auto &device : candidateDevices) {
292             string realPath = GetRealPath(device);
293             if (realPath.empty()) {
294                 continue;
295             }
296             sysfsFd = UniqueFd(open(realPath.c_str(), O_RDWR | O_CLOEXEC));
297             if (sysfsFd.Get() != -1) {
298                 return std::make_unique<LoopbackDeviceUniqueFd>(std::move(sysfsFd), realPath);
299             }
300         }
301         LOG(WARNING) << "Loopback device " << num << " not ready. Waiting 50ms...";
302         usleep(std::chrono::duration_cast<std::chrono::microseconds>(WAIT_FOR_DEVICE_TIME).count());
303     }
304     LOG(ERROR) << "Failed to open loopback device " << num;
305     return nullptr;
306 }
307 
CreateLoopDevice(const string & target,const uint32_t imageOffset,const uint32_t imageSize)308 std::unique_ptr<LoopbackDeviceUniqueFd> CreateLoopDevice(
309     const string &target, const uint32_t imageOffset, const uint32_t imageSize)
310 {
311     UniqueFd ctlFd(open(LOOP_CTL_PATH, O_RDWR | O_CLOEXEC));
312     if (ctlFd.Get() == -1) {
313         LOG(ERROR) << "Failed to open loop-control";
314         return nullptr;
315     }
316     static std::mutex mlock;
317     std::lock_guard lock(mlock);
318     int num = ioctl(ctlFd.Get(), LOOP_CTL_GET_FREE);
319     if (num < 0) {
320         LOG(ERROR) << "Failed to get free loop device err=" << errno;
321         return nullptr;
322     }
323     LOG(INFO) << "Get free loop device num " << num;
324     std::unique_ptr<LoopbackDeviceUniqueFd> loopDevice = WaitForDevice(num);
325     if (loopDevice == nullptr) {
326         LOG(ERROR) << "Failed to create loop device " << num;
327         return nullptr;
328     }
329     if (!SetUpLoopDevice(loopDevice->deviceFd.Get(), target, imageOffset, imageSize)) {
330         LOG(ERROR) << "Failed to configure device";
331         return nullptr;
332     }
333     if (!ConfigureReadAhead(loopDevice->name)) {
334         LOG(ERROR) << "Failed to configure read ahead";
335         return nullptr;
336     }
337     return loopDevice;
338 }
339 
RemoveDmLoopDevice(const std::string & mountPoint,const std::string & imagePath)340 bool RemoveDmLoopDevice(const std::string &mountPoint, const std::string &imagePath)
341 {
342     struct dirent *ent = nullptr;
343     DIR *dir = nullptr;
344 
345     if ((dir = opendir(BLOCK_DEV_PATH)) == nullptr) {
346         LOG(ERROR) << "Failed to open loop dir";
347         return false;
348     }
349     bool ret = false;
350     std::string loopDevPath = "";
351     while ((ent = readdir(dir)) != nullptr) {
352         if (strncmp(ent->d_name, "loop", LOOP_LENGTH) || !strncmp(ent->d_name, "loop-control", LOOP_CTL_LENGTH)) {
353             continue;
354         }
355 
356         loopDevPath = std::string(BLOCK_DEV_PATH) + "/" + std::string(ent->d_name);
357         if (!IsRealPath(loopDevPath)) {
358             LOG(ERROR) << "Dev is not exist, loopDevPath=" << loopDevPath;
359             loopDevPath = "";
360             continue;
361         }
362         if (!IsLoopDevMatchedImg(loopDevPath, imagePath)) {
363             loopDevPath = "";
364             continue;
365         }
366         if (umount(mountPoint.c_str()) != 0) {
367             LOG(WARNING) << "Could not umount " << mountPoint << " errno: " << errno;
368         }
369         bool clearDm = (imagePath.find(UPDATE_ACTIVE_DIR) != std::string::npos);
370         ret = ClearDmLoopDevice(loopDevPath, clearDm);
371         break;
372     }
373     closedir(dir);
374     return ret;
375 }
376 
RemoveDmLoopDevice(const std::string & loopDevPath)377 bool RemoveDmLoopDevice(const std::string &loopDevPath)
378 {
379 #ifndef USER_DEBUG_MODE
380     if (!RemoveDmDevice(loopDevPath)) {
381         LOG(ERROR) << "Close dm error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
382         return false;
383     }
384 #endif
385     if (!CloseLoopDev(loopDevPath)) {
386         LOG(ERROR) << "Close loop error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
387         return false;
388     }
389     return true;
390 }
391 
ClearDmLoopDevice(const std::string & loopDevPath,const bool clearDm)392 bool ClearDmLoopDevice(const std::string &loopDevPath, const bool clearDm)
393 {
394 #ifndef USER_DEBUG_MODE
395     if (clearDm) {
396         if (!RemoveDmDevice(loopDevPath)) {
397             LOG(ERROR) << "Close dm error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
398             return false;
399         }
400     }
401 #endif
402     if (!CloseLoopDev(loopDevPath)) {
403         LOG(ERROR) << "Close loop error, loopDevPath=" << loopDevPath.c_str() << ", errno=" << errno;
404         return false;
405     }
406     return true;
407 }
408 
IsLoopDevMatchedImg(const std::string & loopPath,const std::string & imgFilePath)409 bool IsLoopDevMatchedImg(const std::string &loopPath, const std::string &imgFilePath)
410 {
411     struct loop_info64 info;
412     if (memset_s(&info, sizeof(struct loop_info64), 0, sizeof(struct loop_info64)) != EOK) {
413         LOG(ERROR) << "memset_s failed";
414         return false;
415     }
416 
417     int fd = open(loopPath.c_str(), O_RDWR | O_CLOEXEC);
418     if (fd == -1) {
419         LOG(ERROR) << "Open failed, loopPath=" << loopPath.c_str() << ", errno=" << errno;
420         return false;
421     }
422 
423     if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
424         close(fd);
425         return false;
426     }
427     close(fd);
428     return (imgFilePath == std::string(reinterpret_cast<char *>(info.lo_file_name)));
429 }
430 
CloseLoopDev(const std::string & loopPath)431 bool CloseLoopDev(const std::string &loopPath)
432 {
433     struct stat st;
434     if (stat(loopPath.c_str(), &st)) {
435         LOG(INFO) << "Stat error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
436         return false;
437     }
438 
439     int userFd = open(loopPath.c_str(), O_RDWR);
440     if (userFd < 0) {
441         LOG(ERROR) << "Open error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
442         return false;
443     }
444 
445     int ret = ioctl(userFd, LOOP_CLR_FD);
446     close(userFd);
447     if (ret != 0) {
448         LOG(ERROR) << "Clear error, loopPath=" << loopPath.c_str() << ", errno=" << errno;
449         return false;
450     }
451     return true;
452 }
453 } // Loop
454 } // SysInstaller
455 } // namespace OHOS