summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJanuary <1398871225@qq.com>2022-11-24 14:13:17 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2022-12-13 14:55:57 +0100
commit55371658793b7276fea30bdcd12252e1f75eb5e9 (patch)
treeffeef12ecd7ea30875cdc8c20f88ee6eb9949059
parentexecute: Pass AT_FDCWD instead of -1 (diff)
downloadsystemd-55371658793b7276fea30bdcd12252e1f75eb5e9.tar.xz
systemd-55371658793b7276fea30bdcd12252e1f75eb5e9.zip
doc: add an example code to lock the whole disk
add an example to leverage `libsystemd` infrastructure to get the whole disk of a block device and take BSD lock on it #25046
-rw-r--r--docs/BLOCK_DEVICE_LOCKING.md143
1 files changed, 143 insertions, 0 deletions
diff --git a/docs/BLOCK_DEVICE_LOCKING.md b/docs/BLOCK_DEVICE_LOCKING.md
index 61beff0ff2..87aad67045 100644
--- a/docs/BLOCK_DEVICE_LOCKING.md
+++ b/docs/BLOCK_DEVICE_LOCKING.md
@@ -98,3 +98,146 @@ Summarizing: it is recommended to take `LOCK_EX` BSD file locks when
manipulating block devices in all tools that change file system block devices
(`mkfs`, `fsck`, …) or partition tables (`fdisk`, `parted`, …), right after
opening the node.
+
+# Example of Locking The Whole Disk
+
+The following is an example to leverage `libsystemd` infrastructure to get the whole disk of a block device and take a BSD lock on it.
+
+## Compile and Execute
+**Note that this example requires `libsystemd` version 251 or newer.**
+
+Place the code in a source file, e.g. `take_BSD_lock.c` and run the following commands:
+```
+$ gcc -o take_BSD_lock -lsystemd take_BSD_lock.c
+
+$ ./take_BSD_lock /dev/sda1
+Successfully took a BSD lock: /dev/sda
+
+$ flock -x /dev/sda ./take_BSD_lock /dev/sda1
+Failed to take a BSD lock on /dev/sda: Resource temporarily unavailable
+```
+
+## Code
+```
+/* SPDX-License-Identifier: MIT-0 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <systemd/sd-device.h>
+#include <unistd.h>
+
+static inline void closep(int *fd) {
+ if (*fd >= 0)
+ close(*fd);
+}
+
+/**
+ * lock_whole_disk_from_devname
+ * @devname: devname of a block device, e.g., /dev/sda or /dev/sda1
+ * @open_flags: the flags to open the device, e.g., O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY
+ * @flock_operation: the operation to call flock, e.g., LOCK_EX|LOCK_NB
+ *
+ * given the devname of a block device, take a BSD lock of the whole disk
+ *
+ * Returns: negative errno value on error, or non-negative fd if the lock was taken successfully.
+ **/
+int lock_whole_disk_from_devname(const char *devname, int open_flags, int flock_operation) {
+ __attribute__((cleanup(sd_device_unrefp))) sd_device *dev = NULL;
+ sd_device *whole_dev;
+ const char *whole_disk_devname, *subsystem, *devtype;
+ int r;
+
+ // create a sd_device instance from devname
+ r = sd_device_new_from_devname(&dev, devname);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to create sd_device: %m\n");
+ return r;
+ }
+
+ // if the subsystem of dev is block, but its devtype is not disk, find its parent
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the subsystem: %m\n");
+ return r;
+ }
+ if (strcmp(subsystem, "block") != 0) {
+ fprintf(stderr, "%s is not a block device, refusing.\n", devname);
+ return -EINVAL;
+ }
+
+ r = sd_device_get_devtype(dev, &devtype);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the devtype: %m\n");
+ return r;
+ }
+ if (strcmp(devtype, "disk") == 0)
+ whole_dev = dev;
+ else {
+ r = sd_device_get_parent_with_subsystem_devtype(dev, "block", "disk", &whole_dev);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the parent device: %m\n");
+ return r;
+ }
+ }
+
+ // open the whole disk device node
+ __attribute__((cleanup(closep))) int fd = sd_device_open(whole_dev, open_flags);
+ if (fd < 0) {
+ errno = -fd;
+ fprintf(stderr, "Failed to open the device: %m\n");
+ return fd;
+ }
+
+ // get the whole disk devname
+ r = sd_device_get_devname(whole_dev, &whole_disk_devname);
+ if (r < 0) {
+ errno = -r;
+ fprintf(stderr, "Failed to get the whole disk name: %m\n");
+ return r;
+ }
+
+ // take a BSD lock of the whole disk device node
+ if (flock(fd, flock_operation) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to take a BSD lock on %s: %m\n", whole_disk_devname);
+ return r;
+ }
+
+ printf("Successfully took a BSD lock: %s\n", whole_disk_devname);
+
+ // take the fd to avoid automatic cleanup
+ int ret_fd = fd;
+ fd = -1;
+ return ret_fd;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Invalid number of parameters.\n");
+ return EXIT_FAILURE;
+ }
+
+ // try to take an exclusive and nonblocking BSD lock
+ __attribute__((cleanup(closep))) int fd =
+ lock_whole_disk_from_devname(
+ argv[1],
+ O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY,
+ LOCK_EX|LOCK_NB);
+
+ if (fd < 0)
+ return EXIT_FAILURE;
+
+ /**
+ * The device is now locked until the return below.
+ * Now you can safely manipulate the block device.
+ **/
+
+ return EXIT_SUCCESS;
+}
+```