@@ -375,7 +375,7 @@ impl<'a> Seek for &'a File {
375
375
}
376
376
377
377
impl OpenOptions {
378
- /// Creates a blank net set of options ready for configuration.
378
+ /// Creates a blank new set of options ready for configuration.
379
379
///
380
380
/// All options are initially set to `false`.
381
381
///
@@ -384,7 +384,8 @@ impl OpenOptions {
384
384
/// ```no_run
385
385
/// use std::fs::OpenOptions;
386
386
///
387
- /// let file = OpenOptions::new().open("foo.txt");
387
+ /// let mut options = OpenOptions::new();
388
+ /// let file = options.read(true).open("foo.txt");
388
389
/// ```
389
390
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
390
391
pub fn new ( ) -> OpenOptions {
@@ -413,6 +414,9 @@ impl OpenOptions {
413
414
/// This option, when true, will indicate that the file should be
414
415
/// `write`-able if opened.
415
416
///
417
+ /// If a file already exist, the contents of that file get overwritten, but it is
418
+ /// not truncated.
419
+ ///
416
420
/// # Examples
417
421
///
418
422
/// ```no_run
@@ -429,13 +433,29 @@ impl OpenOptions {
429
433
///
430
434
/// This option, when true, means that writes will append to a file instead
431
435
/// 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.
432
452
///
433
453
/// # Examples
434
454
///
435
455
/// ```no_run
436
456
/// use std::fs::OpenOptions;
437
457
///
438
- /// let file = OpenOptions::new().write(true). append(true).open("foo.txt");
458
+ /// let file = OpenOptions::new().append(true).open("foo.txt");
439
459
/// ```
440
460
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
441
461
pub fn append ( & mut self , append : bool ) -> & mut OpenOptions {
@@ -447,6 +467,8 @@ impl OpenOptions {
447
467
/// If a file is successfully opened with this option set it will truncate
448
468
/// the file to 0 length if it already exists.
449
469
///
470
+ /// The file must be opened with write access for truncate to work.
471
+ ///
450
472
/// # Examples
451
473
///
452
474
/// ```no_run
@@ -464,29 +486,92 @@ impl OpenOptions {
464
486
/// This option indicates whether a new file will be created if the file
465
487
/// does not yet already exist.
466
488
///
489
+ /// The file must be opened with write or append access in order to create
490
+ /// a new file.
491
+ ///
467
492
/// # Examples
468
493
///
469
494
/// ```no_run
470
495
/// use std::fs::OpenOptions;
471
496
///
472
- /// let file = OpenOptions::new().create(true).open("foo.txt");
497
+ /// let file = OpenOptions::new().write(true). create(true).open("foo.txt");
473
498
/// ```
474
499
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
475
500
pub fn create ( & mut self , create : bool ) -> & mut OpenOptions {
476
501
self . 0 . create ( create) ; self
477
502
}
478
503
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
+
479
562
/// Opens a file at `path` with the options specified by `self`.
480
563
///
481
564
/// # Errors
482
565
///
483
566
/// This function will return an error under a number of different
484
567
/// circumstances, to include but not limited to:
485
568
///
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` .
487
570
/// * Attempting to open a file with access that the user lacks
488
571
/// permissions for
489
572
/// * Filesystem-level errors (full disk, etc)
573
+ /// * Invalid combinations of open options (truncate without write access,
574
+ /// no access mode set, etc)
490
575
///
491
576
/// # Examples
492
577
///
@@ -2098,61 +2183,108 @@ mod tests {
2098
2183
2099
2184
let mut r = OO :: new ( ) ; r. read ( true ) ;
2100
2185
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" ) ) ) ;
2116
2224
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
2140
2249
check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
2141
2250
{
2142
2251
let mut f = check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
2143
2252
assert ! ( f. write( "wut" . as_bytes( ) ) . is_err( ) ) ;
2144
2253
}
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
2145
2275
assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2146
2276
{
2147
- let mut f = check ! ( c( & w ) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2277
+ let mut f = check ! ( c( & a ) . open( & tmpdir. join( "h" ) ) ) ;
2148
2278
check ! ( f. write( "bar" . as_bytes( ) ) ) ;
2149
2279
}
2150
2280
assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 6 ) ;
2281
+
2282
+ // Test .append(true) equals .write(true).append(true)
2151
2283
{
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( ) ) ) ;
2154
2286
}
2155
- assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2287
+ assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 9 ) ;
2156
2288
}
2157
2289
2158
2290
#[ test]
0 commit comments