From dc05d89a0837f43ab6923b53714e675384e6a225 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 13 Jun 2019 22:46:46 +0200 Subject: [PATCH] Fix path_readlink: with a 0-sized buffer should succeed --- src/hostcalls/fs.rs | 16 ++++++++------ src/sys/unix/hostcalls_impl/fs.rs | 35 ++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs index 684bd137d9..ea10594472 100644 --- a/src/hostcalls/fs.rs +++ b/src/hostcalls/fs.rs @@ -580,16 +580,20 @@ pub fn path_readlink( Ok(slice) => host_impl::path_from_raw(slice).to_owned(), Err(e) => return enc_errno(e), }; - let rights = host::__WASI_RIGHT_PATH_READLINK; let mut buf = match dec_slice_of_mut::(memory, buf_ptr, buf_len) { Ok(slice) => slice, Err(e) => return enc_errno(e), }; - let host_bufused = - match hostcalls_impl::path_readlink(wasi_ctx, dirfd, path.as_os_str(), rights, &mut buf) { - Ok(host_bufused) => host_bufused, - Err(e) => return enc_errno(e), - }; + let host_bufused = match hostcalls_impl::path_readlink( + wasi_ctx, + dirfd, + path.as_os_str(), + host::__WASI_RIGHT_PATH_READLINK, + &mut buf, + ) { + Ok(host_bufused) => host_bufused, + Err(e) => return enc_errno(e), + }; match enc_usize_byref(memory, buf_used, host_bufused) { Ok(_) => wasm32::__WASI_ESUCCESS, Err(e) => enc_errno(e), diff --git a/src/sys/unix/hostcalls_impl/fs.rs b/src/sys/unix/hostcalls_impl/fs.rs index ac282a209a..2e385a8adf 100644 --- a/src/sys/unix/hostcalls_impl/fs.rs +++ b/src/sys/unix/hostcalls_impl/fs.rs @@ -516,18 +516,43 @@ pub(crate) fn path_readlink( rights: host::__wasi_rights_t, buf: &mut [u8], ) -> Result { - use nix::fcntl::readlinkat; + use nix::errno::Errno; let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights, 0, false) { Ok((dir, path)) => (dir, path), Err(e) => return Err(e), }; - let target_path = match readlinkat(dir, path.as_os_str(), buf) { - Err(e) => return Err(host_impl::errno_from_nix(e.as_errno().unwrap())), - Ok(target_path) => target_path, + let path_cstr = match std::ffi::CString::new(path.as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return Err(host::__WASI_EINVAL), }; - Ok(target_path.len()) + + // Linux requires that the buffer size is positive, whereas POSIX does not. + // Use a fake buffer to store the results if the size is zero. + // TODO: instead of using raw libc::readlinkat call here, this should really + // be fixed in `nix` crate + let fakebuf: &mut [u8] = &mut [0]; + let buf_len = buf.len(); + let len = unsafe { + libc::readlinkat( + dir, + path_cstr.as_ptr() as *const libc::c_char, + if buf_len == 0 { + fakebuf.as_mut_ptr() + } else { + buf.as_mut_ptr() + } as *mut libc::c_char, + if buf_len == 0 { fakebuf.len() } else { buf_len }, + ) + }; + + if len < 0 { + Err(host_impl::errno_from_nix(Errno::last())) + } else { + let len = len as usize; + Ok(if len < buf_len { len } else { buf_len }) + } } pub(crate) fn path_rename(