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