@@ -375,7 +375,7 @@ impl<'a> Seek for &'a File {
375375}
376376
377377impl OpenOptions {
378- /// Creates a blank net set of options ready for configuration.
378+ /// Creates a blank new set of options ready for configuration.
379379 ///
380380 /// All options are initially set to `false`.
381381 ///
@@ -384,7 +384,8 @@ impl OpenOptions {
384384 /// ```no_run
385385 /// use std::fs::OpenOptions;
386386 ///
387- /// let file = OpenOptions::new().open("foo.txt");
387+ /// let mut options = OpenOptions::new();
388+ /// let file = options.read(true).open("foo.txt");
388389 /// ```
389390 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
390391 pub fn new ( ) -> OpenOptions {
@@ -413,6 +414,9 @@ impl OpenOptions {
413414 /// This option, when true, will indicate that the file should be
414415 /// `write`-able if opened.
415416 ///
417+ /// If a file already exist, any write calls on the file will overwrite its
418+ /// contents, without truncating it.
419+ ///
416420 /// # Examples
417421 ///
418422 /// ```no_run
@@ -429,13 +433,30 @@ impl OpenOptions {
429433 ///
430434 /// This option, when true, means that writes will append to a file instead
431435 /// of overwriting previous contents.
436+ /// Note that setting `.write(true).append(true)` has the same effect as
437+ /// setting only `.append(true)`.
438+ ///
439+ /// For most filesystems the operating system guarantees all writes are
440+ /// atomic: no writes get mangled because another process writes at the same
441+ /// time.
442+ ///
443+ /// One maybe obvious note when using append-mode: make sure that all data
444+ /// that belongs together, is written the the file in one operation. This
445+ /// can be done by concatenating strings before passing them to `write()`,
446+ /// or using a buffered writer (with a more than adequately sized buffer)
447+ /// and calling `flush()` when the message is complete.
448+ ///
449+ /// If a file is opened with both read and append access, beware that after
450+ /// opening and after every write the position for reading may be set at the
451+ /// end of the file. So before writing save the current position (using
452+ /// `seek(SeekFrom::Current(0))`, and restore it before the next read.
432453 ///
433454 /// # Examples
434455 ///
435456 /// ```no_run
436457 /// use std::fs::OpenOptions;
437458 ///
438- /// let file = OpenOptions::new().write(true). append(true).open("foo.txt");
459+ /// let file = OpenOptions::new().append(true).open("foo.txt");
439460 /// ```
440461 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
441462 pub fn append ( & mut self , append : bool ) -> & mut OpenOptions {
@@ -447,6 +468,8 @@ impl OpenOptions {
447468 /// If a file is successfully opened with this option set it will truncate
448469 /// the file to 0 length if it already exists.
449470 ///
471+ /// The file must be opened with write access for truncate to work.
472+ ///
450473 /// # Examples
451474 ///
452475 /// ```no_run
@@ -464,29 +487,68 @@ impl OpenOptions {
464487 /// This option indicates whether a new file will be created if the file
465488 /// does not yet already exist.
466489 ///
490+ /// The file must be opened with write or append access in order to create
491+ /// a new file.
492+ ///
467493 /// # Examples
468494 ///
469495 /// ```no_run
470496 /// use std::fs::OpenOptions;
471497 ///
472- /// let file = OpenOptions::new().create(true).open("foo.txt");
498+ /// let file = OpenOptions::new().write(true). create(true).open("foo.txt");
473499 /// ```
474500 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
475501 pub fn create ( & mut self , create : bool ) -> & mut OpenOptions {
476502 self . 0 . create ( create) ; self
477503 }
478504
505+ /// Sets the option to always create a new file.
506+ ///
507+ /// This option indicates whether a new file will be created.
508+ /// No file is allowed to exist at the target location, also no (dangling)
509+ /// symlink.
510+ ///
511+ /// This option is usefull because it as atomic. Otherwise between checking
512+ /// whether a file exists and creating a new one, the file may have been
513+ /// created by another process (a TOCTOU race condition / attack).
514+ ///
515+ /// If `.create_new(true)` is set, `.create()` and `.truncate()` are
516+ /// ignored.
517+ ///
518+ /// The file must be opened with write or append access in order to create
519+ /// a new file.
520+ ///
521+ /// # Examples
522+ ///
523+ /// ```no_run
524+ /// #![feature(expand_open_options)]
525+ /// use std::fs::OpenOptions;
526+ ///
527+ /// let file = OpenOptions::new().write(true)
528+ /// .create_new(true)
529+ /// .open("foo.txt");
530+ /// ```
531+ #[ unstable( feature = "expand_open_options" ,
532+ reason = "recently added" ,
533+ issue = "30014" ) ]
534+ pub fn create_new ( & mut self , create_new : bool ) -> & mut OpenOptions {
535+ self . 0 . create_new ( create_new) ; self
536+ }
537+
479538 /// Opens a file at `path` with the options specified by `self`.
480539 ///
481540 /// # Errors
482541 ///
483542 /// This function will return an error under a number of different
484543 /// circumstances, to include but not limited to:
485544 ///
486- /// * Opening a file that does not exist with read access.
545+ /// * Opening a file that does not exist without setting `create` or
546+ /// `create_new`.
487547 /// * Attempting to open a file with access that the user lacks
488548 /// permissions for
489549 /// * Filesystem-level errors (full disk, etc)
550+ /// * Invalid combinations of open options (truncate without write access,
551+ /// no access mode set, etc)
490552 ///
491553 /// # Examples
492554 ///
@@ -2098,61 +2160,114 @@ mod tests {
20982160
20992161 let mut r = OO :: new ( ) ; r. read ( true ) ;
21002162 let mut w = OO :: new ( ) ; w. write ( true ) ;
2101- let mut rw = OO :: new ( ) ; rw. write ( true ) . read ( true ) ;
2102-
2103- match r. open ( & tmpdir. join ( "a" ) ) {
2104- Ok ( ..) => panic ! ( ) , Err ( ..) => { }
2105- }
2106-
2107- // Perform each one twice to make sure that it succeeds the second time
2108- // (where the file exists)
2109- check ! ( c( & w) . create( true ) . open( & tmpdir. join( "b" ) ) ) ;
2110- assert ! ( tmpdir. join( "b" ) . exists( ) ) ;
2111- check ! ( c( & w) . create( true ) . open( & tmpdir. join( "b" ) ) ) ;
2112- check ! ( w. open( & tmpdir. join( "b" ) ) ) ;
2113-
2163+ let mut rw = OO :: new ( ) ; rw. read ( true ) . write ( true ) ;
2164+ let mut a = OO :: new ( ) ; a. append ( true ) ;
2165+ let mut ra = OO :: new ( ) ; ra. read ( true ) . append ( true ) ;
2166+
2167+ let invalid_options = if cfg ! ( windows) { "The parameter is incorrect" }
2168+ else { "Invalid argument" } ;
2169+
2170+ // Test various combinations of creation modes and access modes.
2171+ //
2172+ // Allowed:
2173+ // creation mode | read | write | read-write | append | read-append |
2174+ // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
2175+ // not set (open existing) | X | X | X | X | X |
2176+ // create | | X | X | X | X |
2177+ // truncate | | X | X | | |
2178+ // create and truncate | | X | X | | |
2179+ // create_new | | X | X | X | X |
2180+ //
2181+ // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
2182+
2183+ // write-only
2184+ check ! ( c( & w) . create_new( true ) . open( & tmpdir. join( "a" ) ) ) ;
2185+ check ! ( c( & w) . create( true ) . truncate( true ) . open( & tmpdir. join( "a" ) ) ) ;
2186+ check ! ( c( & w) . truncate( true ) . open( & tmpdir. join( "a" ) ) ) ;
2187+ check ! ( c( & w) . create( true ) . open( & tmpdir. join( "a" ) ) ) ;
2188+ check ! ( c( & w) . open( & tmpdir. join( "a" ) ) ) ;
2189+
2190+ // read-only
2191+ error ! ( c( & r) . create_new( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2192+ error ! ( c( & r) . create( true ) . truncate( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2193+ error ! ( c( & r) . truncate( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2194+ error ! ( c( & r) . create( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2195+ check ! ( c( & r) . open( & tmpdir. join( "a" ) ) ) ; // try opening the file created with write_only
2196+
2197+ // read-write
2198+ check ! ( c( & rw) . create_new( true ) . open( & tmpdir. join( "c" ) ) ) ;
2199+ check ! ( c( & rw) . create( true ) . truncate( true ) . open( & tmpdir. join( "c" ) ) ) ;
2200+ check ! ( c( & rw) . truncate( true ) . open( & tmpdir. join( "c" ) ) ) ;
21142201 check ! ( c( & rw) . create( true ) . open( & tmpdir. join( "c" ) ) ) ;
2115- assert ! ( tmpdir. join( "c" ) . exists( ) ) ;
2116- check ! ( c( & rw) . create( true ) . open( & tmpdir. join( "c" ) ) ) ;
2117- check ! ( rw. open( & tmpdir. join( "c" ) ) ) ;
2118-
2119- check ! ( c( & w) . append( true ) . create( true ) . open( & tmpdir. join( "d" ) ) ) ;
2120- assert ! ( tmpdir. join( "d" ) . exists( ) ) ;
2121- check ! ( c( & w) . append( true ) . create( true ) . open( & tmpdir. join( "d" ) ) ) ;
2122- check ! ( c( & w) . append( true ) . open( & tmpdir. join( "d" ) ) ) ;
2123-
2124- check ! ( c( & rw) . append( true ) . create( true ) . open( & tmpdir. join( "e" ) ) ) ;
2125- assert ! ( tmpdir. join( "e" ) . exists( ) ) ;
2126- check ! ( c( & rw) . append( true ) . create( true ) . open( & tmpdir. join( "e" ) ) ) ;
2127- check ! ( c( & rw) . append( true ) . open( & tmpdir. join( "e" ) ) ) ;
2128-
2129- check ! ( c( & w) . truncate( true ) . create( true ) . open( & tmpdir. join( "f" ) ) ) ;
2130- assert ! ( tmpdir. join( "f" ) . exists( ) ) ;
2131- check ! ( c( & w) . truncate( true ) . create( true ) . open( & tmpdir. join( "f" ) ) ) ;
2132- check ! ( c( & w) . truncate( true ) . open( & tmpdir. join( "f" ) ) ) ;
2133-
2134- check ! ( c( & rw) . truncate( true ) . create( true ) . open( & tmpdir. join( "g" ) ) ) ;
2135- assert ! ( tmpdir. join( "g" ) . exists( ) ) ;
2136- check ! ( c( & rw) . truncate( true ) . create( true ) . open( & tmpdir. join( "g" ) ) ) ;
2137- check ! ( c( & rw) . truncate( true ) . open( & tmpdir. join( "g" ) ) ) ;
2138-
2139- check ! ( check!( File :: create( & tmpdir. join( "h" ) ) ) . write( "foo" . as_bytes( ) ) ) ;
2202+ check ! ( c( & rw) . open( & tmpdir. join( "c" ) ) ) ;
2203+
2204+ // append
2205+ check ! ( c( & a) . create_new( true ) . open( & tmpdir. join( "d" ) ) ) ;
2206+ error ! ( c( & a) . create( true ) . truncate( true ) . open( & tmpdir. join( "d" ) ) , invalid_options) ;
2207+ error ! ( c( & a) . truncate( true ) . open( & tmpdir. join( "d" ) ) , invalid_options) ;
2208+ check ! ( c( & a) . create( true ) . open( & tmpdir. join( "d" ) ) ) ;
2209+ check ! ( c( & a) . open( & tmpdir. join( "d" ) ) ) ;
2210+
2211+ // read-append
2212+ check ! ( c( & ra) . create_new( true ) . open( & tmpdir. join( "e" ) ) ) ;
2213+ error ! ( c( & ra) . create( true ) . truncate( true ) . open( & tmpdir. join( "e" ) ) , invalid_options) ;
2214+ error ! ( c( & ra) . truncate( true ) . open( & tmpdir. join( "e" ) ) , invalid_options) ;
2215+ check ! ( c( & ra) . create( true ) . open( & tmpdir. join( "e" ) ) ) ;
2216+ check ! ( c( & ra) . open( & tmpdir. join( "e" ) ) ) ;
2217+
2218+ // Test opening a file without setting an access mode
2219+ let mut blank = OO :: new ( ) ;
2220+ error ! ( blank. create( true ) . open( & tmpdir. join( "f" ) ) , invalid_options) ;
2221+
2222+ // Test write works
2223+ check ! ( check!( File :: create( & tmpdir. join( "h" ) ) ) . write( "foobar" . as_bytes( ) ) ) ;
2224+
2225+ // Test write fails for read-only
21402226 check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
21412227 {
21422228 let mut f = check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
21432229 assert ! ( f. write( "wut" . as_bytes( ) ) . is_err( ) ) ;
21442230 }
2231+
2232+ // Test write overwrites
2233+ {
2234+ let mut f = check ! ( c( & w) . open( & tmpdir. join( "h" ) ) ) ;
2235+ check ! ( f. write( "baz" . as_bytes( ) ) ) ;
2236+ }
2237+ {
2238+ let mut f = check ! ( c( & r) . open( & tmpdir. join( "h" ) ) ) ;
2239+ let mut b = vec ! [ 0 ; 6 ] ;
2240+ check ! ( f. read( & mut b) ) ;
2241+ assert_eq ! ( b, "bazbar" . as_bytes( ) ) ;
2242+ }
2243+
2244+ // Test truncate works
2245+ {
2246+ let mut f = check ! ( c( & w) . truncate( true ) . open( & tmpdir. join( "h" ) ) ) ;
2247+ check ! ( f. write( "foo" . as_bytes( ) ) ) ;
2248+ }
2249+ assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2250+
2251+ // Test append works
21452252 assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
21462253 {
2147- let mut f = check ! ( c( & w ) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2254+ let mut f = check ! ( c( & a ) . open( & tmpdir. join( "h" ) ) ) ;
21482255 check ! ( f. write( "bar" . as_bytes( ) ) ) ;
21492256 }
21502257 assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 6 ) ;
2258+
2259+ // Test .append(true) equals .write(true).append(true)
21512260 {
2152- let mut f = check ! ( c( & w) . truncate ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2153- check ! ( f. write( "bar " . as_bytes( ) ) ) ;
2261+ let mut f = check ! ( c( & w) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2262+ check ! ( f. write( "baz " . as_bytes( ) ) ) ;
21542263 }
2155- assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2264+ assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 9 ) ;
2265+ }
2266+
2267+ #[ test]
2268+ fn _assert_send_sync ( ) {
2269+ fn _assert_send_sync < T : Send + Sync > ( ) { }
2270+ _assert_send_sync :: < OpenOptions > ( ) ;
21562271 }
21572272
21582273 #[ test]
0 commit comments