960b7a798c1054378324fe5be7589c243f9800e4
[platform/upstream/cryptsetup.git] / src / utils_blockdev.c
1 /*
2  * Linux block devices helpers
3  *
4  * Copyright (C) 2018-2020 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2018-2020 Ondrej Kozina
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "cryptsetup.h"
23 #include <dirent.h>
24 #ifdef HAVE_SYS_SYSMACROS_H
25 # include <sys/sysmacros.h>     /* for major, minor */
26 #endif
27 #include <uuid/uuid.h>
28
29 #define DM_UUID_LEN             129
30 #define DM_BY_ID_PREFIX         "dm-uuid-"
31 #define DM_BY_ID_PREFIX_LEN     8
32 #define DM_UUID_PREFIX          "CRYPT-"
33 #define DM_UUID_PREFIX_LEN      6
34 #define UUID_LEN 37 /* 36 + \0, libuuid ... */
35
36 static int dm_prepare_uuid(const char *type, const char *uuid, char *buf, size_t buflen)
37 {
38         char *ptr, uuid2[UUID_LEN] = {0};
39         uuid_t uu;
40         unsigned i = 0;
41
42         /* Remove '-' chars */
43         if (uuid) {
44                 if (uuid_parse(uuid, uu) < 0) {
45                         log_dbg("Requested UUID %s has invalid format.", uuid);
46                         return 0;
47                 }
48
49                 for (ptr = uuid2, i = 0; i < UUID_LEN; i++)
50                         if (uuid[i] != '-') {
51                                 *ptr = uuid[i];
52                                 ptr++;
53                         }
54         }
55
56         snprintf(buf, buflen, DM_UUID_PREFIX "%s%s%s%s",
57                 type ?: "", type ? "-" : "",
58                 uuid2[0] ? uuid2 : "", uuid2[0] ? "-" : "");
59
60         return 1;
61 }
62
63 /* return number of holders in general, if matched dm_uuid prefix it's returned via dm_name */
64 /* negative value is error */
65 static int lookup_holder_dm_name(const char *dm_uuid, size_t max_len, dev_t devno, char *dm_name, size_t dm_name_length)
66 {
67         struct dirent *entry;
68         char dm_subpath[PATH_MAX], data_dev_dir[PATH_MAX], uuid[max_len];
69         ssize_t s;
70         struct stat st;
71         int dmfd, fd, len, r = 0; /* not found */
72         DIR *dir;
73
74         if (!dm_name || !dm_name_length)
75                 return -EINVAL;
76
77         *dm_name = '\0';
78
79         len = snprintf(data_dev_dir, PATH_MAX, "/sys/dev/block/%u:%u/holders", major(devno), minor(devno));
80         if (len < 0 || len >= PATH_MAX)
81                 return -EINVAL;
82
83         if (!(dir = opendir(data_dev_dir)))
84                 /* map ENOTDIR to ENOENT we'll handle both errors same */
85                 return errno == ENOTDIR ? -ENOENT : -errno;
86
87         while (r != 1 && (entry = readdir(dir))) {
88                 if (entry->d_name[0] == '.' ||
89                     !strncmp(entry->d_name, "..", 2))
90                         continue;
91
92                 /* there's a holder */
93                 r++;
94
95                 /* we already have a dm_name, just count remaining holders */
96                 if (*dm_name != '\0')
97                         continue;
98
99                 len = snprintf(dm_subpath, PATH_MAX, "%s/%s", entry->d_name, "dm");
100                 if (len < 0 || len >= PATH_MAX) {
101                         r = -EINVAL;
102                         break;
103                 }
104
105                 /* looking for dm-X/dm directory, symlinks are fine */
106                 dmfd = openat(dirfd(dir), dm_subpath, O_DIRECTORY | O_RDONLY);
107                 if (dmfd < 0)
108                         continue;
109
110                 fd = openat(dmfd, "uuid", O_RDONLY);
111                 if (fd < 0) {
112                         close(dmfd);
113                         continue;
114                 }
115
116                 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
117                         close(fd);
118                         close(dmfd);
119                         continue;
120                 }
121
122                 /* reads binary data */
123                 s = read_buffer(fd, uuid, max_len - 1);
124                 close(fd);
125                 uuid[s > 0 ? s : 0] = '\0';
126                 if (!strncmp(uuid, dm_uuid, strlen(dm_uuid)))
127                         log_dbg("Found candidate device %s", entry->d_name);
128                 else {
129                         close(dmfd);
130                         continue;
131                 }
132
133                 fd = openat(dmfd, "name", O_RDONLY);
134                 if (fd < 0) {
135                         close(dmfd);
136                         continue;
137                 }
138
139                 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
140                         close(fd);
141                         close(dmfd);
142                         continue;
143                 }
144
145                 /* reads binary data */
146                 s = read_buffer(fd, dm_name, dm_name_length - 1);
147                 close(fd);
148                 close(dmfd);
149                 if (s > 1) {
150                         dm_name[s-1] = '\0';
151                         log_dbg("Found dm device %s", dm_name);
152                 }
153         }
154
155         closedir(dir);
156
157         return r;
158 }
159
160 int tools_lookup_crypt_device(struct crypt_device *cd, const char *type,
161                 const char *data_device_path, char *name, size_t name_length)
162 {
163         int r;
164         char *c;
165         struct stat st;
166         char dev_uuid[DM_UUID_LEN + DM_BY_ID_PREFIX_LEN] = DM_BY_ID_PREFIX;
167
168         if (!dm_prepare_uuid(type, crypt_get_uuid(cd), dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN))
169                 return -EINVAL;
170
171         c = strrchr(dev_uuid, '-');
172         if (!c)
173                 return -EINVAL;
174
175         /* cut of dm name */
176         *c = '\0';
177
178         log_dbg("Looking for any dm device with prefix: %s", dev_uuid);
179
180         if (stat(data_device_path, &st) < 0)
181                 return -ENODEV;
182
183         if (!S_ISBLK(st.st_mode))
184                 return -ENOTBLK;
185
186         r = lookup_holder_dm_name(dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN,
187                         st.st_rdev, name, name_length);
188         return r;
189 }