fix(ext/fs): truncate should not follow a terminal symlink (#35239)
The permission check for `Deno.truncate` / `Deno.truncateSync` is
performed no-follow: it is checked against the path you name, without
canonicalizing symlinks. The implementation, however, opened the path with a plain
`OpenOptions::write(true).open(path)`, which follows symlinks on Unix. A
writable symlink sitting at a path inside an allowed scope could
therefore be used to truncate (zero out) the file it points to, even when the target
lives outside that scope.
This opens the path with `O_NOFOLLOW` on Unix so that truncating through
a terminal symlink fails with `ELOOP` rather than silently truncating the
link's target. Truncating a regular file is unchanged, and only the final path
component is affected (intermediate symlinks are still resolved,
matching the existing path model). Windows behavior is unchanged.
Added unit tests covering both the sync and async paths: a symlink whose
target holds data is truncated through the link, and the operation now throws
while the target is left untouched.