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 }