• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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