Skip to content

Commit 2dbc2b4

Browse files
committed
work around gz functions not being posix-compatible
The C source assumes that when a write succeeds it writes all bytes. that is not strictly required, but effectively true in most cases where zlib is used. However, miri does not respect this. So when running miri, explicitly loop until all bytes are written
1 parent 1d218ab commit 2dbc2b4

File tree

2 files changed

+78
-6
lines changed
  • libz-rs-sys-cdylib/src
  • test-libz-rs-sys/src

2 files changed

+78
-6
lines changed

libz-rs-sys-cdylib/src/gz.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,41 @@ fn gz_init(state: &mut GzState) -> Result<(), ()> {
18081808
Ok(())
18091809
}
18101810

1811+
#[cfg(not(miri))]
1812+
unsafe fn write(fd: c_int, buf: *const c_void, count: size_t) -> libc::ssize_t {
1813+
unsafe { libc::write(fd, buf, count) }
1814+
}
1815+
1816+
/// Deal with miri often not writing the full buffer in one go. That is valid, but zlib assumes
1817+
/// that writes are to true files and succeed unless e.g. the disk is full.
1818+
#[cfg(miri)]
1819+
unsafe fn write(fd: c_int, buf: *const c_void, count: size_t) -> libc::ssize_t {
1820+
let mut total_written = 0isize;
1821+
loop {
1822+
let ret = unsafe {
1823+
libc::write(
1824+
fd,
1825+
buf.add(total_written as usize),
1826+
count - total_written as usize,
1827+
)
1828+
};
1829+
1830+
match ret.cmp(&0) {
1831+
Ordering::Less => return ret,
1832+
Ordering::Equal => return total_written,
1833+
Ordering::Greater => {
1834+
total_written += ret;
1835+
1836+
if total_written as usize == count {
1837+
break;
1838+
}
1839+
}
1840+
}
1841+
}
1842+
1843+
total_written
1844+
}
1845+
18111846
// Compress whatever is at avail_in and next_in (unless in direct mode) and write
18121847
// to the output file.
18131848
//
@@ -1824,7 +1859,7 @@ fn gz_comp(state: &mut GzState, flush: c_int) -> Result<(), ()> {
18241859
// Write directly if requested.
18251860
if state.direct {
18261861
let got = unsafe {
1827-
libc::write(
1862+
write(
18281863
state.fd,
18291864
state.stream.next_in.cast::<c_void>(),
18301865
state.stream.avail_in as _,
@@ -1868,7 +1903,7 @@ fn gz_comp(state: &mut GzState, flush: c_int) -> Result<(), ()> {
18681903
return Err(());
18691904
}
18701905
if have != 0 {
1871-
let ret = unsafe { libc::write(state.fd, state.next.cast::<c_void>(), have as _) };
1906+
let ret = unsafe { write(state.fd, state.next.cast::<c_void>(), have as _) };
18721907
if ret != have as _ {
18731908
unsafe { gz_error(state, Some((Z_ERRNO, "write error"))) };
18741909
return Err(());

test-libz-rs-sys/src/gz.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,43 @@ fn binary_mode(mode: c_int) -> c_int {
3535
}
3636
}
3737

38+
#[cfg(not(miri))]
39+
unsafe fn write(fd: c_int, buf: *const c_void, count: size_t) -> libc::ssize_t {
40+
unsafe { libc::write(fd, buf, count) }
41+
}
42+
43+
/// Deal with miri often not writing the full buffer in one go. That is valid, but zlib assumes
44+
/// that writes are to true files and succeed unless e.g. the disk is full.
45+
#[cfg(miri)]
46+
unsafe fn write(fd: c_int, buf: *const c_void, count: size_t) -> libc::ssize_t {
47+
use std::cmp::Ordering;
48+
49+
let mut total_written = 0isize;
50+
loop {
51+
let ret = unsafe {
52+
libc::write(
53+
fd,
54+
buf.add(total_written as usize),
55+
count - total_written as usize,
56+
)
57+
};
58+
59+
match ret.cmp(&0) {
60+
Ordering::Less => return ret,
61+
Ordering::Equal => return total_written,
62+
Ordering::Greater => {
63+
total_written += ret;
64+
65+
if total_written as usize == count {
66+
break;
67+
}
68+
}
69+
}
70+
}
71+
72+
total_written
73+
}
74+
3875
// Try to gzopen a file path with a specified mode, and panic if the result is unexpected.
3976
// NOTE: This is a macro, instead of a function, so that the test runner will report errors
4077
// with the line number where it is invoked.
@@ -331,7 +368,7 @@ fn gzread_special_cases() {
331368
};
332369
assert_ne!(fd, -1);
333370
assert_eq!(
334-
unsafe { libc::write(fd, STRING2.as_ptr().cast::<c_void>(), STRING2.len() as _) },
371+
unsafe { write(fd, STRING2.as_ptr().cast::<c_void>(), STRING2.len() as _) },
335372
STRING2.len() as _
336373
);
337374
assert_eq!(unsafe { libc::close(fd) }, 0);
@@ -793,7 +830,7 @@ fn gzoffset_gztell_read() {
793830
};
794831
assert_ne!(fd, -1);
795832
assert_eq!(
796-
unsafe { libc::write(fd, PADDING.as_ptr().cast::<c_void>(), OFFSET as _) },
833+
unsafe { write(fd, PADDING.as_ptr().cast::<c_void>(), OFFSET as _) },
797834
OFFSET as _
798835
);
799836
let source_name = crate_path("src/test-data/issue-109.gz");
@@ -809,7 +846,7 @@ fn gzoffset_gztell_read() {
809846
break;
810847
}
811848
assert_eq!(
812-
unsafe { libc::write(fd, buf.as_ptr().cast(), ret as _) },
849+
unsafe { write(fd, buf.as_ptr().cast(), ret as _) },
813850
ret as _
814851
);
815852
}
@@ -1703,7 +1740,7 @@ fn gzseek_read() {
17031740
break;
17041741
}
17051742
assert_eq!(
1706-
unsafe { libc::write(fd, buf.as_mut_ptr().cast::<c_void>(), ret as _) },
1743+
unsafe { write(fd, buf.as_mut_ptr().cast::<c_void>(), ret as _) },
17071744
ret as _
17081745
);
17091746
}

0 commit comments

Comments
 (0)