3 ms·
Moving to a fully transactional model for filesystems sounds like it will create more pain than help, especially given the historical tendency of filesystem imp
by jcranmer 4y ago
Moving to a fully transactional model for filesystems sounds like it will create more pain than help, especially given the historical tendency of filesystem implementations to cheat on semantics every opportunity they get.
> - fopen(..., 'r') could, by default, operate on a COW snapshot of a file rather than on the raw file on the filesystems. No more having to worry about races with other processes.
You can go a few steps further. If you have some sort of O_ATOMIC mode on creating a file descriptor, that operates on a separate snapshot in both read and write mode (and in the case of open for write, will atomically replace the existing file with the newly written copy on close), that would have more correct semantics than existing POSIX I/O for most applications. You can't make it default at the syscall level, but most language I/O abstractions could probably make it default. Notably, this can be implemented in the OS kernel without actually touching filesystem implementations themselves, just the way the kernel arbitrates between userspace and the filesystem.
Another interesting file mode that fixes many use cases is having some sort of "atomic append" for files that handles the use case of simultaneous readers and writers of log files. Set up the API so that you can guarantee that multiple writers each get to add their own atomic log message (without other synchronization), and that readers can never see just part of a log message.
The difficult part of the filesystem to reason about is the file metadata and directory listings. I've seen suggestions in the past to use file descriptors instead of strings as the basis for handling directory queries in language implementations. If you don't do at least that much, it's basically impossible to solve TOCTOU issues, but I'm not sure there's a lot you can do with COW snapshotting of directories that still manage to be acceptable in performance.
> Temp files, like database temp tables, could by default only be visible to the current process (and optionally its children) or even the current thread. No more having to worry about temp file races and related issues.
Creating unnamed temporary files is a solved problem. Named temporary files is more difficult, but create-only-if-doesn't-exist exists on all platforms, and most language APIs have some way of getting to that (it's "x" flag in C11's fopen).
- gpderetta 4y ago>Another interesting file mode that fixes many use cases is having some sort of "atomic append" for files that handles the use case of simultaneous readers and writers of log files My understanding is that O_APPEND gives exactly this guarantee. Readers can read partial records, but with proper record marking it shouldn't be an issue.
- Filligree 4y agoThat assumes your data can be written in a single write(2) call. I don't know what size record is guaranteed to be writable that way, if any, but I wouldn't assume it's infinity. Short writes aren't illegal. Perhaps you were pointing at this, but then you need to work around the limitations of the OS, and... Why can't we just have transactions?
- eqvinox 4y ago> Short writes aren't illegal. They aren't, but in practice I don't think they can happen in the scenarios I looked at (= BSD and Linux writes to "normal" file systems backed by mass storage) - unless you run out of disk space or your disk has some low-level error. But, again, the problem is that the API does not provide this guarantee. I think it's generally true in practice, but… you can't rely on it. (My context was a thread-capable lock-free logging backend; we just call writev() with the entire log message at once. Of course there's still locking to do this in the kernel, but that's always there — rather deal with one lock than two.)
- Filligree 4y agoHonestly that's the worst of all worlds: Behaviour that's reliable, but not guaranteed. People will definitely start to rely on it, and then you can't change it later when it makes sense, which means the relevant APIs can't be reworked to better treat disks as the asynchronous network devices they nowadays are. Even though the APIs are flexible enough (per documentation) to manage that. On the flip side, when you're the developer, you can't rely on the behaviour without feeling dirty, and/or needing separate codepaths per OS because this isn't specified, and it's a bad idea to rely on if you aren't absolutely sure you're on Linux. And then there's the fact that, while write(2) on O_APPEND is certainly atomic in most cases... I simply can't trust that's true for arbitrarily large writes, because it certainly isn't specified; it might do short writes above 64k, or 64M, or something. So I'll need an error-handling path of some sort anyway.
- gpderetta 4y ago