mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-23 17:34:00 +03:00
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
This commit is contained in:
parent
661e4251a5
commit
5537165879
@ -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;
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user