ae6dec4979f3b667177dccd6ae4b4a378a0a49b8
[platform/upstream/cryptsetup.git] / src / utils_blockdev.c
1 /*
2  * Linux block devices helpers
3  *
4  * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2018-2023 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 UUID_LEN 37 /* 36 + \0, libuuid ... */
30
31 static int dm_prepare_uuid(const char *type, const char *uuid, char *buf, size_t buflen)
32 {
33         char *ptr, uuid2[UUID_LEN] = {0};
34         uuid_t uu;
35         unsigned i = 0;
36
37         /* Remove '-' chars */
38         if (uuid) {
39                 if (uuid_parse(uuid, uu) < 0) {
40                         log_dbg("Requested UUID %s has invalid format.", uuid);
41                         return 0;
42                 }
43
44                 for (ptr = uuid2, i = 0; i < UUID_LEN; i++)
45                         if (uuid[i] != '-') {
46                                 *ptr = uuid[i];
47                                 ptr++;
48                         }
49         }
50
51         if (snprintf(buf, buflen, DM_UUID_PREFIX "%s%s%s%s",
52             type ?: "", type ? "-" : "",
53             uuid2[0] ? uuid2 : "", uuid2[0] ? "-" : "") < 0)
54                 return 0;
55
56         return 1;
57 }
58
59 /* return number of holders in general, if matched dm_uuid prefix it's returned via dm_name */
60 /* negative value is error */
61 static int lookup_holder_dm_name(const char *dm_uuid, dev_t devno, char **r_dm_name)
62 {
63         struct dirent *entry;
64         char dm_subpath[PATH_MAX], data_dev_dir[PATH_MAX], uuid[DM_UUID_LEN], dm_name[PATH_MAX] = {};
65         ssize_t s;
66         struct stat st;
67         int dmfd, fd, len, r = 0; /* not found */
68         DIR *dir;
69
70         if (!r_dm_name)
71                 return -EINVAL;
72
73         len = snprintf(data_dev_dir, PATH_MAX, "/sys/dev/block/%u:%u/holders", major(devno), minor(devno));
74         if (len < 0 || len >= PATH_MAX)
75                 return -EINVAL;
76
77         if (!(dir = opendir(data_dev_dir)))
78                 /* map ENOTDIR to ENOENT we'll handle both errors same */
79                 return errno == ENOTDIR ? -ENOENT : -errno;
80
81         while (r != 1 && (entry = readdir(dir))) {
82                 if (entry->d_name[0] == '.' ||
83                     !strncmp(entry->d_name, "..", 2))
84                         continue;
85
86                 /* there's a holder */
87                 r++;
88
89                 /* we already have a dm_name, just count remaining holders */
90                 if (*dm_name != '\0')
91                         continue;
92
93                 len = snprintf(dm_subpath, PATH_MAX, "%s/%s", entry->d_name, "dm");
94                 if (len < 0 || len >= PATH_MAX) {
95                         r = -EINVAL;
96                         break;
97                 }
98
99                 /* looking for dm-X/dm directory, symlinks are fine */
100                 dmfd = openat(dirfd(dir), dm_subpath, O_DIRECTORY | O_RDONLY);
101                 if (dmfd < 0)
102                         continue;
103
104                 fd = openat(dmfd, "uuid", O_RDONLY);
105                 if (fd < 0) {
106                         close(dmfd);
107                         continue;
108                 }
109
110                 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
111                         close(fd);
112                         close(dmfd);
113                         continue;
114                 }
115
116                 /* reads binary data */
117                 s = read_buffer(fd, uuid, sizeof(uuid) - 1);
118                 close(fd);
119                 uuid[s > 0 ? s : 0] = '\0';
120                 if (!strncmp(uuid, dm_uuid, strlen(dm_uuid)))
121                         log_dbg("Found candidate device %s", entry->d_name);
122                 else {
123                         close(dmfd);
124                         continue;
125                 }
126
127                 fd = openat(dmfd, "name", O_RDONLY);
128                 if (fd < 0) {
129                         close(dmfd);
130                         continue;
131                 }
132
133                 if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
134                         close(fd);
135                         close(dmfd);
136                         continue;
137                 }
138
139                 /* reads binary data */
140                 s = read_buffer(fd, dm_name, sizeof(dm_name));
141                 close(fd);
142                 close(dmfd);
143                 if (s > 1) {
144                         dm_name[s-1] = '\0';
145                         log_dbg("Found dm device %s", dm_name);
146                         if (!(*r_dm_name = strdup(dm_name)))
147                                 return -ENOMEM;
148                 }
149         }
150
151         closedir(dir);
152
153         return r;
154 }
155
156 int tools_lookup_crypt_device(struct crypt_device *cd, const char *type,
157                 const char *data_device_path, char **r_name)
158 {
159         char *c;
160         struct stat st;
161         char dev_uuid[DM_UUID_LEN + DM_BY_ID_PREFIX_LEN] = DM_BY_ID_PREFIX;
162
163         if (!dm_prepare_uuid(type, crypt_get_uuid(cd), dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN))
164                 return -EINVAL;
165
166         c = strrchr(dev_uuid, '-');
167         if (!c)
168                 return -EINVAL;
169
170         /* cut of dm name */
171         *c = '\0';
172
173         log_dbg("Looking for any dm device with prefix: %s", dev_uuid);
174
175         if (stat(data_device_path, &st) < 0)
176                 return -ENODEV;
177
178         if (!S_ISBLK(st.st_mode))
179                 return -ENOTBLK;
180
181         return lookup_holder_dm_name(dev_uuid + DM_BY_ID_PREFIX_LEN, st.st_rdev, r_name);
182 }
183
184 static void report_partition(const char *value, const char *device, bool batch_mode)
185 {
186         if (batch_mode)
187                 log_dbg("Device %s already contains a '%s' partition signature.", device, value);
188         else
189                 log_std(_("WARNING: Device %s already contains a '%s' partition signature.\n"), device, value);
190 }
191
192 static void report_superblock(const char *value, const char *device, bool batch_mode)
193 {
194         if (batch_mode)
195                 log_dbg("Device %s already contains a '%s' superblock signature.", device, value);
196         else
197                 log_std(_("WARNING: Device %s already contains a '%s' superblock signature.\n"), device, value);
198 }
199
200 int tools_detect_signatures(const char *device, tools_probe_filter_info filter,
201                 size_t *count,bool batch_mode)
202 {
203         int r;
204         size_t tmp_count;
205         struct blkid_handle *h;
206         blk_probe_status pr;
207
208         if (!count)
209                 count = &tmp_count;
210
211         *count = 0;
212
213         if (!blk_supported()) {
214                 log_dbg("Blkid support disabled.");
215                 return 0;
216         }
217
218         if ((r = blk_init_by_path(&h, device))) {
219                 log_err(_("Failed to initialize device signature probes."));
220                 return -EINVAL;
221         }
222
223         switch (filter) {
224         case PRB_FILTER_LUKS:
225                 if (blk_superblocks_filter_luks(h)) {
226                         r = -EINVAL;
227                         goto out;
228                 }
229                 /* fall-through */
230         case PRB_FILTER_NONE:
231                 blk_set_chains_for_full_print(h);
232                 break;
233         case PRB_ONLY_LUKS:
234                 blk_set_chains_for_fast_detection(h);
235                 if (blk_superblocks_only_luks(h)) {
236                         r = -EINVAL;
237                         goto out;
238                 }
239         }
240
241         while ((pr = blk_probe(h)) < PRB_EMPTY) {
242                 if (blk_is_partition(h))
243                         report_partition(blk_get_partition_type(h), device, batch_mode);
244                 else if (blk_is_superblock(h))
245                         report_superblock(blk_get_superblock_type(h), device, batch_mode);
246                 else {
247                         log_dbg("Internal tools_detect_signatures() error.");
248                         r = -EINVAL;
249                         goto out;
250                 }
251                 (*count)++;
252         }
253
254         if (pr == PRB_FAIL)
255                 r = -EINVAL;
256 out:
257         blk_free(h);
258         return r;
259 }
260
261 int tools_wipe_all_signatures(const char *path, bool exclusive, bool only_luks)
262 {
263         int fd, flags, r;
264         blk_probe_status pr;
265         struct stat st;
266         struct blkid_handle *h = NULL;
267
268         if (!blk_supported()) {
269                 log_dbg("Blkid support disabled.");
270                 return 0;
271         }
272
273         if (stat(path, &st)) {
274                 log_err(_("Failed to stat device %s."), path);
275                 return -EINVAL;
276         }
277
278         flags = O_RDWR;
279         if (S_ISBLK(st.st_mode) && exclusive)
280                 flags |= O_EXCL;
281
282         /* better than opening regular file with O_EXCL (undefined) */
283         /* coverity[toctou] */
284         fd = open(path, flags); /* lgtm[cpp/toctou-race-condition] */
285         if (fd < 0) {
286                 if (errno == EBUSY)
287                         log_err(_("Cannot exclusively open %s, device in use."), path);
288                 else
289                         log_err(_("Failed to open file %s in read/write mode."), path);
290                 return -EINVAL;
291         }
292
293         if ((r = blk_init_by_fd(&h, fd))) {
294                 log_err(_("Failed to initialize device signature probes."));
295                 r = -EINVAL;
296                 goto out;
297         }
298
299         blk_set_chains_for_wipes(h);
300         if (only_luks && (r = blk_superblocks_only_luks(h))) {
301                 r = -EINVAL;
302                 goto out;
303         }
304
305         while ((pr = blk_probe(h)) < PRB_EMPTY) {
306                 if (blk_is_partition(h))
307                         log_verbose(_("Existing '%s' partition signature on device %s will be wiped."),
308                                     blk_get_partition_type(h), path);
309                 if (blk_is_superblock(h))
310                         log_verbose(_("Existing '%s' superblock signature on device %s will be wiped."),
311                                     blk_get_superblock_type(h), path);
312                 if (blk_do_wipe(h) || fsync(fd)) {
313                         log_err(_("Failed to wipe device signature."));
314                         r = -EINVAL;
315                         goto out;
316                 }
317         }
318
319         if (pr != PRB_EMPTY) {
320                 log_err(_("Failed to probe device %s for a signature."), path);
321                 r = -EINVAL;
322         }
323 out:
324         close(fd);
325         blk_free(h);
326         return r;
327 }
328
329 int tools_superblock_block_size(const char *device, char *sb_name, size_t sb_name_len, unsigned *r_block_size)
330 {
331         struct blkid_handle *h;
332         const char *name;
333         int r = 0;
334
335         if (!r_block_size || !sb_name || sb_name_len < 1)
336                 return -EINVAL;
337
338         if (!blk_supported()) {
339                 log_dbg("Blkid support disabled.");
340                 return 0;
341         }
342
343         if ((r = blk_init_by_path(&h, device))) {
344                 log_err(_("Failed to initialize device signature probes."));
345                 return -EINVAL;
346         }
347
348         blk_set_chains_for_superblocks(h);
349
350         switch (blk_probe(h)) {
351         case PRB_OK:
352                 *r_block_size = blk_get_block_size(h);
353                 if (!*r_block_size) /* same as not-found */
354                         break;
355
356                 if (!(name = blk_get_superblock_type(h))) {
357                         r = -EINVAL;
358                         break;
359                 }
360
361                 /* we don't mind truncating */
362                 strncpy(sb_name, name, sb_name_len - 1);
363                 sb_name[sb_name_len-1] = '\0';
364
365                 log_dbg("Detected superblock %s on device %s (block size: %u).", sb_name, device, *r_block_size);
366                 r = 1;
367                 /* fall-through */
368         case PRB_EMPTY:
369                 break;
370         default:
371                 r = -EINVAL;
372         }
373
374         blk_free(h);
375
376         return r;
377 }
378
379 bool tools_blkid_supported(void)
380 {
381         return blk_supported() != 0;
382 }