]>
Commit | Line | Data |
---|---|---|
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 | 24 | static 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 | 58 | int 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 |
67 | int 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 | 96 | int 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 |
143 | int 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 | ||
189 | int 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 |
202 | int 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 | 246 | int 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 | 274 | int 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 | 287 | int 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 | 304 | int 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 | 324 | int 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 | 337 | int 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 | |
358 | int 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 | |
395 | static 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 |
464 | int 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 |
480 | int 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 | |
496 | int 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 | ||
510 | int 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 |
520 | int 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 |
551 | int 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 |
578 | int 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 | 602 | int 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 | 651 | int 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 | |
719 | int 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 | |
734 | int 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 | |
754 | int 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 |
769 | int 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 |
784 | int 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 | } |