Skip to content

Commit 453dbf8

Browse files
committed
Auto merge of #8950 - ehuss:publish-tarball-metadata, r=alexcrichton
Workaround fs issue in `cargo publish`. `cargo publish` can fail on some filesystems with a mysterious "No such file or directory". The issue is that `statx` (and `fstat`) will fail on the 9p filesystem (which happens to be used by WSL2) after a file has been [renamed](https://github.com/rust-lang/cargo/blame/27187096a380fdd3b747b5d5ec3396b7af67a6f9/src/cargo/ops/cargo_package.rs#L133-L138). The solution for this workaround is to use seek instead of fstat to determine the length of the tarball. More information about the 9p problem can be found at https://bugs.launchpad.net/qemu/+bug/1336794 and https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg06382.html. Fixes #8439
2 parents 2718709 + eb081ed commit 453dbf8

File tree

1 file changed

+17
-6
lines changed

1 file changed

+17
-6
lines changed

crates/crates-io/lib.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
use std::collections::BTreeMap;
55
use std::fs::File;
66
use std::io::prelude::*;
7-
use std::io::Cursor;
7+
use std::io::{Cursor, SeekFrom};
88
use std::time::Instant;
99

10-
use anyhow::{bail, Result};
10+
use anyhow::{bail, Context, Result};
1111
use curl::easy::{Easy, List};
1212
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
1313
use serde::{Deserialize, Serialize};
@@ -161,23 +161,34 @@ impl Registry {
161161
Ok(serde_json::from_str::<Users>(&body)?.users)
162162
}
163163

164-
pub fn publish(&mut self, krate: &NewCrate, tarball: &File) -> Result<Warnings> {
164+
pub fn publish(&mut self, krate: &NewCrate, mut tarball: &File) -> Result<Warnings> {
165165
let json = serde_json::to_string(krate)?;
166166
// Prepare the body. The format of the upload request is:
167167
//
168168
// <le u32 of json>
169169
// <json request> (metadata for the package)
170170
// <le u32 of tarball>
171171
// <source tarball>
172-
let stat = tarball.metadata()?;
172+
173+
// NOTE: This can be replaced with `stream_len` if it is ever stabilized.
174+
//
175+
// This checks the length using seeking instead of metadata, because
176+
// on some filesystems, getting the metadata will fail because
177+
// the file was renamed in ops::package.
178+
let tarball_len = tarball
179+
.seek(SeekFrom::End(0))
180+
.with_context(|| "failed to seek tarball")?;
181+
tarball
182+
.seek(SeekFrom::Start(0))
183+
.with_context(|| "failed to seek tarball")?;
173184
let header = {
174185
let mut w = Vec::new();
175186
w.extend(&(json.len() as u32).to_le_bytes());
176187
w.extend(json.as_bytes().iter().cloned());
177-
w.extend(&(stat.len() as u32).to_le_bytes());
188+
w.extend(&(tarball_len as u32).to_le_bytes());
178189
w
179190
};
180-
let size = stat.len() as usize + header.len();
191+
let size = tarball_len as usize + header.len();
181192
let mut body = Cursor::new(header).chain(tarball);
182193

183194
let url = format!("{}/api/v1/crates/new", self.host);

0 commit comments

Comments
 (0)