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