[PATCH 1/4] libext2fs: add POSIX advisory locking to the unix IO manager

Darrick J. Wong
2025-11-06T22:30:17Z

Add support for using flock() to protect the files opened by the Unix IO
manager so that other fuse2fs servers cannot stomp all over the
filesystem. We may some day want to adopt this in e2fsck.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
lib/ext2fs/ext2_io.h | 12 +++++++
debian/libext2fs2t64.symbols | 2 +
lib/ext2fs/io_manager.c | 16 +++++++++
lib/ext2fs/unix_io.c | 71 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
index 39a4e8fcf6b515..61865d54d82490 100644
--- a/lib/ext2fs/ext2_io.h
+++ b/lib/ext2fs/ext2_io.h
@@ -102,7 +102,8 @@ struct struct_io_manager {
unsigned long long count);
errcode_t (*zeroout)(io_channel channel, unsigned long long block,
unsigned long long count);
- long reserved[14];
+ errcode_t (*flock)(io_channel channel, unsigned int flock_flags);
+ long reserved[13];
};

#define IO_FLAG_RW 0x0001
@@ -112,6 +113,13 @@ struct struct_io_manager {
#define IO_FLAG_THREADS 0x0010
#define IO_FLAG_NOCACHE 0x0020

+/* Prevent other programs from reading or writing to underlying storage */
+#define IO_CHANNEL_FLOCK_EXCLUSIVE 0x1
+/* Prevent other programs from writing to underlying storage */
+#define IO_CHANNEL_FLOCK_SHARED 0x2
+/* Return EBUSY if the lock cannot be taken immediately */
+#define IO_CHANNEL_FLOCK_TRYLOCK 0x4
+
/*
* Convenience functions....
*/
@@ -145,6 +153,8 @@ extern errcode_t io_channel_alloc_buf(io_channel channel,
extern errcode_t io_channel_cache_readahead(io_channel io,
unsigned long long block,
unsigned long long count);
+extern errcode_t io_channel_flock(io_channel io, unsigned int flock_flags);
+extern errcode_t io_channel_funlock(io_channel io);

#ifdef _WIN32
/* windows_io.c */
diff --git a/debian/libext2fs2t64.symbols b/debian/libext2fs2t64.symbols
index a3042c3292da93..b4d80161f1e1b4 100644
--- a/debian/libext2fs2t64.symbols
+++ b/debian/libext2fs2t64.symbols
@@ -693,6 +693,8 @@ libext2fs.so.2 libext2fs2t64 #MINVER#
io_channel_alloc_buf@Base 1.42.3
io_channel_cache_readahead@Base 1.43
io_channel_discard@Base 1.42
+ io_channel_flock@Base 1.47.99
+ io_channel_funlock@Base 1.47.99
io_channel_read_blk64@Base 1.41.1
io_channel_set_options@Base 1.37
io_channel_write_blk64@Base 1.41.1
diff --git a/lib/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c
index dca6af09996b70..791ec7d14adbba 100644
--- a/lib/ext2fs/io_manager.c
+++ b/lib/ext2fs/io_manager.c
@@ -150,3 +150,19 @@ errcode_t io_channel_cache_readahead(io_channel io, unsigned long long block,

return io->manager->cache_readahead(io, block, count);
}
+
+errcode_t io_channel_flock(io_channel io, unsigned int flock_flags)
+{
+ if (!io->manager->flock)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io->manager->flock(io, flock_flags);
+}
+
+errcode_t io_channel_funlock(io_channel io)
+{
+ if (!io->manager->flock)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io->manager->flock(io, 0);
+}
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 1456b4d4bbe212..abd33ba839f7e9 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -64,6 +64,9 @@
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif

#if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */
@@ -135,6 +138,7 @@ struct unix_private_data {
int flags;
int align;
int access_time;
+ int unix_flock_flags;
ext2_loff_t offset;
struct unix_cache *cache;
unsigned int cache_size;
@@ -875,6 +879,68 @@ int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
#endif
}

+#ifdef HAVE_SYS_FILE_H
+static errcode_t unix_funlock(io_channel channel)
+{
+ struct unix_private_data *data;
+ int ret;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->unix_flock_flags) {
+ ret = flock(data->dev, LOCK_UN);
+ if (ret)
+ return errno;
+
+ data->unix_flock_flags = 0;
+ }
+
+ return 0;
+}
+
+static errcode_t unix_flock(io_channel channel, unsigned int flock_flags)
+{
+ struct unix_private_data *data;
+ int unix_flock_flags = 0;
+ errcode_t ret;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ ret = unix_funlock(channel);
+ if (ret)
+ return ret;
+
+ if (flock_flags & IO_CHANNEL_FLOCK_EXCLUSIVE)
+ unix_flock_flags |= LOCK_EX;
+
+ if (flock_flags & IO_CHANNEL_FLOCK_SHARED)
+ unix_flock_flags |= LOCK_SH;
+
+ if (flock_flags & IO_CHANNEL_FLOCK_TRYLOCK)
+ unix_flock_flags |= LOCK_NB;
+
+ if (!unix_flock_flags)
+ return 0;
+
+ ret = flock(data->dev, unix_flock_flags);
+ if (ret < 0)
+ return errno;
+
+ data->unix_flock_flags = unix_flock_flags & ~LOCK_NB;
+ return 0;
+}
+#else
+#define unix_flock NULL
+
+static errcode_t unix_funlock(io_channel channel)
+{
+ return 0;
+}
+#endif /* HAVE_SYS_FILE_H */

static errcode_t unix_open_channel(const char *name, int fd,
int flags, io_channel *channel,
@@ -1061,6 +1127,7 @@ static errcode_t unix_open_channel(const char *name, int fd,

cleanup:
if (data) {
+ unix_funlock(io);
if (io->manager != unixfd_io_manager && data->dev >= 0)
close(data->dev);
if (data->cache) {
@@ -1147,6 +1214,8 @@ static errcode_t unix_close(io_channel channel)
retval = flush_cached_blocks(channel, data, 0);
#endif

+ unix_funlock(channel);
+
if (channel->manager != unixfd_io_manager && close(data->dev) < 0)
retval = errno;
free_cache(data);
@@ -1683,6 +1752,7 @@ static struct struct_io_manager struct_unix_manager = {
.discard = unix_discard,
.cache_readahead = unix_cache_readahead,
.zeroout = unix_zeroout,
+ .flock = unix_flock,
};

io_manager unix_io_manager = &struct_unix_manager;
@@ -1704,6 +1774,7 @@ static struct struct_io_manager struct_unixfd_manager = {
.discard = unix_discard,
.cache_readahead = unix_cache_readahead,
.zeroout = unix_zeroout,
+ .flock = unix_flock,
};

io_manager unixfd_io_manager = &struct_unixfd_manager;