Skip to content

Commit db00ba9

Browse files
droundydpc
authored andcommitted
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.
1 parent a559452 commit db00ba9

File tree

5 files changed

+28
-48
lines changed

5 files changed

+28
-48
lines changed

src/librustc/util/fs.rs

-20
Original file line numberDiff line numberDiff line change
@@ -109,23 +109,3 @@ pub fn rename_or_copy_remove<P: AsRef<Path>, Q: AsRef<Path>>(p: P,
109109
}
110110
}
111111
}
112-
113-
// Like std::fs::create_dir_all, except handles concurrent calls among multiple
114-
// threads or processes.
115-
pub fn create_dir_racy(path: &Path) -> io::Result<()> {
116-
match fs::create_dir(path) {
117-
Ok(()) => return Ok(()),
118-
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
119-
Err(ref e) if e.kind() == io::ErrorKind::NotFound => (),
120-
Err(e) => return Err(e),
121-
}
122-
match path.parent() {
123-
Some(p) => try!(create_dir_racy(p)),
124-
None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")),
125-
}
126-
match fs::create_dir(path) {
127-
Ok(()) => Ok(()),
128-
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
129-
Err(e) => Err(e),
130-
}
131-
}

src/librustc_incremental/persist/fs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ fn generate_session_dir_path(crate_dir: &Path) -> PathBuf {
461461
}
462462

463463
fn create_dir(sess: &Session, path: &Path, dir_tag: &str) -> Result<(),()> {
464-
match fs_util::create_dir_racy(path) {
464+
match std_fs::create_dir_all(path) {
465465
Ok(()) => {
466466
debug!("{} directory created successfully", dir_tag);
467467
Ok(())

src/librustc_save_analysis/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
885885
},
886886
};
887887

888-
if let Err(e) = rustc::util::fs::create_dir_racy(&root_path) {
888+
if let Err(e) = std::fs::create_dir_all(&root_path) {
889889
tcx.sess.err(&format!("Could not create directory {}: {}",
890890
root_path.display(),
891891
e));

src/libstd/fs.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,12 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
15341534
/// error conditions for when a directory is being created (after it is
15351535
/// determined to not exist) are outlined by `fs::create_dir`.
15361536
///
1537+
/// Notable exception is made for situations where any of the directories
1538+
/// specified in the `path` could not be created as it was created concurrently.
1539+
/// Such cases are considered success. In other words: calling `create_dir_all`
1540+
/// concurrently from multiple threads or processes is guaranteed to not fail
1541+
/// due to race itself.
1542+
///
15371543
/// # Examples
15381544
///
15391545
/// ```
@@ -1769,11 +1775,21 @@ impl DirBuilder {
17691775
}
17701776

17711777
fn create_dir_all(&self, path: &Path) -> io::Result<()> {
1772-
if path == Path::new("") || path.is_dir() { return Ok(()) }
1773-
if let Some(p) = path.parent() {
1774-
self.create_dir_all(p)?
1778+
match self.inner.mkdir(path) {
1779+
Ok(()) => return Ok(()),
1780+
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
1781+
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
1782+
Err(e) => return Err(e),
1783+
}
1784+
match path.parent() {
1785+
Some(p) => try!(create_dir_all(p)),
1786+
None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")),
1787+
}
1788+
match self.inner.mkdir(path) {
1789+
Ok(()) => Ok(()),
1790+
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
1791+
Err(e) => Err(e),
17751792
}
1776-
self.inner.mkdir(path)
17771793
}
17781794
}
17791795

src/tools/compiletest/src/runtest.rs

+6-22
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use util::logv;
2525
use std::collections::HashSet;
2626
use std::env;
2727
use std::fmt;
28-
use std::fs::{self, File};
28+
use std::fs::{self, File, create_dir_all};
2929
use std::io::prelude::*;
3030
use std::io::{self, BufReader};
3131
use std::path::{Path, PathBuf};
@@ -395,7 +395,7 @@ actual:\n\
395395

396396
let out_dir = self.output_base_name().with_extension("pretty-out");
397397
let _ = fs::remove_dir_all(&out_dir);
398-
self.create_dir_racy(&out_dir);
398+
create_dir_all(&out_dir).unwrap();
399399

400400
// FIXME (#9639): This needs to handle non-utf8 paths
401401
let mut args = vec!["-".to_owned(),
@@ -1269,7 +1269,7 @@ actual:\n\
12691269

12701270
fn compose_and_run_compiler(&self, args: ProcArgs, input: Option<String>) -> ProcRes {
12711271
if !self.props.aux_builds.is_empty() {
1272-
self.create_dir_racy(&self.aux_output_dir_name());
1272+
create_dir_all(&self.aux_output_dir_name()).unwrap();
12731273
}
12741274

12751275
let aux_dir = self.aux_output_dir_name();
@@ -1340,22 +1340,6 @@ actual:\n\
13401340
input)
13411341
}
13421342

1343-
// Like std::fs::create_dir_all, except handles concurrent calls among multiple
1344-
// threads or processes.
1345-
fn create_dir_racy(&self, path: &Path) {
1346-
match fs::create_dir(path) {
1347-
Ok(()) => return,
1348-
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return,
1349-
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
1350-
Err(e) => panic!("failed to create dir {:?}: {}", path, e),
1351-
}
1352-
self.create_dir_racy(path.parent().unwrap());
1353-
match fs::create_dir(path) {
1354-
Ok(()) => {}
1355-
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => {}
1356-
Err(e) => panic!("failed to create dir {:?}: {}", path, e),
1357-
}
1358-
}
13591343

13601344
fn compose_and_run(&self,
13611345
ProcArgs{ args, prog }: ProcArgs,
@@ -1435,7 +1419,7 @@ actual:\n\
14351419

14361420

14371421
let mir_dump_dir = self.get_mir_dump_dir();
1438-
self.create_dir_racy(mir_dump_dir.as_path());
1422+
create_dir_all(mir_dump_dir.as_path()).unwrap();
14391423
let mut dir_opt = "dump-mir-dir=".to_string();
14401424
dir_opt.push_str(mir_dump_dir.to_str().unwrap());
14411425
debug!("dir_opt: {:?}", dir_opt);
@@ -1923,7 +1907,7 @@ actual:\n\
19231907

19241908
let out_dir = self.output_base_name();
19251909
let _ = fs::remove_dir_all(&out_dir);
1926-
self.create_dir_racy(&out_dir);
1910+
create_dir_all(&out_dir).unwrap();
19271911

19281912
let proc_res = self.document(&out_dir);
19291913
if !proc_res.status.success() {
@@ -2299,7 +2283,7 @@ actual:\n\
22992283
if tmpdir.exists() {
23002284
self.aggressive_rm_rf(&tmpdir).unwrap();
23012285
}
2302-
self.create_dir_racy(&tmpdir);
2286+
create_dir_all(&tmpdir).unwrap();
23032287

23042288
let host = &self.config.host;
23052289
let make = if host.contains("bitrig") || host.contains("dragonfly") ||

0 commit comments

Comments
 (0)