]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/blockdev-util.c
Merge pull request #30513 from rpigott/resolved-ede
[thirdparty/systemd.git] / src / shared / blockdev-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
18c528e9 2
d25697f5 3#include <linux/blkpg.h>
ac83e5ae 4#include <sys/file.h>
d25697f5 5#include <sys/ioctl.h>
86f9b69a 6#include <sys/mount.h>
f5947a5e 7#include <unistd.h>
18c528e9 8
35d40302
DDM
9#include "sd-device.h"
10
18c528e9
LP
11#include "alloc-util.h"
12#include "blockdev-util.h"
13#include "btrfs-util.h"
35d40302 14#include "device-util.h"
7176f06c 15#include "devnum-util.h"
18c528e9 16#include "dirent-util.h"
d25697f5 17#include "errno-util.h"
18c528e9
LP
18#include "fd-util.h"
19#include "fileio.h"
c56be2c2 20#include "fs-util.h"
f5947a5e 21#include "missing_magic.h"
3a47c40d 22#include "parse-util.h"
18c528e9 23
fd690a4b 24static int fd_get_devnum(int fd, BlockDeviceLookupFlag flags, dev_t *ret) {
7e4a0387
YW
25 struct stat st;
26 dev_t devnum;
27 int r;
28
29 assert(fd >= 0);
30 assert(ret);
31
32 if (fstat(fd, &st) < 0)
33 return -errno;
34
35 if (S_ISBLK(st.st_mode))
36 devnum = st.st_rdev;
fd690a4b 37 else if (!FLAGS_SET(flags, BLOCK_DEVICE_LOOKUP_BACKING))
7e4a0387
YW
38 return -ENOTBLK;
39 else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
40 return -ENOTBLK;
41 else if (major(st.st_dev) != 0)
42 devnum = st.st_dev;
43 else {
7e4a0387
YW
44 /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
45 * handing, to get the backing device node. */
46
698bc186 47 r = btrfs_get_block_device_fd(fd, &devnum);
7e4a0387
YW
48 if (r == -ENOTTY) /* not btrfs */
49 return -ENOTBLK;
50 if (r < 0)
51 return r;
52 }
53
54 *ret = devnum;
55 return 0;
56}
57
2f9d2317 58int block_device_is_whole_disk(sd_device *dev) {
c07186ec
YW
59 assert(dev);
60
fb53ee0a 61 if (!device_in_subsystem(dev, "block"))
c07186ec
YW
62 return -ENOTBLK;
63
fb53ee0a 64 return device_is_devtype(dev, "disk");
c07186ec
YW
65}
66
55a437f8
YW
67int block_device_get_whole_disk(sd_device *dev, sd_device **ret) {
68 int r;
69
70 assert(dev);
71 assert(ret);
72
73 /* Do not unref returned sd_device object. */
74
75 r = block_device_is_whole_disk(dev);
76 if (r < 0)
77 return r;
78 if (r == 0) {
79 r = sd_device_get_parent(dev, &dev);
80 if (r == -ENOENT) /* Already removed? Let's return a recognizable error. */
81 return -ENODEV;
82 if (r < 0)
83 return r;
84
85 r = block_device_is_whole_disk(dev);
86 if (r < 0)
87 return r;
88 if (r == 0)
89 return -ENXIO;
90 }
91
92 *ret = dev;
93 return 0;
94}
95
50ab93eb 96int block_device_get_originating(sd_device *dev, sd_device **ret) {
612fc70f
YW
97 _cleanup_(sd_device_unrefp) sd_device *first_found = NULL;
98 const char *suffix;
612fc70f
YW
99 dev_t devnum = 0; /* avoid false maybe-uninitialized warning */
100
101 /* For the specified block device tries to chase it through the layers, in case LUKS-style DM
102 * stacking is used, trying to find the next underlying layer. */
103
104 assert(dev);
105 assert(ret);
106
107 FOREACH_DEVICE_CHILD_WITH_SUFFIX(dev, child, suffix) {
108 sd_device *child_whole_disk;
109 dev_t n;
110
111 if (!path_startswith(suffix, "slaves"))
112 continue;
113
114 if (block_device_get_whole_disk(child, &child_whole_disk) < 0)
115 continue;
116
117 if (sd_device_get_devnum(child_whole_disk, &n) < 0)
118 continue;
119
120 if (!first_found) {
121 first_found = sd_device_ref(child);
122 devnum = n;
123 continue;
124 }
125
126 /* We found a device backed by multiple other devices. We don't really support automatic
127 * discovery on such setups, with the exception of dm-verity partitions. In this case there
128 * are two backing devices: the data partition and the hash partition. We are fine with such
129 * setups, however, only if both partitions are on the same physical device. Hence, let's
130 * verify this by iterating over every node in the 'slaves/' directory and comparing them with
131 * the first that gets returned by readdir(), to ensure they all point to the same device. */
132 if (n != devnum)
133 return -ENOTUNIQ;
134 }
135
136 if (!first_found)
137 return -ENOENT;
138
139 *ret = TAKE_PTR(first_found);
140 return 1; /* found */
141}
142
fd690a4b
YW
143int block_device_new_from_fd(int fd, BlockDeviceLookupFlag flags, sd_device **ret) {
144 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
145 dev_t devnum;
146 int r;
147
148 assert(fd >= 0);
149 assert(ret);
150
151 r = fd_get_devnum(fd, flags, &devnum);
152 if (r < 0)
153 return r;
154
155 r = sd_device_new_from_devnum(&dev, 'b', devnum);
156 if (r < 0)
157 return r;
158
159 if (FLAGS_SET(flags, BLOCK_DEVICE_LOOKUP_ORIGINATING)) {
160 _cleanup_(sd_device_unrefp) sd_device *dev_origin = NULL;
161 sd_device *dev_whole_disk;
162
163 r = block_device_get_whole_disk(dev, &dev_whole_disk);
164 if (r < 0)
165 return r;
166
167 r = block_device_get_originating(dev_whole_disk, &dev_origin);
168 if (r < 0 && r != -ENOENT)
169 return r;
170 if (r > 0)
171 device_unref_and_replace(dev, dev_origin);
172 }
173
174 if (FLAGS_SET(flags, BLOCK_DEVICE_LOOKUP_WHOLE_DISK)) {
175 sd_device *dev_whole_disk;
176
177 r = block_device_get_whole_disk(dev, &dev_whole_disk);
178 if (r < 0)
179 return r;
180
181 *ret = sd_device_ref(dev_whole_disk);
182 return 0;
183 }
184
185 *ret = sd_device_ref(dev);
186 return 0;
187}
188
189int block_device_new_from_path(const char *path, BlockDeviceLookupFlag flags, sd_device **ret) {
254d1313 190 _cleanup_close_ int fd = -EBADF;
fd690a4b
YW
191
192 assert(path);
193 assert(ret);
194
195 fd = open(path, O_CLOEXEC|O_PATH);
196 if (fd < 0)
197 return -errno;
198
199 return block_device_new_from_fd(fd, flags, ret);
200}
201
18c528e9
LP
202int block_get_whole_disk(dev_t d, dev_t *ret) {
203 char p[SYS_BLOCK_PATH_MAX("/partition")];
204 _cleanup_free_ char *s = NULL;
3a47c40d 205 dev_t devt;
18c528e9
LP
206 int r;
207
208 assert(ret);
209
51f14fa1
LP
210 if (major(d) == 0)
211 return -ENODEV;
212
18c528e9
LP
213 /* If it has a queue this is good enough for us */
214 xsprintf_sys_block_path(p, "/queue", d);
215 if (access(p, F_OK) >= 0) {
216 *ret = d;
217 return 0;
218 }
6cba41ab
LP
219 if (errno != ENOENT)
220 return -errno;
18c528e9
LP
221
222 /* If it is a partition find the originating device */
223 xsprintf_sys_block_path(p, "/partition", d);
224 if (access(p, F_OK) < 0)
d18b3009 225 return -errno;
18c528e9
LP
226
227 /* Get parent dev_t */
228 xsprintf_sys_block_path(p, "/../dev", d);
229 r = read_one_line_file(p, &s);
230 if (r < 0)
231 return r;
232
7176f06c 233 r = parse_devnum(s, &devt);
3a47c40d
LP
234 if (r < 0)
235 return r;
18c528e9
LP
236
237 /* Only return this if it is really good enough for us. */
3a47c40d 238 xsprintf_sys_block_path(p, "/queue", devt);
18c528e9 239 if (access(p, F_OK) < 0)
d18b3009 240 return -errno;
18c528e9 241
3a47c40d 242 *ret = devt;
a0ea1dee 243 return 1;
18c528e9
LP
244}
245
0bfef8b4 246int get_block_device_fd(int fd, dev_t *ret) {
18c528e9 247 struct stat st;
db8728a6 248 int r;
18c528e9 249
0bfef8b4 250 assert(fd >= 0);
db8728a6 251 assert(ret);
18c528e9 252
db8728a6
LP
253 /* Gets the block device directly backing a file system. If the block device is encrypted, returns
254 * the device mapper block device. */
255
db8728a6 256 if (fstat(fd, &st))
18c528e9
LP
257 return -errno;
258
259 if (major(st.st_dev) != 0) {
db8728a6 260 *ret = st.st_dev;
18c528e9
LP
261 return 1;
262 }
263
698bc186 264 r = btrfs_get_block_device_fd(fd, ret);
db8728a6
LP
265 if (r > 0)
266 return 1;
267 if (r != -ENOTTY) /* not btrfs */
268 return r;
18c528e9 269
db8728a6 270 *ret = 0;
18c528e9
LP
271 return 0;
272}
273
0bfef8b4 274int get_block_device(const char *path, dev_t *ret) {
254d1313 275 _cleanup_close_ int fd = -EBADF;
0bfef8b4
LP
276
277 assert(path);
278 assert(ret);
279
280 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
281 if (fd < 0)
282 return -errno;
283
284 return get_block_device_fd(fd, ret);
285}
286
880f09bd 287int block_get_originating(dev_t dt, dev_t *ret) {
612fc70f 288 _cleanup_(sd_device_unrefp) sd_device *dev = NULL, *origin = NULL;
18c528e9
LP
289 int r;
290
612fc70f 291 assert(ret);
18c528e9 292
612fc70f 293 r = sd_device_new_from_devnum(&dev, 'b', dt);
18c528e9
LP
294 if (r < 0)
295 return r;
296
612fc70f 297 r = block_device_get_originating(dev, &origin);
3a47c40d 298 if (r < 0)
612fc70f 299 return r;
18c528e9 300
612fc70f 301 return sd_device_get_devnum(origin, ret);
880f09bd
LP
302}
303
bcf8fc26 304int get_block_device_harder_fd(int fd, dev_t *ret) {
880f09bd
LP
305 int r;
306
bcf8fc26 307 assert(fd >= 0);
880f09bd
LP
308 assert(ret);
309
310 /* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its
311 * immediate parent, if there is one. */
312
bcf8fc26 313 r = get_block_device_fd(fd, ret);
880f09bd
LP
314 if (r <= 0)
315 return r;
316
317 r = block_get_originating(*ret, ret);
318 if (r < 0)
bcf8fc26 319 log_debug_errno(r, "Failed to chase block device, ignoring: %m");
18c528e9 320
18c528e9
LP
321 return 1;
322}
ac83e5ae 323
bcf8fc26 324int get_block_device_harder(const char *path, dev_t *ret) {
254d1313 325 _cleanup_close_ int fd = -EBADF;
bcf8fc26
LP
326
327 assert(path);
328 assert(ret);
329
330 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
331 if (fd < 0)
332 return -errno;
333
334 return get_block_device_harder_fd(fd, ret);
335}
336
ac83e5ae 337int lock_whole_block_device(dev_t devt, int operation) {
254d1313 338 _cleanup_close_ int lock_fd = -EBADF;
ac83e5ae
LP
339 dev_t whole_devt;
340 int r;
341
342 /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */
343
344 r = block_get_whole_disk(devt, &whole_devt);
345 if (r < 0)
346 return r;
347
ca822829 348 lock_fd = r = device_open_from_devnum(S_IFBLK, whole_devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, NULL);
ac83e5ae
LP
349 if (r < 0)
350 return r;
351
ac83e5ae
LP
352 if (flock(lock_fd, operation) < 0)
353 return -errno;
354
355 return TAKE_FD(lock_fd);
356}
e8467cd3
LP
357
358int blockdev_partscan_enabled(int fd) {
359 _cleanup_free_ char *p = NULL, *buf = NULL;
360 unsigned long long ull;
361 struct stat st;
362 int r;
363
364 /* Checks if partition scanning is correctly enabled on the block device */
365
366 if (fstat(fd, &st) < 0)
367 return -errno;
368
369 if (!S_ISBLK(st.st_mode))
370 return -ENOTBLK;
371
372 if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0)
373 return -ENOMEM;
374
375 r = read_one_line_file(p, &buf);
376 if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a
377 * partition block device, not the whole block device. And that means we have no
378 * partition scanning on for it (we do for its parent, but not for the partition
379 * itself). */
380 return false;
381 if (r < 0)
382 return r;
383
384 r = safe_atollu_full(buf, 16, &ull);
385 if (r < 0)
386 return r;
387
388#ifndef GENHD_FL_NO_PART_SCAN
389#define GENHD_FL_NO_PART_SCAN (0x0200)
390#endif
391
392 return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN);
393}
b25a930f
ZJS
394
395static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
396 _cleanup_free_ char *p = NULL, *uuids = NULL;
397 _cleanup_closedir_ DIR *d = NULL;
398 int r, found_encrypted = false;
399
400 assert(sysfs_path);
401
402 if (depth_left == 0)
403 return -EINVAL;
404
405 p = path_join(sysfs_path, "dm/uuid");
406 if (!p)
407 return -ENOMEM;
408
409 r = read_one_line_file(p, &uuids);
410 if (r != -ENOENT) {
411 if (r < 0)
412 return r;
413
414 /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
415 if (startswith(uuids, "CRYPT-"))
416 return true;
417 }
418
419 /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
420 * subdir. */
421
422 p = mfree(p);
423 p = path_join(sysfs_path, "slaves");
424 if (!p)
425 return -ENOMEM;
426
427 d = opendir(p);
428 if (!d) {
429 if (errno == ENOENT) /* Doesn't have underlying devices */
430 return false;
431
432 return -errno;
433 }
434
435 for (;;) {
436 _cleanup_free_ char *q = NULL;
437 struct dirent *de;
438
439 errno = 0;
440 de = readdir_no_dot(d);
441 if (!de) {
442 if (errno != 0)
443 return -errno;
444
445 break; /* No more underlying devices */
446 }
447
448 q = path_join(p, de->d_name);
449 if (!q)
450 return -ENOMEM;
451
452 r = blockdev_is_encrypted(q, depth_left - 1);
453 if (r < 0)
454 return r;
455 if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
456 return false;
457
458 found_encrypted = true;
459 }
460
461 return found_encrypted;
462}
463
91358db9
LP
464int fd_is_encrypted(int fd) {
465 char p[SYS_BLOCK_PATH_MAX(NULL)];
466 dev_t devt;
467 int r;
468
469 r = get_block_device_fd(fd, &devt);
470 if (r < 0)
471 return r;
472 if (r == 0) /* doesn't have a block device */
473 return false;
474
475 xsprintf_sys_block_path(p, NULL, devt);
476
477 return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
478}
479
b25a930f
ZJS
480int path_is_encrypted(const char *path) {
481 char p[SYS_BLOCK_PATH_MAX(NULL)];
482 dev_t devt;
483 int r;
484
485 r = get_block_device(path, &devt);
486 if (r < 0)
487 return r;
488 if (r == 0) /* doesn't have a block device */
489 return false;
490
491 xsprintf_sys_block_path(p, NULL, devt);
492
493 return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
494}
26aa4800
DDM
495
496int fd_get_whole_disk(int fd, bool backing, dev_t *ret) {
497 dev_t devt;
26aa4800
DDM
498 int r;
499
7e4a0387 500 assert(fd >= 0);
26aa4800
DDM
501 assert(ret);
502
fd690a4b 503 r = fd_get_devnum(fd, backing ? BLOCK_DEVICE_LOOKUP_BACKING : 0, &devt);
7e4a0387
YW
504 if (r < 0)
505 return r;
26aa4800
DDM
506
507 return block_get_whole_disk(devt, ret);
508}
509
510int path_get_whole_disk(const char *path, bool backing, dev_t *ret) {
254d1313 511 _cleanup_close_ int fd = -EBADF;
26aa4800
DDM
512
513 fd = open(path, O_CLOEXEC|O_PATH);
514 if (fd < 0)
515 return -errno;
516
517 return fd_get_whole_disk(fd, backing, ret);
518}
d25697f5 519
91e1ce1a
LP
520int block_device_add_partition(
521 int fd,
522 const char *name,
523 int nr,
524 uint64_t start,
525 uint64_t size) {
526
d25697f5
DDM
527 assert(fd >= 0);
528 assert(name);
529 assert(nr > 0);
530
531 struct blkpg_partition bp = {
532 .pno = nr,
533 .start = start,
534 .length = size,
535 };
536
537 struct blkpg_ioctl_arg ba = {
538 .op = BLKPG_ADD_PARTITION,
539 .data = &bp,
540 .datalen = sizeof(bp),
541 };
542
543 if (strlen(name) >= sizeof(bp.devname))
544 return -EINVAL;
545
546 strcpy(bp.devname, name);
547
548 return RET_NERRNO(ioctl(fd, BLKPG, &ba));
549}
550
91e1ce1a
LP
551int block_device_remove_partition(
552 int fd,
553 const char *name,
554 int nr) {
555
d25697f5
DDM
556 assert(fd >= 0);
557 assert(name);
558 assert(nr > 0);
559
560 struct blkpg_partition bp = {
561 .pno = nr,
562 };
563
564 struct blkpg_ioctl_arg ba = {
565 .op = BLKPG_DEL_PARTITION,
566 .data = &bp,
567 .datalen = sizeof(bp),
568 };
569
570 if (strlen(name) >= sizeof(bp.devname))
571 return -EINVAL;
572
573 strcpy(bp.devname, name);
574
575 return RET_NERRNO(ioctl(fd, BLKPG, &ba));
576}
35d40302 577
91e1ce1a
LP
578int block_device_resize_partition(
579 int fd,
580 int nr,
581 uint64_t start,
582 uint64_t size) {
583
584 assert(fd >= 0);
585 assert(nr > 0);
586
587 struct blkpg_partition bp = {
588 .pno = nr,
589 .start = start,
590 .length = size,
591 };
592
593 struct blkpg_ioctl_arg ba = {
594 .op = BLKPG_RESIZE_PARTITION,
595 .data = &bp,
596 .datalen = sizeof(bp),
597 };
598
599 return RET_NERRNO(ioctl(fd, BLKPG, &ba));
600}
601
9409174e 602int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret) {
af15ee03
YW
603 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
604 const char *s;
605 int r;
606
607 assert(dev);
608 assert(ret);
609
c07186ec
YW
610 /* Refuse invocation on partition block device, insist on "whole" device */
611 r = block_device_is_whole_disk(dev);
af15ee03
YW
612 if (r < 0)
613 return r;
c07186ec 614 if (r == 0)
5a27af52 615 return -ENXIO; /* return a recognizable error */
af15ee03
YW
616
617 r = sd_device_enumerator_new(&e);
618 if (r < 0)
619 return r;
620
621 r = sd_device_enumerator_allow_uninitialized(e);
622 if (r < 0)
623 return r;
624
625 r = sd_device_enumerator_add_match_parent(e, dev);
626 if (r < 0)
627 return r;
628
e0993236
YW
629 r = sd_device_get_sysname(dev, &s);
630 if (r < 0)
631 return r;
632
633 /* Also add sysname check for safety. Hopefully, this also improves performance. */
634 s = strjoina(s, "*");
635 r = sd_device_enumerator_add_match_sysname(e, s);
636 if (r < 0)
637 return r;
638
af15ee03
YW
639 r = sd_device_enumerator_add_match_subsystem(e, "block", /* match = */ true);
640 if (r < 0)
641 return r;
642
643 r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition");
644 if (r < 0)
645 return r;
646
647 *ret = TAKE_PTR(e);
648 return 0;
649}
650
46c3a288 651int block_device_remove_all_partitions(sd_device *dev, int fd) {
35d40302 652 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
46c3a288 653 _cleanup_(sd_device_unrefp) sd_device *dev_unref = NULL;
254d1313 654 _cleanup_close_ int fd_close = -EBADF;
833106b8 655 bool has_partitions = false;
35d40302
DDM
656 int r, k = 0;
657
46c3a288 658 assert(dev || fd >= 0);
35d40302 659
46c3a288 660 if (!dev) {
fd690a4b 661 r = block_device_new_from_fd(fd, 0, &dev_unref);
46c3a288
YW
662 if (r < 0)
663 return r;
664
665 dev = dev_unref;
666 }
35d40302 667
af15ee03 668 r = partition_enumerator_new(dev, &e);
35d40302
DDM
669 if (r < 0)
670 return r;
671
46c3a288
YW
672 if (fd < 0) {
673 fd_close = sd_device_open(dev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_RDONLY);
674 if (fd_close < 0)
675 return fd_close;
676
677 fd = fd_close;
678 }
679
35d40302
DDM
680 FOREACH_DEVICE(e, part) {
681 const char *v, *devname;
682 int nr;
683
833106b8
YW
684 has_partitions = true;
685
35d40302
DDM
686 r = sd_device_get_devname(part, &devname);
687 if (r < 0)
688 return r;
689
690 r = sd_device_get_property_value(part, "PARTN", &v);
691 if (r < 0)
692 return r;
693
694 r = safe_atoi(v, &nr);
695 if (r < 0)
696 return r;
697
5228c58e
DDM
698 r = btrfs_forget_device(devname);
699 if (r < 0 && r != -ENOENT)
700 log_debug_errno(r, "Failed to forget btrfs device %s, ignoring: %m", devname);
701
35d40302
DDM
702 r = block_device_remove_partition(fd, devname, nr);
703 if (r == -ENODEV) {
704 log_debug("Kernel removed partition %s before us, ignoring", devname);
705 continue;
706 }
707 if (r < 0) {
708 log_debug_errno(r, "Failed to remove partition %s: %m", devname);
833106b8 709 k = k < 0 ? k : r;
35d40302
DDM
710 continue;
711 }
712
713 log_debug("Removed partition %s", devname);
714 }
715
833106b8 716 return k < 0 ? k : has_partitions;
35d40302 717}
aa0295f1
YW
718
719int block_device_has_partitions(sd_device *dev) {
720 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
721 int r;
722
723 assert(dev);
724
725 /* Checks if the specified device currently has partitions. */
726
727 r = partition_enumerator_new(dev, &e);
728 if (r < 0)
729 return r;
730
731 return !!sd_device_enumerator_get_device_first(e);
732}
86f9b69a
YW
733
734int blockdev_reread_partition_table(sd_device *dev) {
254d1313 735 _cleanup_close_ int fd = -EBADF;
86f9b69a
YW
736
737 assert(dev);
738
739 /* Try to re-read the partition table. This only succeeds if none of the devices is busy. */
740
741 fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
742 if (fd < 0)
743 return fd;
744
745 if (flock(fd, LOCK_EX|LOCK_NB) < 0)
746 return -errno;
747
748 if (ioctl(fd, BLKRRPART, 0) < 0)
749 return -errno;
750
751 return 0;
752}
65046b92
LP
753
754int blockdev_get_sector_size(int fd, uint32_t *ret) {
755 int ssz = 0;
756
757 assert(fd >= 0);
758 assert(ret);
759
760 if (ioctl(fd, BLKSSZGET, &ssz) < 0)
761 return -errno;
762 if (ssz <= 0) /* make sure the field is initialized */
763 return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Block device reported invalid sector size %i.", ssz);
764
765 *ret = ssz;
766 return 0;
767}
c56be2c2 768
01db9c85
LP
769int blockdev_get_device_size(int fd, uint64_t *ret) {
770 uint64_t sz = 0;
771
772 assert(fd >= 0);
773 assert(ret);
774
775 /* This is just a type-safe wrapper around BLKGETSIZE64 that gets us around having to include messy linux/fs.h in various clients */
776
777 if (ioctl(fd, BLKGETSIZE64, &sz) < 0)
778 return -errno;
779
780 *ret = sz;
781 return 0;
782}
783
c56be2c2
LP
784int blockdev_get_root(int level, dev_t *ret) {
785 _cleanup_free_ char *p = NULL;
786 dev_t devno;
787 int r;
788
789 /* Returns the device node backing the root file system. Traces through
790 * dm-crypt/dm-verity/... Returns > 0 and the devno of the device on success. If there's no block
791 * device (or multiple) returns 0 and a devno of 0. Failure otherwise.
792 *
793 * If the root mount has been replaced by some form of volatile file system (overlayfs), the original
794 * root block device node is symlinked in /run/systemd/volatile-root. Let's read that here. */
795 r = readlink_malloc("/run/systemd/volatile-root", &p);
796 if (r == -ENOENT) { /* volatile-root not found */
797 r = get_block_device_harder("/", &devno);
798 if (r == -EUCLEAN)
799 return btrfs_log_dev_root(level, r, "root file system");
800 if (r < 0)
801 return log_full_errno(level, r, "Failed to determine block device of root file system: %m");
802 if (r == 0) { /* Not backed by a single block device. (Could be NFS or so, or could be multi-device RAID or so) */
803 r = get_block_device_harder("/usr", &devno);
804 if (r == -EUCLEAN)
805 return btrfs_log_dev_root(level, r, "/usr");
806 if (r < 0)
807 return log_full_errno(level, r, "Failed to determine block device of /usr/ file system: %m");
808 if (r == 0) { /* /usr/ not backed by single block device, either. */
809 log_debug("Neither root nor /usr/ file system are on a (single) block device.");
810
811 if (ret)
812 *ret = 0;
813
814 return 0;
815 }
816 }
817 } else if (r < 0)
818 return log_full_errno(level, r, "Failed to read symlink /run/systemd/volatile-root: %m");
819 else {
820 mode_t m;
821 r = device_path_parse_major_minor(p, &m, &devno);
822 if (r < 0)
823 return log_full_errno(level, r, "Failed to parse major/minor device node: %m");
824 if (!S_ISBLK(m))
825 return log_full_errno(level, SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
826 }
827
828 if (ret)
829 *ret = devno;
830
831 return 1;
832}