While playing around with pruning for the kernel, I noticed that only bitcoind's init locks the blocks directory (AppInitLockDirectories). The kernel doesn't.
Pointing a kernel program at a running node's data directory fails, but only because LevelDB locks blocks/index. To avoid a second copy of the chain, the obvious workaround is to use the node's blocks directory with the program's own data directory, or in-memory databases. It starts without error, but with an empty block index BlockManager writes from offset 0 of the node's blk00000.dat, which FlatFileSeq::Open opens in place with "rb+". The node keeps running and serves the overwritten bytes as its own blocks.
#31674 added the blocks directory lock to bitcoind and noted that "the kernel does not currently do any dir locking, but it should". This PR follows sedited's suggestion to add the lock to the BlockManager constructor, and is based on the lock_blocksdir_raii commit.
BlockManager holds .lock for its whole lifetime through BlocksDirLock, its first member, so the directory is locked before xor.dat is touched. Creating a chainstate manager on a blocks directory locked by another process, bitcoind or a kernel user, fails.
In bitcoind, init already holds the lock, so LockDirectory returns success without locking again. The lock is released when the BlockManager is destroyed rather than at exit. That happens at shutdown, and when a GUI user retries a failed chainstate load with a reindex, after which the new BlockManager reacquires it. A blocks directory used through the kernel now has to be writable, as bitcoind already required.
Added a regression test to feature_filelock.py.