@@ -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, any write calls on the file will overwrite its
418
+ /// contents, without truncating it.
419
+ ///
416
420
/// # Examples
417
421
///
418
422
/// ```no_run
@@ -429,13 +433,30 @@ 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
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.
432
453
///
433
454
/// # Examples
434
455
///
435
456
/// ```no_run
436
457
/// use std::fs::OpenOptions;
437
458
///
438
- /// let file = OpenOptions::new().write(true). append(true).open("foo.txt");
459
+ /// let file = OpenOptions::new().append(true).open("foo.txt");
439
460
/// ```
440
461
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
441
462
pub fn append ( & mut self , append : bool ) -> & mut OpenOptions {
@@ -447,6 +468,8 @@ impl OpenOptions {
447
468
/// If a file is successfully opened with this option set it will truncate
448
469
/// the file to 0 length if it already exists.
449
470
///
471
+ /// The file must be opened with write access for truncate to work.
472
+ ///
450
473
/// # Examples
451
474
///
452
475
/// ```no_run
@@ -464,29 +487,68 @@ impl OpenOptions {
464
487
/// This option indicates whether a new file will be created if the file
465
488
/// does not yet already exist.
466
489
///
490
+ /// The file must be opened with write or append access in order to create
491
+ /// a new file.
492
+ ///
467
493
/// # Examples
468
494
///
469
495
/// ```no_run
470
496
/// use std::fs::OpenOptions;
471
497
///
472
- /// let file = OpenOptions::new().create(true).open("foo.txt");
498
+ /// let file = OpenOptions::new().write(true). create(true).open("foo.txt");
473
499
/// ```
474
500
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
475
501
pub fn create ( & mut self , create : bool ) -> & mut OpenOptions {
476
502
self . 0 . create ( create) ; self
477
503
}
478
504
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
+
479
538
/// Opens a file at `path` with the options specified by `self`.
480
539
///
481
540
/// # Errors
482
541
///
483
542
/// This function will return an error under a number of different
484
543
/// circumstances, to include but not limited to:
485
544
///
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`.
487
547
/// * Attempting to open a file with access that the user lacks
488
548
/// permissions for
489
549
/// * Filesystem-level errors (full disk, etc)
550
+ /// * Invalid combinations of open options (truncate without write access,
551
+ /// no access mode set, etc)
490
552
///
491
553
/// # Examples
492
554
///
@@ -2098,61 +2160,114 @@ mod tests {
2098
2160
2099
2161
let mut r = OO :: new ( ) ; r. read ( true ) ;
2100
2162
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" ) ) ) ;
2114
2201
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
2140
2226
check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
2141
2227
{
2142
2228
let mut f = check ! ( r. open( & tmpdir. join( "h" ) ) ) ;
2143
2229
assert ! ( f. write( "wut" . as_bytes( ) ) . is_err( ) ) ;
2144
2230
}
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
2145
2252
assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 3 ) ;
2146
2253
{
2147
- let mut f = check ! ( c( & w ) . append ( true ) . open( & tmpdir. join( "h" ) ) ) ;
2254
+ let mut f = check ! ( c( & a ) . open( & tmpdir. join( "h" ) ) ) ;
2148
2255
check ! ( f. write( "bar" . as_bytes( ) ) ) ;
2149
2256
}
2150
2257
assert_eq ! ( check!( fs:: metadata( & tmpdir. join( "h" ) ) ) . len( ) , 6 ) ;
2258
+
2259
+ // Test .append(true) equals .write(true).append(true)
2151
2260
{
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( ) ) ) ;
2154
2263
}
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 > ( ) ;
2156
2271
}
2157
2272
2158
2273
#[ test]
0 commit comments