2022-10-15 08:46:49

by zhanchengbin

[permalink] [raw]
Subject: [PATCH] lib/ext2fs/unix_io.c: add flock operation to struct_unix_manager in e2fsprogs

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 before
writing superblock.

Signed-off-by: zhanchengbin <[email protected]>
---
lib/ext2fs/unix_io.c | 90 +++++++++++++++++++++++++++++++++++++++++--
util/android_config.h | 1 +
2 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index e53db333..a0ca8b37 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -61,6 +61,9 @@
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
#if HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
@@ -634,18 +637,93 @@ static errcode_t flush_cached_blocks(io_channel
channel,
#endif
#endif

+/* return 0 on success */
+int blkdev_lock(int fd, const char *devname)
+{
+ int oper, rc;
+ char *lock_mode = NULL;
+
+ lock_mode = getenv("LOCK_BLOCK_DEVICE");
+ if (!lock_mode)
+ lock_mode = "1";
+
+ if (strcasecmp(lock_mode, "yes") == 0 ||
+ strcmp(lock_mode, "1") == 0)
+ oper = LOCK_EX;
+
+ else if (strcasecmp(lock_mode, "nonblock") == 0)
+ oper = LOCK_EX | LOCK_NB;
+
+ else if (strcasecmp(lock_mode, "no") == 0 ||
+ strcmp(lock_mode, "0") == 0)
+ return 0;
+ else {
+ printf("unsupported lock mode: %s", lock_mode);
+ return -EINVAL;
+ }
+
+ if (!(oper & LOCK_NB)) {
+ /* Try non-block first to provide message */
+ rc = flock(fd, oper | LOCK_NB);
+ if (rc == 0)
+ return 0;
+ if (rc != 0 && errno == EWOULDBLOCK) {
+ fprintf(stderr, "%s: device already locked, waiting to get lock ... ",
+ devname);
+ }
+ }
+ rc = flock(fd, oper);
+ if (rc != 0) {
+ switch (errno) {
+ case EWOULDBLOCK: /* LOCK_NB */
+ printf("%s: device already locked", devname);
+ break;
+ default:
+ printf("%s: failed to get lock", devname);
+ }
+ }
+ return rc;
+}
+
+/* return 0 on success */
+int blkdev_unlock(int fd)
+{
+ int oper, rc;
+ char *lock_mode = NULL;
+
+ lock_mode = getenv("LOCK_BLOCK_DEVICE");
+ if (!lock_mode)
+ lock_mode = "1";
+
+ if (strcasecmp(lock_mode, "no") == 0 ||
+ strcmp(lock_mode, "0") == 0)
+ return 0;
+ else
+ oper = LOCK_UN;
+
+ rc = flock(fd, oper);
+ return rc;
+}
+
int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
{
+ int fd = -1;
if (mode)
#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
- return open64(pathname, flags, mode);
+ fd = open64(pathname, flags, mode);
else
- return open64(pathname, flags);
+ fd = open64(pathname, flags);
#else
- return open(pathname, flags, mode);
+ fd = open(pathname, flags, mode);
else
- return open(pathname, flags);
+ fd = open(pathname, flags);
#endif
+ if (blkdev_lock(fd, pathname) != 0) {
+ printf("File %s is locked\n", pathname);
+ exit(-1);
+ }
+
+ return fd;
}

int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
@@ -926,6 +1004,10 @@ static errcode_t unix_close(io_channel channel)
retval = flush_cached_blocks(channel, data, 0);
#endif

+ if(blkdev_unlock(data->dev) != 0){
+ printf("blkdev unlock error\n");
+ retval = errno;
+ }
if (close(data->dev) < 0)
retval = errno;
free_cache(data);
diff --git a/util/android_config.h b/util/android_config.h
index 6ac16fec..4dd3b69f 100644
--- a/util/android_config.h
+++ b/util/android_config.h
@@ -28,6 +28,7 @@
#define HAVE_UTIME_H 1

#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_FILE_H 1
#if !defined(__APPLE__)
# define HAVE_SYS_SYSMACROS_H 1
#endif
--
2.33.0