diff --git a/src/sys/bsd.rs b/src/sys/bsd.rs index 6e772fa4..60c99b8f 100644 --- a/src/sys/bsd.rs +++ b/src/sys/bsd.rs @@ -56,13 +56,18 @@ fn allocate_loop libc::ssize_t>(f: F) -> io::Resul |buf| unsafe { let (ptr, len) = slice_parts(buf); let new_len = cvt(f(ptr, len))?; - assert!( - new_len <= len, - "OS returned length {new_len} greater than buffer size {len}" - ); - Ok(slice::from_raw_parts_mut(ptr.cast(), new_len)) + if new_len < len { + Ok(slice::from_raw_parts_mut(ptr.cast(), new_len)) + } else { + // If the length of the value isn't strictly smaller than the length of the value + // read, there may be more to read. Fake an ERANGE error so we can try again with a + // bigger buffer. + Err(io::Error::from_raw_os_error(crate::sys::ERANGE)) + } }, - || cvt(f(ptr::null_mut(), 0)), + // Estimate size + 1 because, on freebsd, the only way to tell if we've read the entire + // value is read a value smaller than the buffer we passed. + || Ok(cvt(f(ptr::null_mut(), 0))? + 1), ) } diff --git a/src/util.rs b/src/util.rs index 12e89766..2c2ca222 100644 --- a/src/util.rs +++ b/src/util.rs @@ -20,7 +20,7 @@ where S: FnMut() -> io::Result, { // Start by assuming the return value is <= 4KiB. If it is, we can do this in one syscall. - const INITIAL_BUFFER_SIZE: usize = 4096; + const INITIAL_BUFFER_SIZE: usize = 2; match get_value(&mut [MaybeUninit::::uninit(); INITIAL_BUFFER_SIZE]) { Ok(val) => return Ok(val.to_vec()), Err(e) if e.raw_os_error() != Some(crate::sys::ERANGE) => return Err(e),