]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/blockdev-util.c
Merge pull request #23893 from yuwata/core-mount-re-read-mountinfo
[thirdparty/systemd.git] / src / shared / blockdev-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/blkpg.h>
4 #include <sys/file.h>
5 #include <sys/ioctl.h>
6 #include <unistd.h>
7
8 #include "sd-device.h"
9
10 #include "alloc-util.h"
11 #include "blockdev-util.h"
12 #include "btrfs-util.h"
13 #include "device-util.h"
14 #include "devnum-util.h"
15 #include "dirent-util.h"
16 #include "errno-util.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "missing_magic.h"
20 #include "parse-util.h"
21
22 int block_get_whole_disk(dev_t d, dev_t *ret) {
23 char p[SYS_BLOCK_PATH_MAX("/partition")];
24 _cleanup_free_ char *s = NULL;
25 dev_t devt;
26 int r;
27
28 assert(ret);
29
30 if (major(d) == 0)
31 return -ENODEV;
32
33 /* If it has a queue this is good enough for us */
34 xsprintf_sys_block_path(p, "/queue", d);
35 if (access(p, F_OK) >= 0) {
36 *ret = d;
37 return 0;
38 }
39 if (errno != ENOENT)
40 return -errno;
41
42 /* If it is a partition find the originating device */
43 xsprintf_sys_block_path(p, "/partition", d);
44 if (access(p, F_OK) < 0)
45 return -errno;
46
47 /* Get parent dev_t */
48 xsprintf_sys_block_path(p, "/../dev", d);
49 r = read_one_line_file(p, &s);
50 if (r < 0)
51 return r;
52
53 r = parse_devnum(s, &devt);
54 if (r < 0)
55 return r;
56
57 /* Only return this if it is really good enough for us. */
58 xsprintf_sys_block_path(p, "/queue", devt);
59 if (access(p, F_OK) < 0)
60 return -errno;
61
62 *ret = devt;
63 return 1;
64 }
65
66 int get_block_device_fd(int fd, dev_t *ret) {
67 struct stat st;
68 int r;
69
70 assert(fd >= 0);
71 assert(ret);
72
73 /* Gets the block device directly backing a file system. If the block device is encrypted, returns
74 * the device mapper block device. */
75
76 if (fstat(fd, &st))
77 return -errno;
78
79 if (major(st.st_dev) != 0) {
80 *ret = st.st_dev;
81 return 1;
82 }
83
84 r = btrfs_get_block_device_fd(fd, ret);
85 if (r > 0)
86 return 1;
87 if (r != -ENOTTY) /* not btrfs */
88 return r;
89
90 *ret = 0;
91 return 0;
92 }
93
94 int get_block_device(const char *path, dev_t *ret) {
95 _cleanup_close_ int fd = -1;
96
97 assert(path);
98 assert(ret);
99
100 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
101 if (fd < 0)
102 return -errno;
103
104 return get_block_device_fd(fd, ret);
105 }
106
107 int block_get_originating(dev_t dt, dev_t *ret) {
108 _cleanup_closedir_ DIR *d = NULL;
109 _cleanup_free_ char *t = NULL;
110 char p[SYS_BLOCK_PATH_MAX("/slaves")];
111 _cleanup_free_ char *first_found = NULL;
112 const char *q;
113 dev_t devt;
114 int r;
115
116 /* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
117 * trying to find the next underlying layer. */
118
119 xsprintf_sys_block_path(p, "/slaves", dt);
120 d = opendir(p);
121 if (!d)
122 return -errno;
123
124 FOREACH_DIRENT_ALL(de, d, return -errno) {
125
126 if (dot_or_dot_dot(de->d_name))
127 continue;
128
129 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
130 continue;
131
132 if (first_found) {
133 _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
134
135 /* We found a device backed by multiple other devices. We don't really support
136 * automatic discovery on such setups, with the exception of dm-verity partitions. In
137 * this case there are two backing devices: the data partition and the hash
138 * partition. We are fine with such setups, however, only if both partitions are on
139 * the same physical device. Hence, let's verify this by iterating over every node
140 * in the 'slaves/' directory and comparing them with the first that gets returned by
141 * readdir(), to ensure they all point to the same device. */
142
143 u = path_join(p, de->d_name, "../dev");
144 if (!u)
145 return -ENOMEM;
146
147 v = path_join(p, first_found, "../dev");
148 if (!v)
149 return -ENOMEM;
150
151 r = read_one_line_file(u, &a);
152 if (r < 0)
153 return log_debug_errno(r, "Failed to read %s: %m", u);
154
155 r = read_one_line_file(v, &b);
156 if (r < 0)
157 return log_debug_errno(r, "Failed to read %s: %m", v);
158
159 /* Check if the parent device is the same. If not, then the two backing devices are on
160 * different physical devices, and we don't support that. */
161 if (!streq(a, b))
162 return -ENOTUNIQ;
163 } else {
164 first_found = strdup(de->d_name);
165 if (!first_found)
166 return -ENOMEM;
167 }
168 }
169
170 if (!first_found)
171 return -ENOENT;
172
173 q = strjoina(p, "/", first_found, "/dev");
174
175 r = read_one_line_file(q, &t);
176 if (r < 0)
177 return r;
178
179 r = parse_devnum(t, &devt);
180 if (r < 0)
181 return -EINVAL;
182
183 if (major(devt) == 0)
184 return -ENOENT;
185
186 *ret = devt;
187 return 1;
188 }
189
190 int get_block_device_harder_fd(int fd, dev_t *ret) {
191 int r;
192
193 assert(fd >= 0);
194 assert(ret);
195
196 /* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its
197 * immediate parent, if there is one. */
198
199 r = get_block_device_fd(fd, ret);
200 if (r <= 0)
201 return r;
202
203 r = block_get_originating(*ret, ret);
204 if (r < 0)
205 log_debug_errno(r, "Failed to chase block device, ignoring: %m");
206
207 return 1;
208 }
209
210 int get_block_device_harder(const char *path, dev_t *ret) {
211 _cleanup_close_ int fd = -1;
212
213 assert(path);
214 assert(ret);
215
216 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
217 if (fd < 0)
218 return -errno;
219
220 return get_block_device_harder_fd(fd, ret);
221 }
222
223 int lock_whole_block_device(dev_t devt, int operation) {
224 _cleanup_free_ char *whole_node = NULL;
225 _cleanup_close_ int lock_fd = -1;
226 dev_t whole_devt;
227 int r;
228
229 /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */
230
231 r = block_get_whole_disk(devt, &whole_devt);
232 if (r < 0)
233 return r;
234
235 r = device_path_make_major_minor(S_IFBLK, whole_devt, &whole_node);
236 if (r < 0)
237 return r;
238
239 lock_fd = open(whole_node, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
240 if (lock_fd < 0)
241 return -errno;
242
243 if (flock(lock_fd, operation) < 0)
244 return -errno;
245
246 return TAKE_FD(lock_fd);
247 }
248
249 int blockdev_partscan_enabled(int fd) {
250 _cleanup_free_ char *p = NULL, *buf = NULL;
251 unsigned long long ull;
252 struct stat st;
253 int r;
254
255 /* Checks if partition scanning is correctly enabled on the block device */
256
257 if (fstat(fd, &st) < 0)
258 return -errno;
259
260 if (!S_ISBLK(st.st_mode))
261 return -ENOTBLK;
262
263 if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
264 return -ENOMEM;
265
266 r = read_one_line_file(p, &buf);
267 if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
268 * partition block device, not the whole block device. And that means we have no
269 * partition scanning on for it (we do for its parent, but not for the partition
270 * itself). */
271 return false;
272 if (r < 0)
273 return r;
274
275 r = safe_atollu_full(buf, 16, &ull);
276 if (r < 0)
277 return r;
278
279 #ifndef GENHD_FL_NO_PART_SCAN
280 #define GENHD_FL_NO_PART_SCAN (0x0200)
281 #endif
282
283 return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
284 }
285
286 static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
287 _cleanup_free_ char *p = NULL, *uuids = NULL;
288 _cleanup_closedir_ DIR *d = NULL;
289 int r, found_encrypted = false;
290
291 assert(sysfs_path);
292
293 if (depth_left == 0)
294 return -EINVAL;
295
296 p = path_join(sysfs_path, "dm/uuid");
297 if (!p)
298 return -ENOMEM;
299
300 r = read_one_line_file(p, &uuids);
301 if (r != -ENOENT) {
302 if (r < 0)
303 return r;
304
305 /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
306 if (startswith(uuids, "CRYPT-"))
307 return true;
308 }
309
310 /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
311 * subdir. */
312
313 p = mfree(p);
314 p = path_join(sysfs_path, "slaves");
315 if (!p)
316 return -ENOMEM;
317
318 d = opendir(p);
319 if (!d) {
320 if (errno == ENOENT) /* Doesn't have underlying devices */
321 return false;
322
323 return -errno;
324 }
325
326 for (;;) {
327 _cleanup_free_ char *q = NULL;
328 struct dirent *de;
329
330 errno = 0;
331 de = readdir_no_dot(d);
332 if (!de) {
333 if (errno != 0)
334 return -errno;
335
336 break; /* No more underlying devices */
337 }
338
339 q = path_join(p, de->d_name);
340 if (!q)
341 return -ENOMEM;
342
343 r = blockdev_is_encrypted(q, depth_left - 1);
344 if (r < 0)
345 return r;
346 if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
347 return false;
348
349 found_encrypted = true;
350 }
351
352 return found_encrypted;
353 }
354
355 int fd_is_encrypted(int fd) {
356 char p[SYS_BLOCK_PATH_MAX(NULL)];
357 dev_t devt;
358 int r;
359
360 r = get_block_device_fd(fd, &devt);
361 if (r < 0)
362 return r;
363 if (r == 0) /* doesn't have a block device */
364 return false;
365
366 xsprintf_sys_block_path(p, NULL, devt);
367
368 return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
369 }
370
371 int path_is_encrypted(const char *path) {
372 char p[SYS_BLOCK_PATH_MAX(NULL)];
373 dev_t devt;
374 int r;
375
376 r = get_block_device(path, &devt);
377 if (r < 0)
378 return r;
379 if (r == 0) /* doesn't have a block device */
380 return false;
381
382 xsprintf_sys_block_path(p, NULL, devt);
383
384 return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
385 }
386
387 int fd_get_whole_disk(int fd, bool backing, dev_t *ret) {
388 dev_t devt;
389 struct stat st;
390 int r;
391
392 assert(ret);
393
394 if (fstat(fd, &st) < 0)
395 return -errno;
396
397 if (S_ISBLK(st.st_mode))
398 devt = st.st_rdev;
399 else if (!backing)
400 return -ENOTBLK;
401 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
402 return -ENOTBLK;
403 else if (major(st.st_dev) != 0)
404 devt = st.st_dev;
405 else {
406 _cleanup_close_ int regfd = -1;
407
408 /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
409 * handing, to get the backing device node. */
410
411 regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
412 if (regfd < 0)
413 return regfd;
414
415 r = btrfs_get_block_device_fd(regfd, &devt);
416 if (r == -ENOTTY)
417 return -ENOTBLK;
418 if (r < 0)
419 return r;
420 }
421
422 return block_get_whole_disk(devt, ret);
423 }
424
425 int path_get_whole_disk(const char *path, bool backing, dev_t *ret) {
426 _cleanup_close_ int fd = -1;
427
428 fd = open(path, O_CLOEXEC|O_PATH);
429 if (fd < 0)
430 return -errno;
431
432 return fd_get_whole_disk(fd, backing, ret);
433 }
434
435 int block_device_add_partition(int fd, const char *name, int nr, uint64_t start, uint64_t size) {
436 assert(fd >= 0);
437 assert(name);
438 assert(nr > 0);
439
440 struct blkpg_partition bp = {
441 .pno = nr,
442 .start = start,
443 .length = size,
444 };
445
446 struct blkpg_ioctl_arg ba = {
447 .op = BLKPG_ADD_PARTITION,
448 .data = &bp,
449 .datalen = sizeof(bp),
450 };
451
452 if (strlen(name) >= sizeof(bp.devname))
453 return -EINVAL;
454
455 strcpy(bp.devname, name);
456
457 return RET_NERRNO(ioctl(fd, BLKPG, &ba));
458 }
459
460 int block_device_remove_partition(int fd, const char *name, int nr) {
461 assert(fd >= 0);
462 assert(name);
463 assert(nr > 0);
464
465 struct blkpg_partition bp = {
466 .pno = nr,
467 };
468
469 struct blkpg_ioctl_arg ba = {
470 .op = BLKPG_DEL_PARTITION,
471 .data = &bp,
472 .datalen = sizeof(bp),
473 };
474
475 if (strlen(name) >= sizeof(bp.devname))
476 return -EINVAL;
477
478 strcpy(bp.devname, name);
479
480 return RET_NERRNO(ioctl(fd, BLKPG, &ba));
481 }
482
483 int block_device_remove_all_partitions(int fd) {
484 struct stat stat;
485 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
486 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
487 sd_device *part;
488 int r, k = 0;
489
490 if (fstat(fd, &stat) < 0)
491 return -errno;
492
493 r = sd_device_new_from_devnum(&dev, 'b', stat.st_rdev);
494 if (r < 0)
495 return r;
496
497 r = sd_device_enumerator_new(&e);
498 if (r < 0)
499 return r;
500
501 r = sd_device_enumerator_add_match_parent(e, dev);
502 if (r < 0)
503 return r;
504
505 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
506 if (r < 0)
507 return r;
508
509 r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition");
510 if (r < 0)
511 return r;
512
513 FOREACH_DEVICE(e, part) {
514 const char *v, *devname;
515 int nr;
516
517 r = sd_device_get_devname(part, &devname);
518 if (r < 0)
519 return r;
520
521 r = sd_device_get_property_value(part, "PARTN", &v);
522 if (r < 0)
523 return r;
524
525 r = safe_atoi(v, &nr);
526 if (r < 0)
527 return r;
528
529 r = block_device_remove_partition(fd, devname, nr);
530 if (r == -ENODEV) {
531 log_debug("Kernel removed partition %s before us, ignoring", devname);
532 continue;
533 }
534 if (r < 0) {
535 log_debug_errno(r, "Failed to remove partition %s: %m", devname);
536 k = k ?: r;
537 continue;
538 }
539
540 log_debug("Removed partition %s", devname);
541 }
542
543 return k;
544 }