Don't worry. I didn't laugh at you. I was enjoying that You found the same workaround I figured for these issues.
That's a very kind answer, thank you. You are a wholesome person.
Here's an excerpt of what ChatGPT 5.4 Plus Extended Thinking generated about this issue in case it casts some light. If it says anything outrageous, please shout at it (I'm speaking to everybody, not to the OP), not at me. I'm only trying to help. Also, be sure it's really outrageous before shouting, because it may not be once considered carefully.
This looks much more like a
cp(1)() fast-path problem than a basic NFS permissions problem. ZFS snapshots are exposed under
.zfs/snapshot, and those snapshots are read-only. FreeBSD’s
copy_file_range(2)() is an in-kernel copy primitive that may use a file-system-specific technique when source and destination appear to be on the same file system, and recent FreeBSD discussion still refers to
cp(1)() trying to use
copy_file_range(2)(). That combination fits your symptom very well: reading the snapshot works, writing the live file works, but
cp(1)() fails with
Read-only file system when it tries the optimized copy path across the snapshot/live boundary.
So the likely failure is not “
NFS cannot handle a partly writable tree” in the simple sense. It is more specific: the exported namespace contains both the live dataset and the snapshot view, and
cp(1)() is trying an in-kernel copy operation instead of a plain read/write loop. Also, the closer FreeBSD term here is
block cloning or an in-kernel copy optimization, not a “
range-hardlink.”
copy_file_range(2)() explicitly allows file-system-specific techniques, and
VOP_COPY_FILE_RANGE(9)() is the vnode-level hook behind that mechanism.