Nix fixes#27
Conversation
| } as *mut libc::c_char, | ||
| if buf_len == 0 { fakebuf.len() } else { buf_len }, | ||
| ) | ||
| }; |
There was a problem hiding this comment.
The fix for the zero-length readlink buffer in bytecodealliance/lucet#213 looks a little simpler; would it make sense to do the same thing here? That said, I do like the comment here about investigating having nix take care of this detail transparently, so please preserve that comment in any case :-).
There was a problem hiding this comment.
Hmm, do you know if they've double checked it for this particular use case? I'm asking because actually, I've already tried something similar before, but due to the way nix::fcntl::readlinkat is implemented, it wouldn't work for me. Perhaps I was doing something wrong. But to elaborate, here's the readlinkat impl taken verbatim from the nix crate (here's the link):
pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
let res = path.with_nix_path(|cstr| {
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
})?;
wrap_readlink_result(buffer, res)
}And in turn wrap_readlink_result does the following:
fn wrap_readlink_result(buffer: &mut[u8], res: ssize_t) -> Result<&OsStr> {
match Errno::result(res) {
Err(err) => Err(err),
Ok(len) => {
if (len as usize) >= buffer.len() {
Err(Error::Sys(Errno::ENAMETOOLONG))
} else {
Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
}
}
}
}Due to the wrap_readlink_result, if we ever try to read link that's longer than the buffer we provide, nix will trip with ENAMETOOLONG which is exactly the error I was getting when trying the same approach as bytecodealliance/lucet#213 for zero-length buffer.
@jedisct1, @sunfishcode any thoughts and help on this will be much appreciated! :-)
There was a problem hiding this comment.
In the comment above, when I said "it wouldn't work for me" I meant that bytecodealliance/lucet#213 approach wouldn't pass the test_readlink_no_buffer test in sunfishcode/misc-tests. As I pointed out above, this is due to the fact that, the nix crate doesn't only wrap libc::readlinkat syscall but actually checks whether the passed in buffer is larger than the actual number of bytes written to the buffer. So, even if we pass in a buffer of length 1, due to the above additional check, I believe that nix::fcntl::readlinkat will always return ENAMETOOLONG for that particular case.
|
Ok, the PR here looks good to me. I'm going to file a separate issue about |
|
I've now filed https://github.com/CraneStation/wasi-common/issues/30. |
Nice one, thanks! |
This PR builds upon #26. It patches up all problems that cause sunfishcode/misc-tests to fail.
Summary of fixes:
fd_renumbershould currently fail when trying to renumber a preopenpath_readlinkshould be able to handle 0-sized buffer (as pointed out by @sunfishcode in the original C impl, Linux requires the input buffer to have positive size, however, POSIX does not enforce that)path_openhad theOFlags the other way around: theelsebranch when determining read/write attributes should haveOFlags::O_RDONLYas a fallthrough and notOFlags::O_WRONLYas it was until nowpath_getnow correctly handles "interesting paths" with a plethora of trailing slashes, "..", etc. To achieve this, instead of iterating over raw paths (using bytes), we usestd::path::Componentsfunctionality instead; it is still necessary to preserver the path's trailing slash so that opening a file as a dir fails as expectedpath_getnow correctly fails withEILSEQin the presence of spurious NULs in the input pathfdencoding bytecodealliance/wasmtime#173 topath_openpath_opento do with setting theneeded_baseflags in the presence ofOFlag::O_TRUNC: up to now we were incorrectly adjustingneeded_inheritingflags instead ofneeded_basepath_unlink_filenow correctly handles non-Linux *nix OSes, i.e.,unlinkatmay returnEPERMon non-Linux host and it should be adjusted toEISDIR