1 /*
2 * Copyright (c) 2024-2024 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 <sys/mount.h>
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19 #include "securec.h"
20 #include "init_utils.h"
21 #include "fs_dm.h"
22 #include "switch_root.h"
23 #include "fs_manager/fs_manager.h"
24
25 #include "erofs_mount_overlay.h"
26
27 #define BYTE_UNIT 1024
28 #define ALIGN_BLOCK_SIZE (16 * BYTE_UNIT)
29 #define MIN_DM_SIZE (2 * BYTE_UNIT * BYTE_UNIT)
30 #define BLOCK_SIZE_UINT 4096
31 #define EXTHDR_MAGIC 0xFEEDBEEF
32 #define EXTHDR_BLKSIZE 4096
33
34 struct extheader_v1 {
35 uint32_t magic_number;
36 uint16_t exthdr_size;
37 uint16_t bcc16;
38 uint64_t part_size;
39 };
40
AllocDmName(const char * name,char * nameRofs,const uint64_t nameRofsLen,char * nameExt4,const uint64_t nameExt4Len)41 INIT_STATIC void AllocDmName(const char *name, char *nameRofs, const uint64_t nameRofsLen,
42 char *nameExt4, const uint64_t nameExt4Len)
43 {
44 if (snprintf_s(nameRofs, nameRofsLen, nameRofsLen - 1, "%s_erofs", name) < 0) {
45 BEGET_LOGE("Failed to copy nameRofs.");
46 return;
47 }
48
49 if (snprintf_s(nameExt4, nameExt4Len, nameExt4Len - 1, "%s_ext4", name) < 0) {
50 BEGET_LOGE("Failed to copy nameExt4.");
51 return;
52 }
53
54 uint64_t i = 0;
55 while (nameRofs[i] != '\0') {
56 if (nameRofs[i] == '/') {
57 nameRofs[i] = '_';
58 }
59 i++;
60 }
61 i = 0;
62 while (nameExt4[i] != '\0') {
63 if (nameExt4[i] == '/') {
64 nameExt4[i] = '_';
65 }
66 i++;
67 }
68
69 BEGET_LOGI("alloc dm namerofs:[%s], nameext4:[%s]", nameRofs, nameExt4);
70 }
71
LookupErofsEnd(const char * dev)72 INIT_STATIC uint64_t LookupErofsEnd(const char *dev)
73 {
74 int fd = -1;
75 fd = open(dev, O_RDONLY | O_LARGEFILE);
76 if (fd < 0) {
77 BEGET_LOGE("open dev:[%s] failed.", dev);
78 return 0;
79 }
80
81 if (lseek(fd, EROFS_SUPER_BLOCK_START_POSITION, SEEK_SET) < 0) {
82 BEGET_LOGE("lseek dev:[%s] failed.", dev);
83 close(fd);
84 return 0;
85 }
86
87 struct erofs_super_block sb;
88 ssize_t nbytes = read(fd, &sb, sizeof(sb));
89 if (nbytes != sizeof(sb)) {
90 BEGET_LOGE("read dev:[%s] failed.", dev);
91 close(fd);
92 return 0;
93 }
94 close(fd);
95
96 if (sb.magic != EROFS_SUPER_MAGIC) {
97 BEGET_LOGE("dev:[%s] is not erofs system, magic is 0x%x", dev, sb.magic);
98 return 0;
99 }
100
101 uint64_t erofsSize = (uint64_t)sb.blocks * BLOCK_SIZE_UINT;
102 return erofsSize;
103 }
104
GetImgSize(const char * dev,uint64_t offset)105 INIT_STATIC uint64_t GetImgSize(const char *dev, uint64_t offset)
106 {
107 int fd = -1;
108 fd = open(dev, O_RDONLY | O_LARGEFILE);
109 if (fd < 0) {
110 BEGET_LOGE("open dev:[%s] failed.", dev);
111 return 0;
112 }
113
114 if (lseek(fd, offset, SEEK_SET) < 0) {
115 BEGET_LOGE("lseek dev:[%s] failed, offset is %llu", dev, offset);
116 close(fd);
117 return 0;
118 }
119
120 struct extheader_v1 header;
121 ssize_t nbytes = read(fd, &header, sizeof(header));
122 if (nbytes != sizeof(header)) {
123 BEGET_LOGE("read dev:[%s] failed.", dev);
124 close(fd);
125 return 0;
126 }
127 close(fd);
128
129 if (header.magic_number != EXTHDR_MAGIC) {
130 BEGET_LOGI("dev:[%s] is not have ext path, magic is 0x%x", dev, header.magic_number);
131 return 0;
132 }
133 BEGET_LOGI("get img size [%llu]", header.part_size);
134 return header.part_size;
135 }
136
GetFsSize(int fd)137 INIT_STATIC uint64_t GetFsSize(int fd)
138 {
139 struct stat st;
140 if (fstat(fd, &st) == -1) {
141 BEGET_LOGE("fstat failed. errno: %d", errno);
142 return 0;
143 }
144
145 uint64_t size = 0;
146 if (S_ISBLK(st.st_mode)) {
147 if (ioctl(fd, BLKGETSIZE64, &size) == -1) {
148 BEGET_LOGE("ioctl failed. errno: %d", errno);
149 return 0;
150 }
151 } else if (S_ISREG(st.st_mode)) {
152 if (st.st_size < 0) {
153 BEGET_LOGE("st_size is not right. st_size: %lld", st.st_size);
154 return 0;
155 }
156 size = (uint64_t)st.st_size;
157 } else {
158 BEGET_LOGE("unspported type st_mode:[%llu]", st.st_mode);
159 errno = EACCES;
160 return 0;
161 }
162
163 BEGET_LOGI("get fs size:[%llu]", size);
164 return size;
165 }
166
GetBlockSize(const char * dev)167 INIT_STATIC uint64_t GetBlockSize(const char *dev)
168 {
169 int fd = -1;
170 fd = open(dev, O_RDONLY | O_LARGEFILE);
171 if (fd < 0) {
172 BEGET_LOGE("open dev:[%s] failed.", dev);
173 return 0;
174 }
175
176 uint64_t blockSize = GetFsSize(fd);
177 close(fd);
178 return blockSize;
179 }
180
181 /* 字节对齐函数,基于alignment进行字节对齐 */
AlignTo(uint64_t base,uint64_t alignment)182 INIT_STATIC uint64_t AlignTo(uint64_t base, uint64_t alignment)
183 {
184 if (alignment == 0) {
185 return base;
186 }
187 return (((base - 1) / alignment + 1) * alignment);
188 }
189
GetMapperAddr(const char * dev,uint64_t * start,uint64_t * length)190 INIT_STATIC int GetMapperAddr(const char *dev, uint64_t *start, uint64_t *length)
191 {
192 /* 获取EROFS文件系统大小 */
193 *start = LookupErofsEnd(dev);
194 if (*start == 0) {
195 BEGET_LOGE("get erofs end failed.");
196 return -1;
197 }
198
199 /*
200 * 获取镜像大小 当前镜像布局有2种
201 * 老布局:EROFS文件系统 + 全0数据填充 + HVB数据 老布局不存在EXTHEADER,获取到的镜像大小为0。直接基于文件系统切分
202 * 新布局:EROFS文件系统 + EXTHEADER + HVB数据 新布局存在EXTHEADER,基于EXTHEADER获取镜像大小后进行分区切分
203 */
204 uint64_t imgSize = GetImgSize(dev, *start);
205 if (imgSize > 0) {
206 *start = AlignTo(imgSize, ALIGN_BLOCK_SIZE);
207 }
208
209 /* 获取分区大小,老分区布局:分区大小 = 镜像大小 新分区布局:分区大小 = 镜像大小 + 无镜像填充的分区空位 */
210 uint64_t totalSize = GetBlockSize(dev);
211 if (totalSize == 0) {
212 BEGET_LOGE("get block size failed.");
213 return -1;
214 }
215
216 BEGET_LOGI("total size:[%llu], used size: [%llu], empty size:[%llu] on dev: [%s]",
217 totalSize, *start, totalSize - *start, dev);
218
219 if (totalSize > *start) {
220 *length = totalSize - *start;
221 } else {
222 *length = 0;
223 }
224
225 if (*length < MIN_DM_SIZE) {
226 BEGET_LOGE("empty size is too small, skip...");
227 return -1;
228 }
229
230 return 0;
231 }
232
ConstructLinearTarget(DmVerityTarget * target,const char * dev,uint64_t mapStart,uint64_t mapLength)233 INIT_STATIC int ConstructLinearTarget(DmVerityTarget *target, const char *dev, uint64_t mapStart, uint64_t mapLength)
234 {
235 if (target == NULL || dev == NULL) {
236 return -1;
237 }
238
239 target->start = 0;
240 target->length = mapLength / SECTOR_SIZE;
241 target->paras = calloc(1, MAX_BUFFER_LEN);
242 if (target->paras == NULL) {
243 BEGET_LOGE("Failed to calloc target paras");
244 return -1;
245 }
246
247 if (snprintf_s(target->paras, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "%s %llu", dev, mapStart / SECTOR_SIZE) < 0) {
248 BEGET_LOGE("Failed to copy target paras.");
249 return -1;
250 }
251 target->paras_len = strlen(target->paras);
252 BEGET_LOGI("dev [%s], linearparas [%s], length [%s]", dev, target->paras, target->paras_len);
253 return 0;
254 }
255
DestoryLinearTarget(DmVerityTarget * target)256 INIT_STATIC void DestoryLinearTarget(DmVerityTarget *target)
257 {
258 if (target != NULL && target->paras != NULL) {
259 free(target->paras);
260 target->paras = NULL;
261 }
262 }
263
GetOverlayDevice(FstabItem * item,char * devRofs,const uint32_t devRofsLen,char * devExt4,const uint32_t devExt4Len)264 INIT_STATIC int GetOverlayDevice(FstabItem *item, char *devRofs, const uint32_t devRofsLen,
265 char *devExt4, const uint32_t devExt4Len)
266 {
267 uint64_t mapStart;
268 uint64_t mapLength;
269 char nameExt4[MAX_BUFFER_LEN] = {0};
270 char nameRofs[MAX_BUFFER_LEN] = {0};
271
272 if (access(item->deviceName, 0) < 0) {
273 BEGET_LOGE("connot access dev [%s]", item->deviceName);
274 return -1;
275 }
276
277 AllocDmName(item->mountPoint, nameRofs, MAX_BUFFER_LEN, nameExt4, MAX_BUFFER_LEN);
278
279 if (GetMapperAddr(item->deviceName, &mapStart, &mapLength)) {
280 BEGET_LOGE("get mapper addr failed, dev is [%s]", item->deviceName);
281 return -1;
282 }
283
284 DmVerityTarget dmRofsTarget = {0};
285 DmVerityTarget dmExt4Target = {0};
286
287 int rc = ConstructLinearTarget(&dmRofsTarget, item->deviceName, 0, mapStart);
288 if (rc != 0) {
289 BEGET_LOGE("fs construct erofs linear target failed, dev is [%s]", item->deviceName);
290 goto exit;
291 }
292 rc = FsDmCreateLinearDevice(nameRofs, devRofs, devRofsLen, &dmRofsTarget);
293 if (rc != 0) {
294 BEGET_LOGE("fs create erofs linear device failed, dev is [%s]", item->deviceName);
295 goto exit;
296 }
297
298 rc = ConstructLinearTarget(&dmExt4Target, item->deviceName, mapStart, mapLength);
299 if (rc != 0) {
300 BEGET_LOGE("fs construct ext4 linear target failed, dev is [%s]", item->deviceName);
301 goto exit;
302 }
303 rc = FsDmCreateLinearDevice(nameExt4, devExt4, devExt4Len, &dmExt4Target);
304 if (rc != 0) {
305 BEGET_LOGE("fs create ext4 linear device failed, dev is [%s]", item->deviceName);
306 goto exit;
307 }
308 BEGET_LOGI("get overlay device success , dev is [%s]", item->deviceName);
309 exit:
310 DestoryLinearTarget(&dmRofsTarget);
311 DestoryLinearTarget(&dmExt4Target);
312 return rc;
313 }
314
MountRofsDevice(const char * dev,const char * mnt)315 INIT_STATIC int MountRofsDevice(const char *dev, const char *mnt)
316 {
317 int rc = 0;
318 int retryCount = 3;
319 while (retryCount-- > 0) {
320 rc = mount(dev, mnt, "erofs", MS_RDONLY, NULL);
321 if (rc && (errno != EBUSY)) {
322 BEGET_LOGI("mount erofs dev [%s] on mnt [%s] failed, retry", dev, mnt);
323 sleep(1);
324 continue;
325 }
326 break;
327 }
328
329 return 0;
330 }
331
MountExt4Device(const char * dev,const char * mnt,bool isFirstMount)332 int MountExt4Device(const char *dev, const char *mnt, bool isFirstMount)
333 {
334 int ret = 0;
335 char dirExt4[MAX_BUFFER_LEN] = {0};
336 char dirUpper[MAX_BUFFER_LEN] = {0};
337 char dirWork[MAX_BUFFER_LEN] = {0};
338 ret = snprintf_s(dirExt4, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, PREFIX_OVERLAY"%s", mnt);
339 if (ret < 0) {
340 BEGET_LOGE("dirExt4 copy failed errno %d.", errno);
341 return -1;
342 }
343
344 ret = snprintf_s(dirUpper, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, PREFIX_OVERLAY"%s"PREFIX_UPPER, mnt);
345 if (ret < 0) {
346 BEGET_LOGE("dirUpper copy failed errno %d.", errno);
347 return -1;
348 }
349
350 ret = snprintf_s(dirWork, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, PREFIX_OVERLAY"%s"PREFIX_WORK, mnt);
351 if (ret < 0) {
352 BEGET_LOGE("dirWork copy failed errno %d.", errno);
353 return -1;
354 }
355
356 if (mkdir(dirExt4, MODE_MKDIR) && (errno != EEXIST)) {
357 BEGET_LOGE("mkdir %s failed.", dirExt4);
358 return -1;
359 }
360
361 int retryCount = 3;
362 while (retryCount-- > 0) {
363 ret = mount(dev, dirExt4, "ext4", MS_NOATIME | MS_NODEV, NULL);
364 if (ret && (errno != EBUSY)) {
365 BEGET_LOGI("mount ext4 dev [%s] on mnt [%s] failed, retry", dev, dirExt4);
366 sleep(1);
367 continue;
368 }
369 break;
370 }
371
372 if (isFirstMount && mkdir(dirUpper, MODE_MKDIR) && (errno != EEXIST)) {
373 BEGET_LOGE("mkdir dirUpper:%s failed.", dirUpper);
374 return -1;
375 }
376
377 if (isFirstMount && mkdir(dirWork, MODE_MKDIR) && (errno != EEXIST)) {
378 BEGET_LOGE("mkdir dirWork:%s failed.", dirWork);
379 return -1;
380 }
381
382 return ret;
383 }
384
UnlinkMountPoint(const char * mountPoint)385 INIT_STATIC void UnlinkMountPoint(const char *mountPoint)
386 {
387 struct stat statInfo;
388 if (!lstat(mountPoint, &statInfo)) {
389 if ((statInfo.st_mode & S_IFMT) == S_IFLNK) {
390 unlink(mountPoint);
391 }
392 }
393 }
394
MountPartitionDevice(FstabItem * item,const char * devRofs,const char * devExt4)395 INIT_STATIC int MountPartitionDevice(FstabItem *item, const char *devRofs, const char *devExt4)
396 {
397 UnlinkMountPoint(item->mountPoint);
398 if (mkdir(item->mountPoint, MODE_MKDIR) && (errno != EEXIST)) {
399 BEGET_LOGE("mkdir mountPoint:%s failed.errno %d", item->mountPoint, errno);
400 return -1;
401 }
402
403 WaitForFile(devRofs, WAIT_MAX_SECOND);
404 WaitForFile(devExt4, WAIT_MAX_SECOND);
405
406 if (MountRofsDevice(devRofs, item->mountPoint)) {
407 BEGET_LOGE("mount erofs dev [%s] on mnt [%s] failed", devRofs, item->mountPoint);
408 return -1;
409 }
410
411 if (strcmp(item->mountPoint, "/usr") == 0) {
412 SwitchRoot("/usr");
413 }
414
415 if (mkdir(PREFIX_LOWER, MODE_MKDIR) && (errno != EEXIST)) {
416 BEGET_LOGE("mkdir /lower failed. errno: %d", errno);
417 return -1;
418 }
419
420 if (mkdir(PREFIX_OVERLAY, MODE_MKDIR) && (errno != EEXIST)) {
421 BEGET_LOGE("mkdir /overlay failed. errno: %d", errno);
422 return -1;
423 }
424
425 char dirLower[MAX_BUFFER_LEN] = {0};
426 if (snprintf_s(dirLower, MAX_BUFFER_LEN, MAX_BUFFER_LEN - 1, "%s%s", PREFIX_LOWER, item->mountPoint) < 0) {
427 BEGET_LOGE("dirLower[%s]copy failed errno %d.", dirLower, errno);
428 return -1;
429 }
430
431 if (mkdir(dirLower, MODE_MKDIR) && (errno != EEXIST)) {
432 BEGET_LOGE("mkdir dirLower[%s] failed. errno: %d", dirLower, errno);
433 return -1;
434 }
435
436 if (MountRofsDevice(devRofs, dirLower)) {
437 BEGET_LOGE("mount erofs dev [%s] on mnt [%s] failed", devRofs, dirLower);
438 return -1;
439 }
440
441 if (!CheckIsExt4(devExt4, 0)) {
442 BEGET_LOGI("is not ext4 devExt4 [%s] on mnt [%s]", devExt4, item->mountPoint);
443 return 0;
444 }
445
446 BEGET_LOGI("is ext4 devExt4 [%s] on mnt [%s]", devExt4, item->mountPoint);
447 if (MountExt4Device(devExt4, item->mountPoint, false)) {
448 BEGET_LOGE("mount ext4 dev [%s] on mnt [%s] failed", devExt4, item->mountPoint);
449 return -1;
450 }
451
452 return 0;
453 }
454
DoMountOverlayDevice(FstabItem * item)455 int DoMountOverlayDevice(FstabItem *item)
456 {
457 char devRofs[MAX_BUFFER_LEN] = {0};
458 char devExt4[MAX_BUFFER_LEN] = {0};
459 int rc = 0;
460 rc = GetOverlayDevice(item, devRofs, MAX_BUFFER_LEN, devExt4, MAX_BUFFER_LEN);
461 if (rc) {
462 BEGET_LOGE("get overlay device failed, source [%s] target [%s]", item->deviceName, item->mountPoint);
463 return -1;
464 }
465
466 rc = FsDmInitDmDev(devRofs, true);
467 if (rc) {
468 BEGET_LOGE("init erofs dm dev failed");
469 return -1;
470 }
471
472 rc = FsDmInitDmDev(devExt4, true);
473 if (rc) {
474 BEGET_LOGE("init ext4 dm dev failed");
475 return -1;
476 }
477
478 rc = MountPartitionDevice(item, devRofs, devExt4);
479 if (rc) {
480 BEGET_LOGE("init ext4 dm dev failed");
481 return -1;
482 }
483 return rc;
484 }