Auto merge of #45899 - eddyb:meta-race, r=alexcrichton
rustc_trans: atomically write .rmeta outputs to avoid races. Fixes #45841 in a similar vein to how LLVM writes archives: write a temporary file and then rename it. r? @alexcrichton
This commit is contained in:
commit
8752aeed3a
@ -262,19 +262,31 @@ fn link_binary_output(sess: &Session,
|
|||||||
check_file_is_writeable(obj, sess);
|
check_file_is_writeable(obj, sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmpdir = match TempDir::new("rustc") {
|
|
||||||
Ok(tmpdir) => tmpdir,
|
|
||||||
Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut out_filenames = vec![];
|
let mut out_filenames = vec![];
|
||||||
|
|
||||||
if outputs.outputs.contains_key(&OutputType::Metadata) {
|
if outputs.outputs.contains_key(&OutputType::Metadata) {
|
||||||
let out_filename = filename_for_metadata(sess, crate_name, outputs);
|
let out_filename = filename_for_metadata(sess, crate_name, outputs);
|
||||||
emit_metadata(sess, trans, &out_filename);
|
// To avoid races with another rustc process scanning the output directory,
|
||||||
|
// we need to write the file somewhere else and atomically move it to its
|
||||||
|
// final destination, with a `fs::rename` call. In order for the rename to
|
||||||
|
// always succeed, the temporary file needs to be on the same filesystem,
|
||||||
|
// which is why we create it inside the output directory specifically.
|
||||||
|
let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") {
|
||||||
|
Ok(tmpdir) => tmpdir,
|
||||||
|
Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)),
|
||||||
|
};
|
||||||
|
let metadata = emit_metadata(sess, trans, &metadata_tmpdir);
|
||||||
|
if let Err(e) = fs::rename(metadata, &out_filename) {
|
||||||
|
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
|
||||||
|
}
|
||||||
out_filenames.push(out_filename);
|
out_filenames.push(out_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tmpdir = match TempDir::new("rustc") {
|
||||||
|
Ok(tmpdir) => tmpdir,
|
||||||
|
Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
if outputs.outputs.should_trans() {
|
if outputs.outputs.should_trans() {
|
||||||
let out_filename = out_filename(sess, crate_type, outputs, crate_name);
|
let out_filename = out_filename(sess, crate_type, outputs, crate_name);
|
||||||
match crate_type {
|
match crate_type {
|
||||||
@ -283,10 +295,10 @@ fn link_binary_output(sess: &Session,
|
|||||||
trans,
|
trans,
|
||||||
RlibFlavor::Normal,
|
RlibFlavor::Normal,
|
||||||
&out_filename,
|
&out_filename,
|
||||||
tmpdir.path()).build();
|
&tmpdir).build();
|
||||||
}
|
}
|
||||||
config::CrateTypeStaticlib => {
|
config::CrateTypeStaticlib => {
|
||||||
link_staticlib(sess, trans, &out_filename, tmpdir.path());
|
link_staticlib(sess, trans, &out_filename, &tmpdir);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
link_natively(sess, crate_type, &out_filename, trans, tmpdir.path());
|
link_natively(sess, crate_type, &out_filename, trans, tmpdir.path());
|
||||||
@ -321,14 +333,23 @@ fn archive_config<'a>(sess: &'a Session,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, out_filename: &Path) {
|
/// We use a temp directory here to avoid races between concurrent rustc processes,
|
||||||
let result = fs::File::create(out_filename).and_then(|mut f| {
|
/// such as builds in the same directory using the same filename for metadata while
|
||||||
|
/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
|
||||||
|
/// directory being searched for `extern crate` (observing an incomplete file).
|
||||||
|
/// The returned path is the temporary file containing the complete metadata.
|
||||||
|
fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, tmpdir: &TempDir)
|
||||||
|
-> PathBuf {
|
||||||
|
let out_filename = tmpdir.path().join(METADATA_FILENAME);
|
||||||
|
let result = fs::File::create(&out_filename).and_then(|mut f| {
|
||||||
f.write_all(&trans.metadata.raw_data)
|
f.write_all(&trans.metadata.raw_data)
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
|
sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_filename
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RlibFlavor {
|
enum RlibFlavor {
|
||||||
@ -346,7 +367,7 @@ fn link_rlib<'a>(sess: &'a Session,
|
|||||||
trans: &CrateTranslation,
|
trans: &CrateTranslation,
|
||||||
flavor: RlibFlavor,
|
flavor: RlibFlavor,
|
||||||
out_filename: &Path,
|
out_filename: &Path,
|
||||||
tmpdir: &Path) -> ArchiveBuilder<'a> {
|
tmpdir: &TempDir) -> ArchiveBuilder<'a> {
|
||||||
info!("preparing rlib to {:?}", out_filename);
|
info!("preparing rlib to {:?}", out_filename);
|
||||||
let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None));
|
let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None));
|
||||||
|
|
||||||
@ -408,12 +429,8 @@ fn link_rlib<'a>(sess: &'a Session,
|
|||||||
match flavor {
|
match flavor {
|
||||||
RlibFlavor::Normal => {
|
RlibFlavor::Normal => {
|
||||||
// Instead of putting the metadata in an object file section, rlibs
|
// Instead of putting the metadata in an object file section, rlibs
|
||||||
// contain the metadata in a separate file. We use a temp directory
|
// contain the metadata in a separate file.
|
||||||
// here so concurrent builds in the same directory don't try to use
|
ab.add_file(&emit_metadata(sess, trans, tmpdir));
|
||||||
// the same filename for metadata (stomping over one another)
|
|
||||||
let metadata = tmpdir.join(METADATA_FILENAME);
|
|
||||||
emit_metadata(sess, trans, &metadata);
|
|
||||||
ab.add_file(&metadata);
|
|
||||||
|
|
||||||
// For LTO purposes, the bytecode of this library is also inserted
|
// For LTO purposes, the bytecode of this library is also inserted
|
||||||
// into the archive.
|
// into the archive.
|
||||||
@ -457,7 +474,7 @@ fn link_rlib<'a>(sess: &'a Session,
|
|||||||
fn link_staticlib(sess: &Session,
|
fn link_staticlib(sess: &Session,
|
||||||
trans: &CrateTranslation,
|
trans: &CrateTranslation,
|
||||||
out_filename: &Path,
|
out_filename: &Path,
|
||||||
tempdir: &Path) {
|
tempdir: &TempDir) {
|
||||||
let mut ab = link_rlib(sess,
|
let mut ab = link_rlib(sess,
|
||||||
trans,
|
trans,
|
||||||
RlibFlavor::StaticlibBase,
|
RlibFlavor::StaticlibBase,
|
||||||
|
Loading…
Reference in New Issue
Block a user