rust/hg-core/src/repo.rs
changeset 49217 13dfad0f9f7a
parent 49177 90a15199cbc6
parent 49202 2d0e22171ef9
child 50180 be019ac8c1e4
child 49930 e98fd81bb151
equal deleted inserted replaced
49212:d3d3495a5749 49217:13dfad0f9f7a
   454             };
   454             };
   455 
   455 
   456             let data_filename = format!("dirstate.{}", uuid);
   456             let data_filename = format!("dirstate.{}", uuid);
   457             let data_filename = self.hg_vfs().join(data_filename);
   457             let data_filename = self.hg_vfs().join(data_filename);
   458             let mut options = std::fs::OpenOptions::new();
   458             let mut options = std::fs::OpenOptions::new();
   459             if append {
   459             options.write(true);
   460                 options.append(true);
   460 
   461             } else {
   461             // Why are we not using the O_APPEND flag when appending?
   462                 options.write(true).create_new(true);
   462             //
   463             }
   463             // - O_APPEND makes it trickier to deal with garbage at the end of
       
   464             //   the file, left by a previous uncommitted transaction. By
       
   465             //   starting the write at [old_data_size] we make sure we erase
       
   466             //   all such garbage.
       
   467             //
       
   468             // - O_APPEND requires to special-case 0-byte writes, whereas we
       
   469             //   don't need that.
       
   470             //
       
   471             // - Some OSes have bugs in implementation O_APPEND:
       
   472             //   revlog.py talks about a Solaris bug, but we also saw some ZFS
       
   473             //   bug: https://github.com/openzfs/zfs/pull/3124,
       
   474             //   https://github.com/openzfs/zfs/issues/13370
       
   475             //
       
   476             if !append {
       
   477                 options.create_new(true);
       
   478             }
       
   479 
   464             let data_size = (|| {
   480             let data_size = (|| {
   465                 // TODO: loop and try another random ID if !append and this
   481                 // TODO: loop and try another random ID if !append and this
   466                 // returns `ErrorKind::AlreadyExists`? Collision chance of two
   482                 // returns `ErrorKind::AlreadyExists`? Collision chance of two
   467                 // random IDs is one in 2**32
   483                 // random IDs is one in 2**32
   468                 let mut file = options.open(&data_filename)?;
   484                 let mut file = options.open(&data_filename)?;
   469                 if data.is_empty() {
   485                 if append {
   470                     // If we're not appending anything, the data size is the
   486                     file.seek(SeekFrom::Start(old_data_size as u64))?;
   471                     // same as in the previous docket. It is *not* the file
       
   472                     // length, since it could have garbage at the end.
       
   473                     // We don't have to worry about it when we do have data
       
   474                     // to append since we rewrite the root node in this case.
       
   475                     Ok(old_data_size as u64)
       
   476                 } else {
       
   477                     file.write_all(&data)?;
       
   478                     file.flush()?;
       
   479                     // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+
       
   480                     file.seek(SeekFrom::Current(0))
       
   481                 }
   487                 }
       
   488                 file.write_all(&data)?;
       
   489                 file.flush()?;
       
   490                 file.seek(SeekFrom::Current(0))
   482             })()
   491             })()
   483             .when_writing_file(&data_filename)?;
   492             .when_writing_file(&data_filename)?;
   484 
   493 
   485             let packed_dirstate = DirstateDocket::serialize(
   494             let packed_dirstate = DirstateDocket::serialize(
   486                 parents,
   495                 parents,