@@ -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, the contents of that file get overwritten, but it is
418+ /// not truncated.
419+ ///
416420 /// # Examples
417421 ///
418422 /// ```no_run
@@ -429,13 +433,29 @@ 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 atomic:
440+ /// no writes get mangled because another process writes at the same time.
441+ ///
442+ /// One maybe obvious note when using append-mode: make sure that all data that
443+ /// belongs together, is written the the file in one operation. This can be done
444+ /// by concatenating strings before passing them to `write()`, or using a buffered
445+ /// writer (with a more than adequately sized buffer) and calling `flush()` when the
446+ /// message is complete.
447+ ///
448+ /// If a file is opened with both read and append access, beware that after opening
449+ /// and after every write the position for reading may be set at the end of the file.
450+ /// So before writing save the current position (using `seek(SeekFrom::Current(0))`,
451+ /// and restore it before the next read.
432452 ///
433453 /// # Examples
434454 ///
435455 /// ```no_run
436456 /// use std::fs::OpenOptions;
437457 ///
438- /// let file = OpenOptions::new().write(true). append(true).open("foo.txt");
458+ /// let file = OpenOptions::new().append(true).open("foo.txt");
439459 /// ```
440460 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
441461 pub fn append ( & mut self , append : bool ) -> & mut OpenOptions {
@@ -447,6 +467,8 @@ impl OpenOptions {
447467 /// If a file is successfully opened with this option set it will truncate
448468 /// the file to 0 length if it already exists.
449469 ///
470+ /// The file must be opened with write access for truncate to work.
471+ ///
450472 /// # Examples
451473 ///
452474 /// ```no_run
@@ -464,29 +486,92 @@ impl OpenOptions {
464486 /// This option indicates whether a new file will be created if the file
465487 /// does not yet already exist.
466488 ///
489+ /// The file must be opened with write or append access in order to create
490+ /// a new file.
491+ ///
467492 /// # Examples
468493 ///
469494 /// ```no_run
470495 /// use std::fs::OpenOptions;
471496 ///
472- /// let file = OpenOptions::new().create(true).open("foo.txt");
497+ /// let file = OpenOptions::new().write(true). create(true).open("foo.txt");
473498 /// ```
474499 #[ stable( feature = "rust1" , since = "1.0.0" ) ]
475500 pub fn create ( & mut self , create : bool ) -> & mut OpenOptions {
476501 self . 0 . create ( create) ; self
477502 }
478503
504+ /// Sets the option to always create a new file.
505+ ///
506+ /// This option indicates whether a new file will be created.
507+ /// No file is allowed to exist at the target location, also no (dangling)
508+ /// symlink.
509+ ///
510+ /// if `.create_new(true)` is set, `.create()` and `.truncate()` are ignored.
511+ ///
512+ /// The file must be opened with write or append access in order to create
513+ /// a new file.
514+ ///
515+ /// # Examples
516+ ///
517+ /// ```no_run
518+ /// use std::fs::OpenOptions;
519+ ///
520+ /// let file = OpenOptions::new().write(true).create_new(true).open("foo.txt");
521+ /// ```
522+ #[ stable( feature = "expand_open_options" , since = "1.7.0" ) ]
523+ pub fn create_new ( & mut self , create_new : bool ) -> & mut OpenOptions {
524+ self . 0 . create_new ( create_new) ; self
525+ }
526+
527+ /// Pass custom open flags to the operating system.
528+ ///
529+ /// Windows and the various flavours of Unix support flags that are not
530+ /// cross-platform, but that can be useful in some circumstances. On Unix they will
531+ /// be passed as the variable _flags_ to `open`, on Windows as the
532+ /// _dwFlagsAndAttributes_ parameter.
533+ ///
534+ /// The cross-platform options of Rust can do magic: they can set any flag necessary
535+ /// to ensure it works as expected. For example, `.append(true)` on Unix not only
536+ /// sets the flag `O_APPEND`, but also automatically `O_WRONLY` or `O_RDWR`. This
537+ /// special treatment is not available for the custom flags.
538+ ///
539+ /// Custom flags can only set flags, not remove flags set by Rusts options.
540+ ///
541+ /// For the custom flags on Unix, the bits that define the access mode are masked
542+ /// out with `O_ACCMODE`, to ensure they do not interfere with the access mode set
543+ /// by Rusts options.
544+ ///
545+ /// # Examples
546+ ///
547+ /// ```rust,ignore
548+ /// extern crate libc;
549+ /// extern crate winapi;
550+ /// use std::fs::OpenOptions;
551+ ///
552+ /// let options = OpenOptions::new().write(true);
553+ /// if cfg!(unix) { options.custom_flags(libc::O_NOFOLLOW); }
554+ /// if cfg!(windows) { options.custom_flags(winapi::FILE_FLAG_BACKUP_SEMANTICS); }
555+ /// let file = options.open("foo.txt");
556+ /// ```
557+ #[ stable( feature = "expand_open_options" , since = "1.7.0" ) ]
558+ pub fn custom_flags ( & mut self , flags : u32 ) -> & mut OpenOptions {
559+ self . 0 . custom_flags ( flags) ; self
560+ }
561+
479562 /// Opens a file at `path` with the options specified by `self`.
480563 ///
481564 /// # Errors
482565 ///
483566 /// This function will return an error under a number of different
484567 /// circumstances, to include but not limited to:
485568 ///
486- /// * Opening a file that does not exist with read access .
569+ /// * Opening a file that does not exist without setting `create` or `create_new` .
487570 /// * Attempting to open a file with access that the user lacks
488571 /// permissions for
489572 /// * Filesystem-level errors (full disk, etc)
573+ /// * Invalid combinations of open options (truncate without write access,
574+ /// no access mode set, etc)
490575 ///
491576 /// # Examples
492577 ///
@@ -2098,61 +2183,108 @@ mod tests {
20982183
20992184 let mut r = OO :: new ( ) ; r. read ( true ) ;
21002185 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-
2114- check ! ( c( & rw) . create( true ) . open( & tmpdir. join( "c" ) ) ) ;
2115- assert ! ( tmpdir. join( "c" ) . exists( ) ) ;
2186+ let mut rw = OO :: new ( ) ; rw. read ( true ) . write ( true ) ;
2187+ let mut a = OO :: new ( ) ; a. append ( true ) ;
2188+ let mut ra = OO :: new ( ) ; ra. read ( true ) . append ( true ) ;
2189+
2190+ let invalid_options = if cfg ! ( windows) { "The parameter is incorrect" }
2191+ else { "Invalid argument" } ;
2192+
2193+ // Test various combinations of creation modes and access modes.
2194+ //
2195+ // Allowed:
2196+ // creation mode | read | write | read-write | append | read-append |
2197+ // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
2198+ // not set (open existing) | X | X | X | X | X |
2199+ // create | | X | X | X | X |
2200+ // truncate | | X | X | | |
2201+ // create and truncate | | X | X | | |
2202+ // create_new | | X | X | X | X |
2203+ //
2204+ // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
2205+
2206+ // write-only
2207+ check ! ( c( & w) . create_new( true ) . open( & tmpdir. join( "a" ) ) ) ;
2208+ check ! ( c( & w) . create( true ) . truncate( true ) . open( & tmpdir. join( "a" ) ) ) ;
2209+ check ! ( c( & w) . truncate( true ) . open( & tmpdir. join( "a" ) ) ) ;
2210+ check ! ( c( & w) . create( true ) . open( & tmpdir. join( "a" ) ) ) ;
2211+ check ! ( c( & w) . open( & tmpdir. join( "a" ) ) ) ;
2212+
2213+ // read-only
2214+ error ! ( c( & r) . create_new( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2215+ error ! ( c( & r) . create( true ) . truncate( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2216+ error ! ( c( & r) . truncate( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2217+ error ! ( c( & r) . create( true ) . open( & tmpdir. join( "b" ) ) , invalid_options) ;
2218+ check ! ( c( & r) . open( & tmpdir. join( "a" ) ) ) ; // try opening the file created with write_only
2219+
2220+ // read-write
2221+ check ! ( c( & rw) . create_new( true ) . open( & tmpdir. join( "c" ) ) ) ;
2222+ check ! ( c( & rw) . create( true ) . truncate( true ) . open( & tmpdir. join( "c" ) ) ) ;
2223+ check ! ( c( & rw) . truncate( true ) . open( & tmpdir. join( "c" ) ) ) ;
21162224 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( ) ) ) ;
2225+ check ! ( c( & rw) . open( & tmpdir. join( "c" ) ) ) ;
2226+
2227+ // append
2228+ check ! ( c( & a) . create_new( true ) . open( & tmpdir. join( "d" ) ) ) ;
2229+ error ! ( c( & a) . create( true ) . truncate( true ) . open( & tmpdir. join( "d" ) ) , invalid_options) ;
2230+ error ! ( c( & a) . truncate( true ) . open( & tmpdir. join( "d" ) ) , invalid_options) ;
2231+ check ! ( c( & a) . create( true ) . open( & tmpdir. join( "d" ) ) ) ;
2232+ check ! ( c( & a) . open( & tmpdir. join( "d" ) ) ) ;
2233+
2234+ // read-append
2235+ check ! ( c( & ra) . create_new( true ) . open( & tmpdir. join( "e" ) ) ) ;
2236+ error ! ( c( & ra) . create( true ) . truncate( true ) . open( & tmpdir. join( "e" ) ) , invalid_options) ;
2237+ error ! ( c( & ra) . truncate( true ) . open( & tmpdir. join( "e" ) ) , invalid_options) ;
2238+ check ! ( c( & ra) . create( true ) . open( & tmpdir. join( "e" ) ) ) ;
2239+ check ! ( c( & ra) . open( & tmpdir. join( "e" ) ) ) ;
2240+
2241+ // Test opening a file without setting an access mode
2242+ let mut blank = OO :: new ( ) ;
2243+ error ! ( blank. create( true ) . open( & tmpdir. join( "f" ) ) , invalid_options) ;
2244+
2245+ // Test write works
2246+ check ! ( check!( File :: create( & tmpdir. join( "h" ) ) ) . write( "foobar" . as_bytes( ) ) ) ;
2247+
2248+ // Test write fails for read-only
21402249 check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
21412250 {
21422251 let mut f = check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
21432252 assert ! ( f. write( "wut" . as_bytes( ) ) . is_err( ) ) ;
21442253 }
2254+
2255+ // Test write overwrites
2256+ {
2257+ let mut f = check ! ( c( & w) . open( & tmpdir. join( "h" ) ) ) ;
2258+ check ! ( f. write( "baz" . as_bytes( ) ) ) ;
2259+ }
2260+ {
2261+ let mut f = check ! ( c( & r) . open( & tmpdir. join( "h" ) ) ) ;
2262+ let mut b = vec ! [ 0 ; 6 ] ;
2263+ check ! ( f. read( & mut b) ) ;
2264+ assert_eq ! ( b, "bazbar" . as_bytes( ) ) ;
2265+ }
2266+
2267+ // Test truncate works
2268+ {
2269+ let mut f = check ! ( c( & w) . truncate( true ) . open( & tmpdir. join( "h" ) ) ) ;
2270+ check ! ( f. write( "foo" . as_bytes( ) ) ) ;
2271+ }
2272+ assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2273+
2274+ // Test append works
21452275 assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
21462276 {
2147- let mut f = check ! ( c( & w ) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2277+ let mut f = check ! ( c( & a ) . open( & tmpdir. join( "h" ) ) ) ;
21482278 check ! ( f. write( "bar" . as_bytes( ) ) ) ;
21492279 }
21502280 assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 6 ) ;
2281+
2282+ // Test .append(true) equals .write(true).append(true)
21512283 {
2152- let mut f = check ! ( c( & w) . truncate ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2153- check ! ( f. write( "bar " . as_bytes( ) ) ) ;
2284+ let mut f = check ! ( c( & w) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2285+ check ! ( f. write( "baz " . as_bytes( ) ) ) ;
21542286 }
2155- assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2287+ assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 9 ) ;
21562288 }
21572289
21582290 #[ test]
0 commit comments