Subject: bug#58598: [PATCH]parted: acquire flock before modifying the device superblock

From: "zhanghongtao (A)" <[email-masked]>
Date: Mon, 17 Oct 2022 21:14:41 +0800

We noticed that systemd has an issue about symlink unreliable caused by
formatting filesystem and systemd operating on same device.
Issue Link: https://github.com/systemd/systemd/issues/23746

According to systemd doc, a BSD flock needs to be acquired before
formatting the device.
Related Link: https://systemd.io/BLOCK_DEVICE_LOCKING/

So we acquire flock after opening the device.
But this patch causes multiple parted processes to fail to run
simultaneously in interactive mode.
Or, can we add an option for lock mode?

Signed-off-by: Hongtao Zhang <[email-masked]>
---
 libparted/arch/linux.c | 49 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c
index f3bf14d..f7645f4 100644
--- a/libparted/arch/linux.c
+++ b/libparted/arch/linux.c
@@ -22,6 +22,7 @@
 #include <linux/blkpg.h>
 #include <parted/parted.h>
 #include <parted/debug.h>
+#include <sys/file.h>
 #if defined __s390__ || defined __s390x__
 #include <parted/fdasd.h>
 #endif
@@ -1706,6 +1707,50 @@ _device_open_ro (PedDevice* dev)
     return rc;
 }

+static int lock_blkdev (int fd, char *path)
+{
+        int rc, msg = 0;
+
+        /* Try non-block first to provide message */
+        rc = flock(fd, LOCK_EX | LOCK_NB);
+        if (rc == 0)
+                return 0;
+        if (rc != 0 && errno == EWOULDBLOCK) {
+                ped_exception_throw (
+                        PED_EXCEPTION_WARNING,
+                        PED_EXCEPTION_OK,
+                        _("%s: device already locked, "
+                        "waiting to get lock ..."),
+                        path);
+                msg = 1;
+        }
+
+        rc = flock (fd, LOCK_EX);
+        if (rc != 0) {
+                switch (errno) {
+                case EWOULDBLOCK: /* LOCK_NB */
+                        ped_exception_throw (
+                                PED_EXCEPTION_WARNING,
+                                PED_EXCEPTION_OK,
+                                _("%s: device already locked"),
+                                path);
+                        break;
+                default:
+                        ped_exception_throw (
+                                PED_EXCEPTION_WARNING,
+                                PED_EXCEPTION_OK,
+                                _("%s: failed to get lock"),
+                                path);
+                }
+        } else if (msg)
+                ped_exception_throw (
+                        PED_EXCEPTION_WARNING,
+                        PED_EXCEPTION_OK,
+                        _("%s: lock ok"),
+                        path);
+        return rc;
+}
+
 static int
 linux_open (PedDevice* dev)
 {
@@ -1747,6 +1792,10 @@ retry:
                 }
         } else {
                 dev->read_only = 0;
+                if (lock_blkdev (arch_specific->fd, dev->path) != 0) {
+                        close (arch_specific->fd);
+                        return 0;
+                }
         }

         _flush_cache (dev);
--
2.33.0