Skip to content

Commit 6eb9960

Browse files
committed
Auto merge of #39799 - dpc:create_dir_all, r=alexcrichton
Fix race condition in fs::create_dir_all The code would crash if the directory was created after create_dir_all checked whether the directory already existed. This was contrary to the documentation which claimed to create the directory if it doesn't exist, implying (but not stating) that there would not be a failure due to the directory existing.
2 parents 38c53f3 + 088696b commit 6eb9960

File tree

5 files changed

+64
-48
lines changed

5 files changed

+64
-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
@@ -884,7 +884,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
884884
},
885885
};
886886

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

src/libstd/fs.rs

+56-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,25 @@ 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+
if path == Path::new("") {
1779+
return Ok(())
1780+
}
1781+
1782+
match self.inner.mkdir(path) {
1783+
Ok(()) => return Ok(()),
1784+
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
1785+
Err(_) if path.is_dir() => return Ok(()),
1786+
Err(e) => return Err(e),
1787+
}
1788+
match path.parent() {
1789+
Some(p) => try!(self.create_dir_all(p)),
1790+
None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")),
1791+
}
1792+
match self.inner.mkdir(path) {
1793+
Ok(()) => Ok(()),
1794+
Err(_) if path.is_dir() => Ok(()),
1795+
Err(e) => Err(e),
17751796
}
1776-
self.inner.mkdir(path)
17771797
}
17781798
}
17791799

@@ -1793,6 +1813,7 @@ mod tests {
17931813
use rand::{StdRng, Rng};
17941814
use str;
17951815
use sys_common::io::test::{TempDir, tmpdir};
1816+
use thread;
17961817

17971818
#[cfg(windows)]
17981819
use os::windows::fs::{symlink_dir, symlink_file};
@@ -2260,11 +2281,42 @@ mod tests {
22602281
assert!(result.is_err());
22612282
}
22622283

2284+
#[test]
2285+
fn concurrent_recursive_mkdir() {
2286+
for _ in 0..100 {
2287+
let dir = tmpdir();
2288+
let mut dir = dir.join("a");
2289+
for _ in 0..40 {
2290+
dir = dir.join("a");
2291+
}
2292+
let mut join = vec!();
2293+
for _ in 0..8 {
2294+
let dir = dir.clone();
2295+
join.push(thread::spawn(move || {
2296+
check!(fs::create_dir_all(&dir));
2297+
}))
2298+
}
2299+
2300+
// No `Display` on result of `join()`
2301+
join.drain(..).map(|join| join.join().unwrap()).count();
2302+
}
2303+
}
2304+
22632305
#[test]
22642306
fn recursive_mkdir_slash() {
22652307
check!(fs::create_dir_all(&Path::new("/")));
22662308
}
22672309

2310+
#[test]
2311+
fn recursive_mkdir_dot() {
2312+
check!(fs::create_dir_all(&Path::new(".")));
2313+
}
2314+
2315+
#[test]
2316+
fn recursive_mkdir_empty() {
2317+
check!(fs::create_dir_all(&Path::new("")));
2318+
}
2319+
22682320
#[test]
22692321
fn recursive_rmdir() {
22702322
let tmpdir = tmpdir();

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)