ocaml-gdocs: atomic-write store and truncate HTTP error bodies
Two corrections surfaced by reviewing the equivalent code in
[ocaml-gsheets]:
- [Store.save_file] used [Eio.Path.save ~create:(`Or_truncate 0o600)]
followed by [Unix.chmod 0o600]. [Or_truncate] only applies the mode
on file creation, so when an existing file at the target was at a
wider mode (left over from a different tool, or a buggy older
version), the new content was written and briefly observable at the
wider mode before [chmod] tightened it. There was also no atomicity:
a crash mid-write left a half-written or zero-byte file. Replace
with the standard pattern -- write to a sibling [<path>.tmp.<pid>]
with [`Exclusive 0o600], then [Eio.Path.rename] over the target.
The new content is never observable at a wider mode (the file
inherits the tmp file's [0o600]), and a crash before the rename
leaves the original target untouched. Best-effort [unlink] of any
stale [.tmp.<pid>] from a previous crash before the [Exclusive]
open.
- [err_http] in both [Gdocs] and [Comments] piped the upstream
response body straight into a [`Msg]. A 4xx/5xx body from a CDN
edge can be multi-KB of HTML, which then dumps unbounded onto the
user's stderr. Add a local [truncate_body] that caps at 256 bytes
and appends ["... [truncated; <N> bytes total]"] so the user has a
rough sense of what was elided.