about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/tempfile.rs74
1 files changed, 44 insertions, 30 deletions
diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs
index 45e0dd4e8e5..394686be814 100644
--- a/src/libstd/io/tempfile.rs
+++ b/src/libstd/io/tempfile.rs
@@ -10,16 +10,18 @@
 
 //! Temporary files and directories
 
-use io::{fs, IoResult};
+use io::{fs, IoError, IoErrorKind, IoResult};
 use io;
-use libc;
+use iter::{IteratorExt, range};
 use ops::Drop;
 use option::Option;
 use option::Option::{None, Some};
 use os;
 use path::{Path, GenericPath};
+use rand::{Rng, thread_rng};
 use result::Result::{Ok, Err};
-use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
+use str::StrExt;
+use string::String;
 
 /// A wrapper for a path to temporary directory implementing automatic
 /// scope-based deletion.
@@ -31,7 +33,7 @@ use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
 ///
 /// {
 ///     // create a temporary directory
-///     let tmpdir = match TempDir::new("mysuffix") {
+///     let tmpdir = match TempDir::new("myprefix") {
 ///         Ok(dir) => dir,
 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
 ///     };
@@ -46,7 +48,7 @@ use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
 /// }
 /// {
 ///     // create a temporary directory, this time using a custom path
-///     let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "mysuffix") {
+///     let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") {
 ///         Ok(dir) => dir,
 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
 ///     };
@@ -61,7 +63,7 @@ use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
 /// }
 /// {
 ///     // create a temporary directory
-///     let tmpdir = match TempDir::new("mysuffix") {
+///     let tmpdir = match TempDir::new("myprefix") {
 ///         Ok(dir) => dir,
 ///         Err(e) => panic!("couldn't create temporary directory: {}", e)
 ///     };
@@ -78,47 +80,59 @@ pub struct TempDir {
     disarmed: bool
 }
 
+// How many times should we (re)try finding an unused random name? It should be
+// enough that an attacker will run out of luck before we run out of patience.
+const NUM_RETRIES: u32 = 1 << 31;
+// How many characters should we include in a random file name? It needs to
+// be enough to dissuade an attacker from trying to preemptively create names
+// of that length, but not so huge that we unnecessarily drain the random number
+// generator of entropy.
+const NUM_RAND_CHARS: uint = 12;
+
 impl TempDir {
     /// Attempts to make a temporary directory inside of `tmpdir` whose name
-    /// will have the suffix `suffix`. The directory will be automatically
+    /// will have the prefix `prefix`. The directory will be automatically
     /// deleted once the returned wrapper is destroyed.
     ///
     /// If no directory can be created, `Err` is returned.
-    pub fn new_in(tmpdir: &Path, suffix: &str) -> IoResult<TempDir> {
+    pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> {
         if !tmpdir.is_absolute() {
             let abs_tmpdir = try!(os::make_absolute(tmpdir));
-            return TempDir::new_in(&abs_tmpdir, suffix);
+            return TempDir::new_in(&abs_tmpdir, prefix);
         }
 
-        static CNT: AtomicUint = ATOMIC_UINT_INIT;
-
-        let mut attempts = 0u;
-        loop {
-            let filename =
-                format!("rs-{}-{}-{}",
-                        unsafe { libc::getpid() },
-                        CNT.fetch_add(1, Ordering::SeqCst),
-                        suffix);
-            let p = tmpdir.join(filename);
-            match fs::mkdir(&p, io::USER_RWX) {
-                Err(error) => {
-                    if attempts >= 1000 {
-                        return Err(error)
-                    }
-                    attempts += 1;
-                }
-                Ok(()) => return Ok(TempDir { path: Some(p), disarmed: false })
+        let mut rng = thread_rng();
+        for _ in range(0, NUM_RETRIES) {
+            let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect();
+            let leaf = if prefix.len() > 0 {
+                format!("{}.{}", prefix, suffix)
+            } else {
+                // If we're given an empty string for a prefix, then creating a
+                // directory starting with "." would lead to it being
+                // semi-invisible on some systems.
+                suffix
+            };
+            let path = tmpdir.join(leaf);
+            match fs::mkdir(&path, io::USER_RWX) {
+                Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }),
+                Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (),
+                Err(e) => return Err(e)
             }
         }
+
+        return Err(IoError{
+                       kind: IoErrorKind::PathAlreadyExists,
+                       desc:"Exhausted",
+                       detail: None});
     }
 
     /// Attempts to make a temporary directory inside of `os::tmpdir()` whose
-    /// name will have the suffix `suffix`. The directory will be automatically
+    /// name will have the prefix `prefix`. The directory will be automatically
     /// deleted once the returned wrapper is destroyed.
     ///
     /// If no directory can be created, `Err` is returned.
-    pub fn new(suffix: &str) -> IoResult<TempDir> {
-        TempDir::new_in(&os::tmpdir(), suffix)
+    pub fn new(prefix: &str) -> IoResult<TempDir> {
+        TempDir::new_in(&os::tmpdir(), prefix)
     }
 
     /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.