]>
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" | |
f5947a5e | 20 | #include "missing_magic.h" |
3a47c40d | 21 | #include "parse-util.h" |
18c528e9 | 22 | |
2f9d2317 | 23 | int block_device_is_whole_disk(sd_device *dev) { |
c07186ec YW |
24 | const char *s; |
25 | int r; | |
26 | ||
27 | assert(dev); | |
28 | ||
29 | r = sd_device_get_subsystem(dev, &s); | |
30 | if (r < 0) | |
31 | return r; | |
32 | ||
33 | if (!streq(s, "block")) | |
34 | return -ENOTBLK; | |
35 | ||
36 | r = sd_device_get_devtype(dev, &s); | |
37 | if (r < 0) | |
38 | return r; | |
39 | ||
40 | return streq(s, "disk"); | |
41 | } | |
42 | ||
55a437f8 YW |
43 | int block_device_get_whole_disk(sd_device *dev, sd_device **ret) { |
44 | int r; | |
45 | ||
46 | assert(dev); | |
47 | assert(ret); | |
48 | ||
49 | /* Do not unref returned sd_device object. */ | |
50 | ||
51 | r = block_device_is_whole_disk(dev); | |
52 | if (r < 0) | |
53 | return r; | |
54 | if (r == 0) { | |
55 | r = sd_device_get_parent(dev, &dev); | |
56 | if (r == -ENOENT) /* Already removed? Let's return a recognizable error. */ | |
57 | return -ENODEV; | |
58 | if (r < 0) | |
59 | return r; | |
60 | ||
61 | r = block_device_is_whole_disk(dev); | |
62 | if (r < 0) | |
63 | return r; | |
64 | if (r == 0) | |
65 | return -ENXIO; | |
66 | } | |
67 | ||
68 | *ret = dev; | |
69 | return 0; | |
70 | } | |
71 | ||
18c528e9 LP |
72 | int block_get_whole_disk(dev_t d, dev_t *ret) { |
73 | char p[SYS_BLOCK_PATH_MAX("/partition")]; | |
74 | _cleanup_free_ char *s = NULL; | |
3a47c40d | 75 | dev_t devt; |
18c528e9 LP |
76 | int r; |
77 | ||
78 | assert(ret); | |
79 | ||
51f14fa1 LP |
80 | if (major(d) == 0) |
81 | return -ENODEV; | |
82 | ||
18c528e9 LP |
83 | /* If it has a queue this is good enough for us */ |
84 | xsprintf_sys_block_path(p, "/queue", d); | |
85 | if (access(p, F_OK) >= 0) { | |
86 | *ret = d; | |
87 | return 0; | |
88 | } | |
6cba41ab LP |
89 | if (errno != ENOENT) |
90 | return -errno; | |
18c528e9 LP |
91 | |
92 | /* If it is a partition find the originating device */ | |
93 | xsprintf_sys_block_path(p, "/partition", d); | |
94 | if (access(p, F_OK) < 0) | |
d18b3009 | 95 | return -errno; |
18c528e9 LP |
96 | |
97 | /* Get parent dev_t */ | |
98 | xsprintf_sys_block_path(p, "/../dev", d); | |
99 | r = read_one_line_file(p, &s); | |
100 | if (r < 0) | |
101 | return r; | |
102 | ||
7176f06c | 103 | r = parse_devnum(s, &devt); |
3a47c40d LP |
104 | if (r < 0) |
105 | return r; | |
18c528e9 LP |
106 | |
107 | /* Only return this if it is really good enough for us. */ | |
3a47c40d | 108 | xsprintf_sys_block_path(p, "/queue", devt); |
18c528e9 | 109 | if (access(p, F_OK) < 0) |
d18b3009 | 110 | return -errno; |
18c528e9 | 111 | |
3a47c40d | 112 | *ret = devt; |
a0ea1dee | 113 | return 1; |
18c528e9 LP |
114 | } |
115 | ||
0bfef8b4 | 116 | int get_block_device_fd(int fd, dev_t *ret) { |
18c528e9 | 117 | struct stat st; |
db8728a6 | 118 | int r; |
18c528e9 | 119 | |
0bfef8b4 | 120 | assert(fd >= 0); |
db8728a6 | 121 | assert(ret); |
18c528e9 | 122 | |
db8728a6 LP |
123 | /* Gets the block device directly backing a file system. If the block device is encrypted, returns |
124 | * the device mapper block device. */ | |
125 | ||
db8728a6 | 126 | if (fstat(fd, &st)) |
18c528e9 LP |
127 | return -errno; |
128 | ||
129 | if (major(st.st_dev) != 0) { | |
db8728a6 | 130 | *ret = st.st_dev; |
18c528e9 LP |
131 | return 1; |
132 | } | |
133 | ||
13879c54 LP |
134 | r = fcntl(fd, F_GETFL); |
135 | if (r < 0) | |
136 | return -errno; | |
137 | if (FLAGS_SET(r, O_PATH) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) { | |
138 | _cleanup_close_ int real_fd = -1; | |
139 | ||
140 | /* The fstat() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot. Hence | |
141 | * acquire a "real" fd first, without the O_PATH flag. */ | |
142 | ||
143 | real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC); | |
144 | if (real_fd < 0) | |
145 | return real_fd; | |
146 | r = btrfs_get_block_device_fd(real_fd, ret); | |
147 | } else | |
148 | r = btrfs_get_block_device_fd(fd, ret); | |
db8728a6 LP |
149 | if (r > 0) |
150 | return 1; | |
151 | if (r != -ENOTTY) /* not btrfs */ | |
152 | return r; | |
18c528e9 | 153 | |
db8728a6 | 154 | *ret = 0; |
18c528e9 LP |
155 | return 0; |
156 | } | |
157 | ||
0bfef8b4 LP |
158 | int get_block_device(const char *path, dev_t *ret) { |
159 | _cleanup_close_ int fd = -1; | |
160 | ||
161 | assert(path); | |
162 | assert(ret); | |
163 | ||
164 | fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC); | |
165 | if (fd < 0) | |
166 | return -errno; | |
167 | ||
168 | return get_block_device_fd(fd, ret); | |
169 | } | |
170 | ||
880f09bd | 171 | int block_get_originating(dev_t dt, dev_t *ret) { |
18c528e9 LP |
172 | _cleanup_closedir_ DIR *d = NULL; |
173 | _cleanup_free_ char *t = NULL; | |
174 | char p[SYS_BLOCK_PATH_MAX("/slaves")]; | |
c68fc351 | 175 | _cleanup_free_ char *first_found = NULL; |
880f09bd | 176 | const char *q; |
3a47c40d | 177 | dev_t devt; |
18c528e9 LP |
178 | int r; |
179 | ||
880f09bd LP |
180 | /* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used, |
181 | * trying to find the next underlying layer. */ | |
18c528e9 LP |
182 | |
183 | xsprintf_sys_block_path(p, "/slaves", dt); | |
184 | d = opendir(p); | |
880f09bd | 185 | if (!d) |
18c528e9 | 186 | return -errno; |
18c528e9 LP |
187 | |
188 | FOREACH_DIRENT_ALL(de, d, return -errno) { | |
189 | ||
190 | if (dot_or_dot_dot(de->d_name)) | |
191 | continue; | |
192 | ||
193 | if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) | |
194 | continue; | |
195 | ||
c68fc351 | 196 | if (first_found) { |
18c528e9 LP |
197 | _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; |
198 | ||
c68fc351 LP |
199 | /* We found a device backed by multiple other devices. We don't really support |
200 | * automatic discovery on such setups, with the exception of dm-verity partitions. In | |
201 | * this case there are two backing devices: the data partition and the hash | |
202 | * partition. We are fine with such setups, however, only if both partitions are on | |
203 | * the same physical device. Hence, let's verify this by iterating over every node | |
204 | * in the 'slaves/' directory and comparing them with the first that gets returned by | |
205 | * readdir(), to ensure they all point to the same device. */ | |
18c528e9 | 206 | |
657ee2d8 | 207 | u = path_join(p, de->d_name, "../dev"); |
18c528e9 LP |
208 | if (!u) |
209 | return -ENOMEM; | |
210 | ||
c68fc351 | 211 | v = path_join(p, first_found, "../dev"); |
18c528e9 LP |
212 | if (!v) |
213 | return -ENOMEM; | |
214 | ||
215 | r = read_one_line_file(u, &a); | |
880f09bd LP |
216 | if (r < 0) |
217 | return log_debug_errno(r, "Failed to read %s: %m", u); | |
18c528e9 LP |
218 | |
219 | r = read_one_line_file(v, &b); | |
880f09bd LP |
220 | if (r < 0) |
221 | return log_debug_errno(r, "Failed to read %s: %m", v); | |
18c528e9 LP |
222 | |
223 | /* Check if the parent device is the same. If not, then the two backing devices are on | |
224 | * different physical devices, and we don't support that. */ | |
225 | if (!streq(a, b)) | |
880f09bd | 226 | return -ENOTUNIQ; |
c68fc351 LP |
227 | } else { |
228 | first_found = strdup(de->d_name); | |
229 | if (!first_found) | |
230 | return -ENOMEM; | |
18c528e9 | 231 | } |
18c528e9 LP |
232 | } |
233 | ||
c68fc351 | 234 | if (!first_found) |
880f09bd | 235 | return -ENOENT; |
18c528e9 | 236 | |
c68fc351 | 237 | q = strjoina(p, "/", first_found, "/dev"); |
18c528e9 LP |
238 | |
239 | r = read_one_line_file(q, &t); | |
18c528e9 LP |
240 | if (r < 0) |
241 | return r; | |
242 | ||
7176f06c | 243 | r = parse_devnum(t, &devt); |
3a47c40d | 244 | if (r < 0) |
18c528e9 LP |
245 | return -EINVAL; |
246 | ||
3a47c40d | 247 | if (major(devt) == 0) |
880f09bd | 248 | return -ENOENT; |
18c528e9 | 249 | |
3a47c40d | 250 | *ret = devt; |
18c528e9 | 251 | return 1; |
880f09bd LP |
252 | } |
253 | ||
bcf8fc26 | 254 | int get_block_device_harder_fd(int fd, dev_t *ret) { |
880f09bd LP |
255 | int r; |
256 | ||
bcf8fc26 | 257 | assert(fd >= 0); |
880f09bd LP |
258 | assert(ret); |
259 | ||
260 | /* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its | |
261 | * immediate parent, if there is one. */ | |
262 | ||
bcf8fc26 | 263 | r = get_block_device_fd(fd, ret); |
880f09bd LP |
264 | if (r <= 0) |
265 | return r; | |
266 | ||
267 | r = block_get_originating(*ret, ret); | |
268 | if (r < 0) | |
bcf8fc26 | 269 | log_debug_errno(r, "Failed to chase block device, ignoring: %m"); |
18c528e9 | 270 | |
18c528e9 LP |
271 | return 1; |
272 | } | |
ac83e5ae | 273 | |
bcf8fc26 LP |
274 | int get_block_device_harder(const char *path, dev_t *ret) { |
275 | _cleanup_close_ int fd = -1; | |
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_harder_fd(fd, ret); | |
285 | } | |
286 | ||
ac83e5ae | 287 | int lock_whole_block_device(dev_t devt, int operation) { |
ac83e5ae LP |
288 | _cleanup_close_ int lock_fd = -1; |
289 | dev_t whole_devt; | |
290 | int r; | |
291 | ||
292 | /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */ | |
293 | ||
294 | r = block_get_whole_disk(devt, &whole_devt); | |
295 | if (r < 0) | |
296 | return r; | |
297 | ||
ca822829 | 298 | lock_fd = r = device_open_from_devnum(S_IFBLK, whole_devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, NULL); |
ac83e5ae LP |
299 | if (r < 0) |
300 | return r; | |
301 | ||
ac83e5ae LP |
302 | if (flock(lock_fd, operation) < 0) |
303 | return -errno; | |
304 | ||
305 | return TAKE_FD(lock_fd); | |
306 | } | |
e8467cd3 LP |
307 | |
308 | int blockdev_partscan_enabled(int fd) { | |
309 | _cleanup_free_ char *p = NULL, *buf = NULL; | |
310 | unsigned long long ull; | |
311 | struct stat st; | |
312 | int r; | |
313 | ||
314 | /* Checks if partition scanning is correctly enabled on the block device */ | |
315 | ||
316 | if (fstat(fd, &st) < 0) | |
317 | return -errno; | |
318 | ||
319 | if (!S_ISBLK(st.st_mode)) | |
320 | return -ENOTBLK; | |
321 | ||
322 | if (asprintf(&p, "/sys/dev/block/%u:%u/capability", major(st.st_rdev), minor(st.st_rdev)) < 0) | |
323 | return -ENOMEM; | |
324 | ||
325 | r = read_one_line_file(p, &buf); | |
326 | if (r == -ENOENT) /* If the capability file doesn't exist then we are most likely looking at a | |
327 | * partition block device, not the whole block device. And that means we have no | |
328 | * partition scanning on for it (we do for its parent, but not for the partition | |
329 | * itself). */ | |
330 | return false; | |
331 | if (r < 0) | |
332 | return r; | |
333 | ||
334 | r = safe_atollu_full(buf, 16, &ull); | |
335 | if (r < 0) | |
336 | return r; | |
337 | ||
338 | #ifndef GENHD_FL_NO_PART_SCAN | |
339 | #define GENHD_FL_NO_PART_SCAN (0x0200) | |
340 | #endif | |
341 | ||
342 | return !FLAGS_SET(ull, GENHD_FL_NO_PART_SCAN); | |
343 | } | |
b25a930f ZJS |
344 | |
345 | static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { | |
346 | _cleanup_free_ char *p = NULL, *uuids = NULL; | |
347 | _cleanup_closedir_ DIR *d = NULL; | |
348 | int r, found_encrypted = false; | |
349 | ||
350 | assert(sysfs_path); | |
351 | ||
352 | if (depth_left == 0) | |
353 | return -EINVAL; | |
354 | ||
355 | p = path_join(sysfs_path, "dm/uuid"); | |
356 | if (!p) | |
357 | return -ENOMEM; | |
358 | ||
359 | r = read_one_line_file(p, &uuids); | |
360 | if (r != -ENOENT) { | |
361 | if (r < 0) | |
362 | return r; | |
363 | ||
364 | /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ | |
365 | if (startswith(uuids, "CRYPT-")) | |
366 | return true; | |
367 | } | |
368 | ||
369 | /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" | |
370 | * subdir. */ | |
371 | ||
372 | p = mfree(p); | |
373 | p = path_join(sysfs_path, "slaves"); | |
374 | if (!p) | |
375 | return -ENOMEM; | |
376 | ||
377 | d = opendir(p); | |
378 | if (!d) { | |
379 | if (errno == ENOENT) /* Doesn't have underlying devices */ | |
380 | return false; | |
381 | ||
382 | return -errno; | |
383 | } | |
384 | ||
385 | for (;;) { | |
386 | _cleanup_free_ char *q = NULL; | |
387 | struct dirent *de; | |
388 | ||
389 | errno = 0; | |
390 | de = readdir_no_dot(d); | |
391 | if (!de) { | |
392 | if (errno != 0) | |
393 | return -errno; | |
394 | ||
395 | break; /* No more underlying devices */ | |
396 | } | |
397 | ||
398 | q = path_join(p, de->d_name); | |
399 | if (!q) | |
400 | return -ENOMEM; | |
401 | ||
402 | r = blockdev_is_encrypted(q, depth_left - 1); | |
403 | if (r < 0) | |
404 | return r; | |
405 | if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ | |
406 | return false; | |
407 | ||
408 | found_encrypted = true; | |
409 | } | |
410 | ||
411 | return found_encrypted; | |
412 | } | |
413 | ||
91358db9 LP |
414 | int fd_is_encrypted(int fd) { |
415 | char p[SYS_BLOCK_PATH_MAX(NULL)]; | |
416 | dev_t devt; | |
417 | int r; | |
418 | ||
419 | r = get_block_device_fd(fd, &devt); | |
420 | if (r < 0) | |
421 | return r; | |
422 | if (r == 0) /* doesn't have a block device */ | |
423 | return false; | |
424 | ||
425 | xsprintf_sys_block_path(p, NULL, devt); | |
426 | ||
427 | return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); | |
428 | } | |
429 | ||
b25a930f ZJS |
430 | int path_is_encrypted(const char *path) { |
431 | char p[SYS_BLOCK_PATH_MAX(NULL)]; | |
432 | dev_t devt; | |
433 | int r; | |
434 | ||
435 | r = get_block_device(path, &devt); | |
436 | if (r < 0) | |
437 | return r; | |
438 | if (r == 0) /* doesn't have a block device */ | |
439 | return false; | |
440 | ||
441 | xsprintf_sys_block_path(p, NULL, devt); | |
442 | ||
443 | return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); | |
444 | } | |
26aa4800 DDM |
445 | |
446 | int fd_get_whole_disk(int fd, bool backing, dev_t *ret) { | |
447 | dev_t devt; | |
448 | struct stat st; | |
449 | int r; | |
450 | ||
451 | assert(ret); | |
452 | ||
453 | if (fstat(fd, &st) < 0) | |
454 | return -errno; | |
455 | ||
456 | if (S_ISBLK(st.st_mode)) | |
457 | devt = st.st_rdev; | |
458 | else if (!backing) | |
459 | return -ENOTBLK; | |
460 | else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) | |
461 | return -ENOTBLK; | |
462 | else if (major(st.st_dev) != 0) | |
463 | devt = st.st_dev; | |
464 | else { | |
465 | _cleanup_close_ int regfd = -1; | |
466 | ||
467 | /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special | |
468 | * handing, to get the backing device node. */ | |
469 | ||
470 | regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK); | |
471 | if (regfd < 0) | |
472 | return regfd; | |
473 | ||
474 | r = btrfs_get_block_device_fd(regfd, &devt); | |
475 | if (r == -ENOTTY) | |
476 | return -ENOTBLK; | |
477 | if (r < 0) | |
478 | return r; | |
479 | } | |
480 | ||
481 | return block_get_whole_disk(devt, ret); | |
482 | } | |
483 | ||
484 | int path_get_whole_disk(const char *path, bool backing, dev_t *ret) { | |
485 | _cleanup_close_ int fd = -1; | |
486 | ||
487 | fd = open(path, O_CLOEXEC|O_PATH); | |
488 | if (fd < 0) | |
489 | return -errno; | |
490 | ||
491 | return fd_get_whole_disk(fd, backing, ret); | |
492 | } | |
d25697f5 | 493 | |
91e1ce1a LP |
494 | int block_device_add_partition( |
495 | int fd, | |
496 | const char *name, | |
497 | int nr, | |
498 | uint64_t start, | |
499 | uint64_t size) { | |
500 | ||
d25697f5 DDM |
501 | assert(fd >= 0); |
502 | assert(name); | |
503 | assert(nr > 0); | |
504 | ||
505 | struct blkpg_partition bp = { | |
506 | .pno = nr, | |
507 | .start = start, | |
508 | .length = size, | |
509 | }; | |
510 | ||
511 | struct blkpg_ioctl_arg ba = { | |
512 | .op = BLKPG_ADD_PARTITION, | |
513 | .data = &bp, | |
514 | .datalen = sizeof(bp), | |
515 | }; | |
516 | ||
517 | if (strlen(name) >= sizeof(bp.devname)) | |
518 | return -EINVAL; | |
519 | ||
520 | strcpy(bp.devname, name); | |
521 | ||
522 | return RET_NERRNO(ioctl(fd, BLKPG, &ba)); | |
523 | } | |
524 | ||
91e1ce1a LP |
525 | int block_device_remove_partition( |
526 | int fd, | |
527 | const char *name, | |
528 | int nr) { | |
529 | ||
d25697f5 DDM |
530 | assert(fd >= 0); |
531 | assert(name); | |
532 | assert(nr > 0); | |
533 | ||
534 | struct blkpg_partition bp = { | |
535 | .pno = nr, | |
536 | }; | |
537 | ||
538 | struct blkpg_ioctl_arg ba = { | |
539 | .op = BLKPG_DEL_PARTITION, | |
540 | .data = &bp, | |
541 | .datalen = sizeof(bp), | |
542 | }; | |
543 | ||
544 | if (strlen(name) >= sizeof(bp.devname)) | |
545 | return -EINVAL; | |
546 | ||
547 | strcpy(bp.devname, name); | |
548 | ||
549 | return RET_NERRNO(ioctl(fd, BLKPG, &ba)); | |
550 | } | |
35d40302 | 551 | |
91e1ce1a LP |
552 | int block_device_resize_partition( |
553 | int fd, | |
554 | int nr, | |
555 | uint64_t start, | |
556 | uint64_t size) { | |
557 | ||
558 | assert(fd >= 0); | |
559 | assert(nr > 0); | |
560 | ||
561 | struct blkpg_partition bp = { | |
562 | .pno = nr, | |
563 | .start = start, | |
564 | .length = size, | |
565 | }; | |
566 | ||
567 | struct blkpg_ioctl_arg ba = { | |
568 | .op = BLKPG_RESIZE_PARTITION, | |
569 | .data = &bp, | |
570 | .datalen = sizeof(bp), | |
571 | }; | |
572 | ||
573 | return RET_NERRNO(ioctl(fd, BLKPG, &ba)); | |
574 | } | |
575 | ||
9409174e | 576 | int partition_enumerator_new(sd_device *dev, sd_device_enumerator **ret) { |
af15ee03 YW |
577 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
578 | const char *s; | |
579 | int r; | |
580 | ||
581 | assert(dev); | |
582 | assert(ret); | |
583 | ||
c07186ec YW |
584 | /* Refuse invocation on partition block device, insist on "whole" device */ |
585 | r = block_device_is_whole_disk(dev); | |
af15ee03 YW |
586 | if (r < 0) |
587 | return r; | |
c07186ec | 588 | if (r == 0) |
5a27af52 | 589 | return -ENXIO; /* return a recognizable error */ |
af15ee03 YW |
590 | |
591 | r = sd_device_enumerator_new(&e); | |
592 | if (r < 0) | |
593 | return r; | |
594 | ||
595 | r = sd_device_enumerator_allow_uninitialized(e); | |
596 | if (r < 0) | |
597 | return r; | |
598 | ||
599 | r = sd_device_enumerator_add_match_parent(e, dev); | |
600 | if (r < 0) | |
601 | return r; | |
602 | ||
e0993236 YW |
603 | r = sd_device_get_sysname(dev, &s); |
604 | if (r < 0) | |
605 | return r; | |
606 | ||
607 | /* Also add sysname check for safety. Hopefully, this also improves performance. */ | |
608 | s = strjoina(s, "*"); | |
609 | r = sd_device_enumerator_add_match_sysname(e, s); | |
610 | if (r < 0) | |
611 | return r; | |
612 | ||
af15ee03 YW |
613 | r = sd_device_enumerator_add_match_subsystem(e, "block", /* match = */ true); |
614 | if (r < 0) | |
615 | return r; | |
616 | ||
617 | r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition"); | |
618 | if (r < 0) | |
619 | return r; | |
620 | ||
621 | *ret = TAKE_PTR(e); | |
622 | return 0; | |
623 | } | |
624 | ||
46c3a288 | 625 | int block_device_remove_all_partitions(sd_device *dev, int fd) { |
35d40302 | 626 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; |
46c3a288 YW |
627 | _cleanup_(sd_device_unrefp) sd_device *dev_unref = NULL; |
628 | _cleanup_close_ int fd_close = -1; | |
833106b8 | 629 | bool has_partitions = false; |
35d40302 DDM |
630 | sd_device *part; |
631 | int r, k = 0; | |
632 | ||
46c3a288 | 633 | assert(dev || fd >= 0); |
35d40302 | 634 | |
46c3a288 YW |
635 | if (!dev) { |
636 | struct stat st; | |
637 | ||
638 | if (fstat(fd, &st) < 0) | |
639 | return -errno; | |
640 | ||
641 | r = sd_device_new_from_stat_rdev(&dev_unref, &st); | |
642 | if (r < 0) | |
643 | return r; | |
644 | ||
645 | dev = dev_unref; | |
646 | } | |
35d40302 | 647 | |
af15ee03 | 648 | r = partition_enumerator_new(dev, &e); |
35d40302 DDM |
649 | if (r < 0) |
650 | return r; | |
651 | ||
46c3a288 YW |
652 | if (fd < 0) { |
653 | fd_close = sd_device_open(dev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_RDONLY); | |
654 | if (fd_close < 0) | |
655 | return fd_close; | |
656 | ||
657 | fd = fd_close; | |
658 | } | |
659 | ||
35d40302 DDM |
660 | FOREACH_DEVICE(e, part) { |
661 | const char *v, *devname; | |
662 | int nr; | |
663 | ||
833106b8 YW |
664 | has_partitions = true; |
665 | ||
35d40302 DDM |
666 | r = sd_device_get_devname(part, &devname); |
667 | if (r < 0) | |
668 | return r; | |
669 | ||
670 | r = sd_device_get_property_value(part, "PARTN", &v); | |
671 | if (r < 0) | |
672 | return r; | |
673 | ||
674 | r = safe_atoi(v, &nr); | |
675 | if (r < 0) | |
676 | return r; | |
677 | ||
678 | r = block_device_remove_partition(fd, devname, nr); | |
679 | if (r == -ENODEV) { | |
680 | log_debug("Kernel removed partition %s before us, ignoring", devname); | |
681 | continue; | |
682 | } | |
683 | if (r < 0) { | |
684 | log_debug_errno(r, "Failed to remove partition %s: %m", devname); | |
833106b8 | 685 | k = k < 0 ? k : r; |
35d40302 DDM |
686 | continue; |
687 | } | |
688 | ||
689 | log_debug("Removed partition %s", devname); | |
690 | } | |
691 | ||
833106b8 | 692 | return k < 0 ? k : has_partitions; |
35d40302 | 693 | } |
aa0295f1 YW |
694 | |
695 | int block_device_has_partitions(sd_device *dev) { | |
696 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
697 | int r; | |
698 | ||
699 | assert(dev); | |
700 | ||
701 | /* Checks if the specified device currently has partitions. */ | |
702 | ||
703 | r = partition_enumerator_new(dev, &e); | |
704 | if (r < 0) | |
705 | return r; | |
706 | ||
707 | return !!sd_device_enumerator_get_device_first(e); | |
708 | } | |
86f9b69a YW |
709 | |
710 | int blockdev_reread_partition_table(sd_device *dev) { | |
711 | _cleanup_close_ int fd = -1; | |
712 | ||
713 | assert(dev); | |
714 | ||
715 | /* Try to re-read the partition table. This only succeeds if none of the devices is busy. */ | |
716 | ||
717 | fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); | |
718 | if (fd < 0) | |
719 | return fd; | |
720 | ||
721 | if (flock(fd, LOCK_EX|LOCK_NB) < 0) | |
722 | return -errno; | |
723 | ||
724 | if (ioctl(fd, BLKRRPART, 0) < 0) | |
725 | return -errno; | |
726 | ||
727 | return 0; | |
728 | } |