Yet another reminder that you have to think about file IO as a concurrency problem: https://thomask.sdf.org/blog/2019/11/09/take-care-editing-bash-scripts.html
Take care editing bash scripts

Imagine I write the following bash script and call it delay.sh. What do you suppose happens when I run ./delay.sh?#!/bin/bashsleep 30#rm -rf --no-preserve-ro...

To confuse matters further, the file saving strategy differs not only between text editors but within the same text editor depending on the context. There are basically two robust strategies for file saves:

1. Write new version to newly created foo.txt~ and atomically rename foo.txt~ to foo.txt.
2. Copy backup of foo.txt to foo.txt~, overwrite foo.txt in place with new version, delete foo.txt~.

Option 1 has better transactional properties (new file has no concurrent readers, original version doesn't have to be restored after crash) and requires less IO but breaks hardlinks.

I haven't surveyed the implementation of different editors to check under which circumstances they choose which strategy but at least in vim's case I know from straces that it defaults to 1 but switches to 2 if the file has multiple hardlinks to avoid breaking them. Some editors always do 1 and others always do 2.

@pervognsen Option 1 would also avoid the problem with editing a running bash script in the link you posted, right? As the open file handle in bash would still point to the original contents, IIUC.
@reedbeta Yes, that's correct.
@reedbeta By the way, I don't think option 1 is realistic on Windows. First, they didn't add a POSIX-style rename/unlink until a later Windows 10 revision with FILE_RENAME_POSIX_SEMANTICS. Second, even if you're on an OS version that supports FILE_RENAME_POSIX_SEMANTICS, if there are concurrent readers they must have opened the target file with FILE_SHARE_DELETE, and you don't get that sharing flag with fopen() so the concurrent rename attempt would just fail.
@pervognsen @reedbeta Another problem with #1 is behind-the-scenes file versioning (OneDrive, Dropbox, etc), which can sometimes freak out if you do this because there's an instant where the file doesn't exist. Of course that's not a problem with the editor as such, but it's still a problem.

@pervognsen Vim has an option for this ('backupcopy').

Also, some tools (IIRC 'crontab -e' is one of them) break if you use the wrong backup strategy.

I was a much happier person before I knew any of that.

@pervognsen Another is that clang uses inode number for #pragma once so you get different errors, if you save a file mid-build, depending on if entire inode is replaced or not.
In the inverse genre (tailing a log file where readers observing concurrent writes is a feature, not a bug) there's also this terrifying but entertaining lightning talk from @bcantrill: https://www.youtube.com/watch?v=vm1GJMp0QN4&t=2475s
Surge 2013: LightningTalks

YouTube
@pervognsen @bcantrill if only there was an OS that got this significantly more right than the others (it's Windows, Windows gets it significantly more right than the others).
@DirtyPunk @bcantrill Windows certainly has much stricter semantics around sharing. E.g. the equivalent of the mmap truncate SIGBUS wouldn't happen on Windows because SetEndOfFile fails if there are any open mappings, so the truncate would fail if there were concurrent mapping-based readers. But this bondage and discipline philosophy around sharing is a double-edged sword. When you think about it, it's crazy that read permission to a log file can block the log writer from truncating it.

@pervognsen @bcantrill Oh, I'm aware of that, I still think Windows got this right and the people pointing at *that infamous issue* got it wrong.

The only issue there is with UI.

POSIX file handling semantics are *fundamentally broken and you can not build correctness on top of them*.

@pervognsen @bcantrill This is why you can use a file based DB like Access or sqllite on a Windows-to-Windows network drive connection and the same thing is fundamentally not possible on nearly any UNIX.
@DirtyPunk @bcantrill The issue is definitely more than just UI. There's no privilege level which allows someone to read but not map a file on Windows. Both of them require the same GENERIC_READ mode. So if you want to expose a log file as readable to some other process, you as the log writer process have to assume you might _never_ be able to truncate the log. At the very least the NT team should have disaggregated those privileges if open mappings can globally block truncates.

@pervognsen @bcantrill No, I'd still argue they made the right choice, because there is no fundamentally correct way to implement that semantic.

It isn't just UI, it's also developer education, but there's a reason why Windows is pretty much the only OS used in environments with strict regulations around file-based records.

@pervognsen @bcantrill Well, I say "no fundamentally correct way" - but full serializable MVCC semantics would get you there. Now, that I would like to see.
@pervognsen the filesystem is the worst IPC method... needlessly hierarchical but not actually hierarchical (e.g. you can't lock or delete a directory and its contents), requires names but not fully arbitrary names, has busted atomicity/locking and ties access control to UIDs and with only a bolted-on, hard-to-shore-up capability interface. and you're required to use this broken IPC system for any data you want persisted, but you can't even cheaply query (much less assume) integrity across runs!