Cross-platform library for file locking?

I was looking for a way to lock region of a file. Not whole file...

From Wikipedia:

Most operating systems support the concept of record locking, which means that individual records within any given file may be locked, thereby increasing the number of concurrent update processes.

Since Rust standard library has no API for file locking... I was looking for an alternative.

Is there any rust crate for byte-range, cross-platform API for file locking ?

More complicated thing is, library should also use None Blocking API, and bind that in an async environment, Such as Tokio runtime for rust... But even if we ignore this feature, Is there any Rust, C, C++ cross-platform library for file locking ?

Even after some searching, I couldn't find any, which is kind of surprising. It's certainly possible: Java includes FileLock, but I couldn't find library for any of C, C++, Rust.

OS support for file locking is very inconsistent. There are various methods with differing OS support and different semantics. Do locks unlock as soon as one fd to them closes or all of them? What if an fd to a locked file is sent to another process? Which processes need to exit for the lock to unlock. Are the locks advisory (optional) or mandatory? If it is advisory you need to explicitly pass a flag to respect the lock or do you need a flag to disrespect it? AFAIK there are locking api's with different answers for each of these questions and most OSes only support some of these locking api's.

2 Likes

While fcntl locks support locking byte ranges, the behavior standardized by POSIX for fcntl is insane.

The second strange behaviour of fcntl() locks is this: the lock doesn't belong to a file descriptor, it belongs to a (pid,inode) pair. Worse, if you close any fd referring to that inode, it releases all your locks on that inode. For example, let's say you open /etc/passwd and lock some bytes, and then call getpwent() in libc, which usually opens /etc/passwd, reads some stuff, and closes it again. That process of opening /etc/passwd and closing it - which has nothing to do with your existing fd open on /etc/passwd - will cause you to lose your locks!

That behaviour is certifiably insane, and there's no possible justification for why it should work that way. But it's how it's always worked, and POSIX standardized it, and now you're stuck with it.

Furthermore, flock is sometimes implemented by wrapping fcntl — which means that on any such system, flock is also insane.

In addition, there have classically been issues with file locking remote file systems like NFS, because emulating atomic locking over a network is very hard. Some people have worked very hard to mitigate the situation though.

To my mind, portable locking with range locking is too much to ask for. I just want flock advisory-whole-file-locks to work consistently across all operating systems for local file systems.

I can deal with LockFileEx on Windows as a workaround, because the failure mode of emulating flock with LockFileEx is at least a fail-safe "permission denied" as opposed to the fail-dangerous "permission granted" of fcntl.

Question for the group: It's 2021. How many operating systems are left that don't provide sane flock?

Most operating systems may "support the concept of record locking", but any OS which provides such support via POSIX semantics is worse than useless. (Unless it doesn't support processes — then it's "fine". :P)

I'm glad that std doesn't define a file locking API. It's impossible to provide a portable interface which behaves reliably across all target operating systems.

1 Like

Java have to provide something because otherwise you wouldn't have anything (that's how Java works).

But it's neither cross-platform nor consistent. Because cross-platform consistent byte-level locking doesn't exist.

In C/C++/Rust you can just use native capabilities and offer saner high-level API to the other libraries (SQL, e.g.)

1 Like

To my mind, portable locking with range locking is too much to ask for. I just want flock advisory-whole-file-locks to work consistently across all operating systems for local file systems.

As I understand, fs2 crate, which is for "cross-platform file locks and file duplication", does provide such implementation.

Though it does provide locking (only whole file), It doesn't provide byte-range locking. It use flock(2) on Unix. which don't provide byte-range lock.

The fs2 crate uses flock on some systems and fcntl on other systems. Flock locks are per fd, while fcntl locks are per (pid,inode) pair. With flock opening the file without lock and closing it again (eg because a system library does this behind your back) doesn't unlock the file. With fcntl the moment any fd referencing the file is closed even if unrelated to the fd using which the lock was acquired will unlock the file. This happens even if the original fd that was used to lock the file still exists. This is not a portable file locking implementation! Portable means not only using the same api, but also behaving the same way. Another thing is that both smb and nfs on linux only support one of flock and fcntl. Both differ in which one they support.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.