Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .bran-export.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema_version": 1,
"source_repository": "alphazede/bran-dev",
"source_commit": "32f37928f77b21b1d1a55a22862c91f61fce7ed0",
"source_commit": "cd396e16a2f4c9a53d95cab6eb2ef2d617964351",
"version": "0.1.0",
"public_repository": "alphazede/bran",
"files": [
Expand Down Expand Up @@ -740,8 +740,8 @@
{
"path": "xtask/src/archive.rs",
"mode": "100644",
"bytes": 11299,
"sha256": "b62ebac302bd3d718a29d6c22d2e7440780372ead11b313e8d7bf1a0177025b4"
"bytes": 13176,
"sha256": "65241bbec66e7d6cbe542186126f8f52a7241ed2d3cdde75ed2727f6965cec28"
},
{
"path": "xtask/src/main.rs",
Expand Down
47 changes: 46 additions & 1 deletion xtask/src/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@ fn write_octal(field: &mut [u8], value: u64) -> io::Result<()> {
/// (uncompressed), with no extra fields, no comments and no timestamps
/// beyond the DOS epoch 1980-01-01 00:00:00.
pub fn write_zip(path: &Path, member: &str, data: &[u8]) -> io::Result<()> {
write_zip_to(fs::File::create(path)?, member, data).map(|_| ())
// patch_zip_external_attr reads the end-of-central-directory record back,
// so the handle must be readable. File::create is write-only: that compiles
// (File: Read) but fails at runtime with os error 5 on Windows and os
// error 9 on Unix.
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)?;
write_zip_to(file, member, data).map(|_| ())
}

fn write_zip_to<W: Write + Read + Seek>(mut writer: W, member: &str, data: &[u8]) -> io::Result<W> {
Expand Down Expand Up @@ -282,4 +292,39 @@ mod tests {
]) as usize;
(offset, count)
}

/// Regression: `write_zip` opens a real file, unlike the `Cursor`-based
/// tests above. `File::create` yields a write-only handle, and
/// `patch_zip_external_attr` reads the archive back — which compiles,
/// because `File: Read`, but fails at runtime. Windows reports os error 5
/// and Linux os error 9. This is the only test that covers the path-taking
/// entry point the release actually calls.
#[test]
fn write_zip_writes_a_real_file_it_can_read_back() {
let dir = std::env::temp_dir().join(format!("bran-xtask-zip-{}", std::process::id()));
fs::create_dir_all(&dir).unwrap();
let path = dir.join("bran-test-x86_64-pc-windows-msvc.zip");
let data = b"windows binary bytes".to_vec();

write_zip(&path, "bran.exe", &data).expect("write_zip must succeed against a real file");

let written = fs::read(&path).unwrap();
assert!(!written.is_empty(), "archive must not be empty");
assert_eq!(
&written[..4],
&[0x50, 0x4b, 0x03, 0x04],
"must start with a local file header"
);

// Deterministic: a second write of the same input is byte-identical.
let second = dir.join("second.zip");
write_zip(&second, "bran.exe", &data).unwrap();
assert_eq!(
written,
fs::read(&second).unwrap(),
"zip output must be deterministic"
);

fs::remove_dir_all(&dir).unwrap();
}
}