1 /*
2  * Copyright (c) 2022-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 <fs_dm.h>
17 #include "securec.h"
18 #include "beget_ext.h"
19 #include <linux/dm-ioctl.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/sysmacros.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include "ueventd.h"
27 #include "ueventd_socket.h"
28 #include <limits.h>
29 
30 #ifdef __cplusplus
31 #if __cplusplus
32 extern "C" {
33 #endif
34 #endif
35 
36 #define DM_VERSION0 (4)
37 #define DM_VERSION1 (0)
38 #define DM_VERSION2 (0)
39 #define DM_ALIGN_MASK (7)
40 #define DM_ALIGN(x) (((x) + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
41 #define DEVICE_MAPPER_PATH  "/dev/mapper/control"
42 #define DM_DEVICE_PATH_PREFIX  "/dev/block/dm-"
43 
44 #define FS_DM_RETURN_ERR_IF_NULL(__ptr)                 \
45     do {                                                \
46         if ((__ptr) == NULL) {                          \
47             BEGET_LOGE("error, %s is NULL\n", #__ptr); \
48             return -1;                                  \
49         }                                               \
50     } while (0)
51 
InitDmIo(struct dm_ioctl * io,const char * devName)52 static int InitDmIo(struct dm_ioctl *io, const char *devName)
53 {
54     errno_t err;
55 
56     err = memset_s(io, sizeof(*io), 0, sizeof(*io));
57     if (err != EOK) {
58         BEGET_LOGE("memset io, ret=%d", err);
59         return -1;
60     }
61 
62     io->version[0] = DM_VERSION0;
63     io->version[1] = DM_VERSION1;
64     io->version[2] = DM_VERSION2;
65     io->data_size = sizeof(*io);
66     io->data_start = 0;
67 
68     if (devName == NULL) {
69         return 0;
70     }
71 
72     err = strcpy_s(io->name, sizeof(io->name), devName);
73     if (err != EOK) {
74         BEGET_LOGE("cp devName, ret=%d", err);
75         return -1;
76     }
77 
78     return 0;
79 }
80 
CreateDmDevice(int fd,const char * devName)81 static int CreateDmDevice(int fd, const char *devName)
82 {
83     int rc;
84     struct dm_ioctl io;
85 
86     rc = InitDmIo(&io, devName);
87     if (rc != 0) {
88         return rc;
89     }
90 
91     rc = ioctl(fd, DM_DEV_CREATE, &io);
92     if (rc != 0) {
93         BEGET_LOGE("error, DM_DEV_CREATE failed for %s, ret=%d", devName, rc);
94         return rc;
95     }
96 
97     return 0;
98 }
99 
LoadDmDeviceTable(int fd,const char * devName,DmVerityTarget * target,bool needDmVerity)100 static int LoadDmDeviceTable(int fd, const char *devName,
101                              DmVerityTarget *target, bool needDmVerity)
102 {
103     int rc;
104     errno_t err;
105     char *parasBuf = NULL;
106     size_t parasTotalSize;
107     struct dm_ioctl *io = NULL;
108     struct dm_target_spec *ts = NULL;
109     char *paras = NULL;
110 
111     FS_DM_RETURN_ERR_IF_NULL(target);
112     FS_DM_RETURN_ERR_IF_NULL(target->paras);
113 
114     parasTotalSize = DM_ALIGN(sizeof(*io) + sizeof(*ts) + target->paras_len + 1);
115     parasBuf = calloc(1, parasTotalSize);
116     if (parasBuf == NULL) {
117         BEGET_LOGE("error, calloc dm table fail");
118         return -1;
119     }
120 
121     io = (struct dm_ioctl *)parasBuf;
122     ts = (struct dm_target_spec *)(parasBuf + sizeof(*io));
123     paras = parasBuf + sizeof(*io) + sizeof((*ts));
124 
125     do {
126         rc = InitDmIo(io, devName);
127         if (rc != 0) {
128             BEGET_LOGE("error 0x%x, init dm io", rc);
129             break;
130         }
131 
132         io->data_size = parasTotalSize;
133         io->data_start = sizeof(*io);
134         io->target_count = 1;
135         io->flags |= DM_READONLY_FLAG;
136 
137         ts->status = 0;
138         ts->sector_start = target->start;
139         ts->length = target->length;
140         char *targetType = needDmVerity ? "verity" : "linear";
141         err = strcpy_s(ts->target_type, sizeof(ts->target_type), targetType);
142         if (err != EOK) {
143             rc = -1;
144             BEGET_LOGE("error 0x%x, cp target type", err);
145             break;
146         }
147 
148         err = strcpy_s(paras, target->paras_len + 1, target->paras);
149         if (err != EOK) {
150             rc = -1;
151             BEGET_LOGE("error 0x%x, cp target paras", err);
152             break;
153         }
154 
155         ts->next = parasTotalSize - sizeof(*io);
156 
157         rc = ioctl(fd, DM_TABLE_LOAD, io);
158         if (rc != 0) {
159             BEGET_LOGE("error 0x%x, DM_TABLE_LOAD failed for %s", rc, devName);
160             break;
161         }
162     } while (0);
163 
164     free(parasBuf);
165 
166     return rc;
167 }
168 
ActiveDmDevice(int fd,const char * devName)169 static int ActiveDmDevice(int fd, const char *devName)
170 {
171     int rc;
172     struct dm_ioctl io;
173 
174     rc = InitDmIo(&io, devName);
175     if (rc != 0) {
176         return rc;
177     }
178 
179     io.flags |= DM_READONLY_FLAG;
180 
181     rc = ioctl(fd, DM_DEV_SUSPEND, &io);
182     if (rc != 0) {
183         BEGET_LOGE("error, DM_TABLE_SUSPEND for %s, ret=%d", devName, rc);
184         return rc;
185     }
186 
187     return 0;
188 }
189 
GetDmDevPath(int fd,char ** dmDevPath,const char * devName)190 int GetDmDevPath(int fd, char **dmDevPath, const char *devName)
191 {
192     int rc;
193     char *path = NULL;
194     size_t path_len = strlen(DM_DEVICE_PATH_PREFIX) + 32;
195     unsigned int dev_num;
196     struct dm_ioctl io;
197 
198     rc = InitDmIo(&io, devName);
199     if (rc != 0) {
200         BEGET_LOGE("error 0x%x, init Dm io", rc);
201         return rc;
202     }
203 
204     rc = ioctl(fd, DM_DEV_STATUS, &io);
205     if (rc != 0) {
206         BEGET_LOGE("error, DM_DEV_STATUS for %s, ret=%d", devName, rc);
207         return rc;
208     }
209 
210     dev_num = minor(io.dev);
211 
212     path = calloc(1, path_len);
213     if (path == NULL) {
214         BEGET_LOGE("error, alloc dm dev path");
215         return -1;
216     }
217 
218     rc = snprintf_s(path, path_len, path_len - 1, "%s%u", DM_DEVICE_PATH_PREFIX, dev_num);
219     if (rc < 0) {
220         BEGET_LOGE("error 0x%x, cp dm dev path", rc);
221         free(path);
222         return -1;
223     }
224 
225     *dmDevPath = path;
226 
227     return 0;
228 }
229 
FsDmCreateDevice(char ** dmDevPath,const char * devName,DmVerityTarget * target)230 int FsDmCreateDevice(char **dmDevPath, const char *devName, DmVerityTarget *target)
231 {
232     int rc;
233     int fd = -1;
234 
235     fd = open(DEVICE_MAPPER_PATH, O_RDWR | O_CLOEXEC);
236     if (fd < 0) {
237         BEGET_LOGE("error 0x%x, open %s", errno, DEVICE_MAPPER_PATH);
238         return -1;
239     }
240 
241     rc = CreateDmDevice(fd, devName);
242     if (rc != 0) {
243         BEGET_LOGE("error 0x%x, create dm device fail", rc);
244         close(fd);
245         return rc;
246     }
247 
248     rc = LoadDmDeviceTable(fd, devName, target, true);
249     if (rc != 0) {
250         BEGET_LOGE("error 0x%x, load device table fail", rc);
251         close(fd);
252         return rc;
253     }
254 
255     rc = ActiveDmDevice(fd, devName);
256     if (rc != 0) {
257         BEGET_LOGE("error 0x%x, active device fail", rc);
258         close(fd);
259         return rc;
260     }
261 
262     rc = GetDmDevPath(fd, dmDevPath, devName);
263     if (rc != 0) {
264         BEGET_LOGE("error 0x%x, get dm dev fail", rc);
265         close(fd);
266         return rc;
267     }
268     close(fd);
269     return 0;
270 }
271 
FsDmInitDmDev(char * devPath,bool useSocket)272 int FsDmInitDmDev(char *devPath, bool useSocket)
273 {
274     int rc;
275     char dmDevPath[PATH_MAX] = {0};
276     char *devName = NULL;
277 
278     if (devPath == NULL) {
279         BEGET_LOGE("error, devPath is NULL");
280         return -1;
281     }
282 
283     devName = basename(devPath);
284 
285     rc = snprintf_s(&dmDevPath[0], sizeof(dmDevPath), sizeof(dmDevPath) - 1, "%s%s", "/sys/block/", devName);
286     if (rc < 0) {
287         BEGET_LOGE("error 0x%x, format dm dev", rc);
288         return rc;
289     }
290 
291     int ueventSockFd = -1;
292     if (useSocket) {
293         ueventSockFd = UeventdSocketInit();
294         if (ueventSockFd < 0) {
295             BEGET_LOGE("error, Failed to create uevent socket");
296             return -1;
297         }
298     }
299 
300     RetriggerUeventByPath(ueventSockFd, &dmDevPath[0]);
301 
302     close(ueventSockFd);
303 
304     return 0;
305 }
306 
FsDmRemoveDevice(const char * devName)307 int FsDmRemoveDevice(const char *devName)
308 {
309     int rc;
310     int fd = -1;
311     struct dm_ioctl io;
312 
313     fd = open(DEVICE_MAPPER_PATH, O_RDWR | O_CLOEXEC);
314     if (fd < 0) {
315         BEGET_LOGE("error 0x%x, open %s", errno, DEVICE_MAPPER_PATH);
316         return -1;
317     }
318 
319     rc = InitDmIo(&io, devName);
320     if (rc != 0) {
321         close(fd);
322         BEGET_LOGE("error 0x%x, init dm io", rc);
323         return -1;
324     }
325 
326     io.flags |= DM_READONLY_FLAG;
327 
328     rc = ioctl(fd, DM_DEV_REMOVE, &io);
329     if (rc != 0) {
330         close(fd);
331         BEGET_LOGE("error, DM_DEV_REMOVE failed for %s, ret=%d", devName, rc);
332         return -1;
333     }
334 
335     close(fd);
336     return 0;
337 }
338 
DmGetDeviceName(int fd,const char * devName,char * outDevName,const uint64_t outDevNameLen)339 static int DmGetDeviceName(int fd, const char *devName, char *outDevName, const uint64_t outDevNameLen)
340 {
341     int rc;
342     char *path = NULL;
343     size_t pathLen = strlen(DM_DEVICE_PATH_PREFIX) + 32;
344     struct dm_ioctl io;
345     rc = InitDmIo(&io, devName);
346     if (rc != 0) {
347         BEGET_LOGE("init dm io failed");
348         return rc;
349     }
350 
351     rc = ioctl(fd, DM_DEV_STATUS, &io);
352     if (rc != 0) {
353         BEGET_LOGE("get  DM_DEV_STATUS failed");
354         return rc;
355     }
356 
357     int devNum = (io.dev & 0xff) | ((io.dev >> 12) & 0xff00);
358     path = calloc(1, pathLen);
359     if (path == NULL) {
360         BEGET_LOGE("calloc path failed");
361         return rc;
362     }
363 
364     rc = snprintf_s(path, pathLen, pathLen - 1, "%s%d", DM_DEVICE_PATH_PREFIX, devNum);
365     if (rc < 0) {
366         BEGET_LOGE("copy dm path failed");
367         free(path);
368         path = NULL;
369         return rc;
370     }
371 
372     rc = memcpy_s(outDevName, outDevNameLen, path, pathLen);
373     if (rc < 0) {
374         BEGET_LOGE("copy dm path failed");
375     }
376     BEGET_LOGI("dm get device outDevName %s, devNum %d", path, devNum);
377     free(path);
378     path = NULL;
379     return rc;
380 }
381 
FsDmCreateLinearDevice(const char * devName,char * dmBlkName,uint64_t dmBlkNameLen,DmVerityTarget * target)382 int FsDmCreateLinearDevice(const char *devName, char *dmBlkName, uint64_t dmBlkNameLen, DmVerityTarget *target)
383 {
384     int rc;
385     int fd = -1;
386     fd = open(DEVICE_MAPPER_PATH, O_RDWR | O_CLOEXEC);
387     if (fd < 0) {
388         BEGET_LOGE("open mapper path failed");
389         return -1;
390     }
391 
392     rc = CreateDmDevice(fd, devName);
393     if (rc) {
394         BEGET_LOGE("create dm device failed");
395         close(fd);
396         return -1;
397     }
398 
399     rc = DmGetDeviceName(fd, devName, dmBlkName, dmBlkNameLen);
400     if (rc) {
401         BEGET_LOGE("get dm device name failed");
402         close(fd);
403         return -1;
404     }
405 
406     rc = LoadDmDeviceTable(fd, devName, target, false);
407     if (rc) {
408         BEGET_LOGE("load dm device name failed");
409         close(fd);
410         return -1;
411     }
412 
413     rc = ActiveDmDevice(fd, devName);
414     if (rc) {
415         BEGET_LOGE("active dm device name failed");
416         close(fd);
417         return -1;
418     }
419     close(fd);
420     BEGET_LOGE("fs create rofs linear device success, dev is [%s]", devName);
421     return 0;
422 }
423 
424 #ifdef __cplusplus
425 #if __cplusplus
426 }
427 #endif
428 #endif
429