Fix race condition in fs::create_dir_all

It is more robust to not fail if any directory in a path was created
concurrently. This change lifts rustc internal `create_dir_racy` that
was created to handle such conditions to be new `create_dir_all`
implementation.
This commit is contained in:
David Roundy 2015-12-01 17:29:56 -05:00 committed by Dawid Ciężarkiewicz
parent a559452b05
commit db00ba9eb2
5 changed files with 28 additions and 48 deletions

View File

@ -109,23 +109,3 @@ pub fn rename_or_copy_remove<P: AsRef<Path>, Q: AsRef<Path>>(p: P,
}
}
}
// Like std::fs::create_dir_all, except handles concurrent calls among multiple
// threads or processes.
pub fn create_dir_racy(path: &Path) -> io::Result<()> {
match fs::create_dir(path) {
Ok(()) => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => (),
Err(e) => return Err(e),
}
match path.parent() {
Some(p) => try!(create_dir_racy(p)),
None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")),
}
match fs::create_dir(path) {
Ok(()) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(e) => Err(e),
}
}

View File

@ -461,7 +461,7 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf {
}
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(),()> {
match fs_util::create_dir_racy(path) {
match std_fs::create_dir_all(path) {
Ok(()) => {
debug!("{} directory created successfully", dir_tag);
Ok(())

View File

@ -885,7 +885,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
},
};
if let Err(e) = rustc::util::fs::create_dir_racy(&root_path) {
if let Err(e) = std::fs::create_dir_all(&root_path) {
tcx.sess.err(&format!("Could not create directory {}: {}",
root_path.display(),
e));

View File

@ -1534,6 +1534,12 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// error conditions for when a directory is being created (after it is
/// determined to not exist) are outlined by `fs::create_dir`.
///
/// Notable exception is made for situations where any of the directories
/// specified in the `path` could not be created as it was created concurrently.
/// Such cases are considered success. In other words: calling `create_dir_all`
/// concurrently from multiple threads or processes is guaranteed to not fail
/// due to race itself.
///
/// # Examples
///
/// ```
@ -1769,11 +1775,21 @@ impl DirBuilder {
}
fn create_dir_all(&self, path: &Path) -> io::Result<()> {
if path == Path::new("") || path.is_dir() { return Ok(()) }
if let Some(p) = path.parent() {
self.create_dir_all(p)?
match self.inner.mkdir(path) {
Ok(()) => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(e) => return Err(e),
}
match path.parent() {
Some(p) => try!(create_dir_all(p)),
None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")),
}
match self.inner.mkdir(path) {
Ok(()) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(e) => Err(e),
}
self.inner.mkdir(path)
}
}

View File

@ -25,7 +25,7 @@ use util::logv;
use std::collections::HashSet;
use std::env;
use std::fmt;
use std::fs::{self, File};
use std::fs::{self, File, create_dir_all};
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::{Path, PathBuf};
@ -395,7 +395,7 @@ actual:\n\
let out_dir = self.output_base_name().with_extension("pretty-out");
let _ = fs::remove_dir_all(&out_dir);
self.create_dir_racy(&out_dir);
create_dir_all(&out_dir).unwrap();
// FIXME (#9639): This needs to handle non-utf8 paths
let mut args = vec!["-".to_owned(),
@ -1269,7 +1269,7 @@ actual:\n\
fn compose_and_run_compiler(&self, args: ProcArgs, input: Option<String>) -> ProcRes {
if !self.props.aux_builds.is_empty() {
self.create_dir_racy(&self.aux_output_dir_name());
create_dir_all(&self.aux_output_dir_name()).unwrap();
}
let aux_dir = self.aux_output_dir_name();
@ -1340,22 +1340,6 @@ actual:\n\
input)
}
// Like std::fs::create_dir_all, except handles concurrent calls among multiple
// threads or processes.
fn create_dir_racy(&self, path: &Path) {
match fs::create_dir(path) {
Ok(()) => return,
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return,
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(e) => panic!("failed to create dir {:?}: {}", path, e),
}
self.create_dir_racy(path.parent().unwrap());
match fs::create_dir(path) {
Ok(()) => {}
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => {}
Err(e) => panic!("failed to create dir {:?}: {}", path, e),
}
}
fn compose_and_run(&self,
ProcArgs{ args, prog }: ProcArgs,
@ -1435,7 +1419,7 @@ actual:\n\
let mir_dump_dir = self.get_mir_dump_dir();
self.create_dir_racy(mir_dump_dir.as_path());
create_dir_all(mir_dump_dir.as_path()).unwrap();
let mut dir_opt = "dump-mir-dir=".to_string();
dir_opt.push_str(mir_dump_dir.to_str().unwrap());
debug!("dir_opt: {:?}", dir_opt);
@ -1923,7 +1907,7 @@ actual:\n\
let out_dir = self.output_base_name();
let _ = fs::remove_dir_all(&out_dir);
self.create_dir_racy(&out_dir);
create_dir_all(&out_dir).unwrap();
let proc_res = self.document(&out_dir);
if !proc_res.status.success() {
@ -2299,7 +2283,7 @@ actual:\n\
if tmpdir.exists() {
self.aggressive_rm_rf(&tmpdir).unwrap();
}
self.create_dir_racy(&tmpdir);
create_dir_all(&tmpdir).unwrap();
let host = &self.config.host;
let make = if host.contains("bitrig") || host.contains("dragonfly") ||