rust/hg-core/src/vfs.rs
author Rapha?l Gom?s <rgomes@octobus.net>
Mon, 04 Nov 2024 11:26:41 +0100
changeset 52303 22d24f6d6411
parent 52294 645d247d4c75
child 52306 a876ab6c3fd5
permissions -rw-r--r--
rust-lib: remove exports for not too common pattern-related types This only muddies the lib and makes the imports more confusing.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
     1
use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     2
use crate::exit_codes;
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     3
use crate::fncache::FnCache;
52178
bd8081e9fd62 rust: don't star export from the `revlog` module
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52168
diff changeset
     4
use crate::revlog::path_encode::path_encode;
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     5
use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     6
use dyn_clone::DynClone;
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
     7
use format_bytes::format_bytes;
47955
e834b79def74 rust: Switch to the memmap2-rs crate
Simon Sapin <simon.sapin@octobus.net>
parents: 47952
diff changeset
     8
use memmap2::{Mmap, MmapOptions};
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
     9
use rand::distributions::{Alphanumeric, DistString};
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    10
use std::fs::{File, Metadata, OpenOptions};
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    11
use std::io::{ErrorKind, Read, Seek, Write};
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    12
use std::os::fd::AsRawFd;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    13
use std::os::unix::fs::{MetadataExt, PermissionsExt};
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
use std::path::{Path, PathBuf};
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    15
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    16
use std::sync::atomic::AtomicUsize;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    17
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
    18
use std::sync::atomic::Ordering;
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    19
use std::sync::OnceLock;
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    20
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
/// Filesystem access abstraction for the contents of a given "base" diretory
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    22
#[derive(Clone)]
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    23
pub struct VfsImpl {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    24
    pub(crate) base: PathBuf,
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    25
    pub readonly: bool,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    26
    pub mode: Option<u32>,
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    27
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    28
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    29
struct FileNotFound(std::io::Error, PathBuf);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    30
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    31
/// Store the umask for the whole process since it's expensive to get.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    32
static UMASK: OnceLock<u32> = OnceLock::new();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    33
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    34
fn get_umask() -> u32 {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    35
    *UMASK.get_or_init(|| unsafe {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    36
        // TODO is there any way of getting the umask without temporarily
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    37
        // setting it? Doesn't this affect all threads in this tiny window?
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    38
        let mask = libc::umask(0);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    39
        libc::umask(mask);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    40
        mask & 0o777
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    41
    })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    42
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    43
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    44
/// Return the (unix) mode with which we will create/fix files
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    45
fn get_mode(base: impl AsRef<Path>) -> Option<u32> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    46
    match base.as_ref().metadata() {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    47
        Ok(meta) => {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    48
            // files in .hg/ will be created using this mode
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    49
            let mode = meta.mode();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    50
            // avoid some useless chmods
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    51
            if (0o777 & !get_umask()) == (0o777 & mode) {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    52
                None
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    53
            } else {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    54
                Some(mode)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    55
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    56
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    57
        Err(_) => None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    58
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    59
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    60
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    61
impl VfsImpl {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    62
    pub fn new(base: PathBuf, readonly: bool) -> Self {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    63
        let mode = get_mode(&base);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    64
        Self {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    65
            base,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    66
            readonly,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    67
            mode,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    68
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    69
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    70
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    71
    // XXX these methods are probably redundant with VFS trait?
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
    72
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    73
    pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    74
        self.base.join(relative_path)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    75
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    76
48345
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    77
    pub fn symlink_metadata(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    78
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    79
        relative_path: impl AsRef<Path>,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    80
    ) -> Result<std::fs::Metadata, HgError> {
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    81
        let path = self.join(relative_path);
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    82
        std::fs::symlink_metadata(&path).when_reading_file(&path)
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    83
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    84
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    85
    pub fn read_link(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    86
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    87
        relative_path: impl AsRef<Path>,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    88
    ) -> Result<PathBuf, HgError> {
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    89
        let path = self.join(relative_path);
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    90
        std::fs::read_link(&path).when_reading_file(&path)
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    91
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    92
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    93
    pub fn read(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    94
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    95
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
    ) -> Result<Vec<u8>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
        let path = self.join(relative_path);
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
        std::fs::read(&path).when_reading_file(&path)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   100
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   101
    /// Returns `Ok(None)` if the file does not exist.
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   102
    pub fn try_read(
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   103
        &self,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   104
        relative_path: impl AsRef<Path>,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   105
    ) -> Result<Option<Vec<u8>>, HgError> {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   106
        match self.read(relative_path) {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   107
            Err(e) => match &e {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   108
                HgError::IoError { error, .. } => match error.kind() {
49914
58074252db3c rust: run `cargo clippy`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   109
                    ErrorKind::NotFound => Ok(None),
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   110
                    _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   111
                },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   112
                _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   113
            },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   114
            Ok(v) => Ok(Some(v)),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   115
        }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   116
    }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
   117
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   118
    fn mmap_open_gen(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   119
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   120
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   121
    ) -> Result<Result<Mmap, FileNotFound>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   122
        let path = self.join(relative_path);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   123
        let file = match std::fs::File::open(&path) {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   124
            Err(err) => {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   125
                if let ErrorKind::NotFound = err.kind() {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   126
                    return Ok(Err(FileNotFound(err, path)));
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   127
                };
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   128
                return (Err(err)).when_reading_file(&path);
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   129
            }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   130
            Ok(file) => file,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   131
        };
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   132
        // Safety is "enforced" by locks and assuming other processes are
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   133
        // well-behaved. If any misbehaving or malicious process does touch
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   134
        // the index, it could lead to corruption. This is inherent
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   135
        // to file-based `mmap`, though some platforms have some ways of
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   136
        // mitigating.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   137
        // TODO linux: set the immutable flag with `chattr(1)`?
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   138
        let mmap = unsafe { MmapOptions::new().map(&file) }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   139
            .when_reading_file(&path)?;
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   140
        Ok(Ok(mmap))
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   141
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   142
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   143
    pub fn mmap_open_opt(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   144
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   145
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   146
    ) -> Result<Option<Mmap>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   147
        self.mmap_open_gen(relative_path).map(|res| res.ok())
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   148
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   149
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   150
    pub fn mmap_open(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   151
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   152
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   153
    ) -> Result<Mmap, HgError> {
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   154
        match self.mmap_open_gen(relative_path)? {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   155
            Err(FileNotFound(err, path)) => Err(err).when_reading_file(&path),
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   156
            Ok(res) => Ok(res),
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   157
        }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   158
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   159
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   160
    #[cfg(unix)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   161
    pub fn create_symlink(
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   162
        &self,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   163
        relative_link_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   164
        target_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   165
    ) -> Result<(), HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   166
        let link_path = self.join(relative_link_path);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   167
        std::os::unix::fs::symlink(target_path, &link_path)
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   168
            .when_writing_file(&link_path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   169
    }
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   170
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   171
    /// Write `contents` into a temporary file, then rename to `relative_path`.
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   172
    /// This makes writing to a file "atomic": a reader opening that path will
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   173
    /// see either the previous contents of the file or the complete new
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   174
    /// content, never a partial write.
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   175
    pub fn atomic_write(
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   176
        &self,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   177
        relative_path: impl AsRef<Path>,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   178
        contents: &[u8],
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   179
    ) -> Result<(), HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   180
        let mut tmp = tempfile::NamedTempFile::new_in(&self.base)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   181
            .when_writing_file(&self.base)?;
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   182
        tmp.write_all(contents)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   183
            .and_then(|()| tmp.flush())
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   184
            .when_writing_file(tmp.path())?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   185
        let path = self.join(relative_path);
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   186
        tmp.persist(&path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   187
            .map_err(|e| e.error)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   188
            .when_writing_file(&path)?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   189
        Ok(())
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   190
    }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   191
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   192
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   193
fn fs_metadata(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   194
    path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   195
) -> Result<Option<std::fs::Metadata>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   196
    let path = path.as_ref();
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   197
    match path.metadata() {
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   198
        Ok(meta) => Ok(Some(meta)),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   199
        Err(error) => match error.kind() {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   200
            // TODO: when we require a Rust version where `NotADirectory` is
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   201
            // stable, invert this logic and return None for it and `NotFound`
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   202
            // and propagate any other error.
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   203
            ErrorKind::PermissionDenied => Err(error).with_context(|| {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   204
                IoErrorContext::ReadingMetadata(path.to_owned())
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   205
            }),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   206
            _ => Ok(None),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   207
        },
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   208
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   209
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   210
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   211
/// Abstraction over the files handled by a [`Vfs`].
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   212
#[derive(Debug)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   213
pub enum VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   214
    Atomic(AtomicFile),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   215
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   216
    Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   217
        file: File,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   218
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   219
        /// If `Some`, check (and maybe fix) this file's timestamp ambiguity.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   220
        /// See [`is_filetime_ambiguous`].
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   221
        check_ambig: Option<Metadata>,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   222
    },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   223
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   224
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   225
impl VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   226
    pub fn normal(file: File, path: PathBuf) -> Self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   227
        Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   228
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   229
            check_ambig: None,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   230
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   231
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   232
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   233
    pub fn normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   234
        file: File,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   235
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   236
    ) -> Result<Self, HgError> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   237
        Ok(Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   238
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   239
            check_ambig: Some(path.metadata().when_reading_file(&path)?),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   240
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   241
        })
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   242
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   243
    pub fn try_clone(&self) -> Result<VfsFile, HgError> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   244
        Ok(match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   245
            VfsFile::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   246
                fp,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   247
                temp_path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   248
                check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   249
                target_name,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   250
                is_open,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   251
            }) => Self::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   252
                fp: fp.try_clone().when_reading_file(temp_path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   253
                temp_path: temp_path.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   254
                check_ambig: *check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   255
                target_name: target_name.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   256
                is_open: *is_open,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   257
            }),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   258
            VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   259
                file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   260
                check_ambig,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   261
                path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   262
            } => Self::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   263
                file: file.try_clone().when_reading_file(path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   264
                check_ambig: check_ambig.clone(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   265
                path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   266
            },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   267
        })
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   268
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   269
    pub fn set_len(&self, len: u64) -> Result<(), std::io::Error> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   270
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   271
            VfsFile::Atomic(atomic_file) => atomic_file.fp.set_len(len),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   272
            VfsFile::Normal { file, .. } => file.set_len(len),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   273
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   274
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   275
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   276
    pub fn metadata(&self) -> Result<std::fs::Metadata, std::io::Error> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   277
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   278
            VfsFile::Atomic(atomic_file) => atomic_file.fp.metadata(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   279
            VfsFile::Normal { file, .. } => file.metadata(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   280
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   281
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   282
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   283
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   284
impl AsRawFd for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   285
    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   286
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   287
            VfsFile::Atomic(atomic_file) => atomic_file.fp.as_raw_fd(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   288
            VfsFile::Normal { file, .. } => file.as_raw_fd(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   289
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   290
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   291
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   292
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   293
impl Seek for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   294
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   295
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   296
            VfsFile::Atomic(atomic_file) => atomic_file.seek(pos),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   297
            VfsFile::Normal { file, .. } => file.seek(pos),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   298
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   299
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   300
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   301
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   302
impl Read for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   303
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   304
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   305
            VfsFile::Atomic(atomic_file) => atomic_file.fp.read(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   306
            VfsFile::Normal { file, .. } => file.read(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   307
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   308
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   309
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   310
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   311
impl Write for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   312
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   313
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   314
            VfsFile::Atomic(atomic_file) => atomic_file.fp.write(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   315
            VfsFile::Normal { file, .. } => file.write(buf),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   316
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   317
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   318
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   319
    fn flush(&mut self) -> std::io::Result<()> {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   320
        match self {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   321
            VfsFile::Atomic(atomic_file) => atomic_file.fp.flush(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   322
            VfsFile::Normal { file, .. } => file.flush(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   323
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   324
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   325
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   326
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   327
impl Drop for VfsFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   328
    fn drop(&mut self) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   329
        if let VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   330
            path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   331
            check_ambig: Some(old),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   332
            ..
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   333
        } = self
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   334
        {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   335
            avoid_timestamp_ambiguity(path, old)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   336
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   337
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   338
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   339
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   340
/// Records the number of times we've fixed a timestamp ambiguity, only
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   341
/// applicable for tests.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   342
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   343
static TIMESTAMP_FIXES_CALLS: AtomicUsize = AtomicUsize::new(0);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   344
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   345
fn avoid_timestamp_ambiguity(path: &Path, old: &Metadata) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   346
    if let Ok(new) = path.metadata() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   347
        let is_ambiguous = is_filetime_ambiguous(&new, old);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   348
        if is_ambiguous {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   349
            let advanced =
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   350
                filetime::FileTime::from_unix_time(old.mtime() + 1, 0);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   351
            if filetime::set_file_times(path, advanced, advanced).is_ok() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   352
                #[cfg(test)]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   353
                {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   354
                    TIMESTAMP_FIXES_CALLS.fetch_add(1, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   355
                }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   356
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   357
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   358
    }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   359
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   360
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   361
/// Examine whether new stat is ambiguous against old one
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   362
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   363
/// "S[N]" below means stat of a file at N-th change:
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   364
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   365
/// - S[n-1].ctime  < S[n].ctime: can detect change of a file
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   366
/// - S[n-1].ctime == S[n].ctime
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   367
///   - S[n-1].ctime  < S[n].mtime: means natural advancing (*1)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   368
///   - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   369
///   - S[n-1].ctime  > S[n].mtime: never occurs naturally (don't care)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   370
/// - S[n-1].ctime  > S[n].ctime: never occurs naturally (don't care)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   371
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   372
/// Case (*2) above means that a file was changed twice or more at
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   373
/// same time in sec (= S[n-1].ctime), and comparison of timestamp
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   374
/// is ambiguous.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   375
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   376
/// Base idea to avoid such ambiguity is "advance mtime 1 sec, if
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   377
/// timestamp is ambiguous".
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   378
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   379
/// But advancing mtime only in case (*2) doesn't work as
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   380
/// expected, because naturally advanced S[n].mtime in case (*1)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   381
/// might be equal to manually advanced S[n-1 or earlier].mtime.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   382
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   383
/// Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   384
/// treated as ambiguous regardless of mtime, to avoid overlooking
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   385
/// by confliction between such mtime.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   386
///
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   387
/// Advancing mtime "if isambig(new, old)" ensures "S[n-1].mtime !=
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   388
/// S[n].mtime", even if size of a file isn't changed.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   389
fn is_filetime_ambiguous(new: &Metadata, old: &Metadata) -> bool {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   390
    new.ctime() == old.ctime()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   391
}
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   392
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   393
/// Writable file object that atomically updates a file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   394
///
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   395
/// All writes will go to a temporary copy of the original file. Call
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   396
/// [`Self::close`] when you are done writing, and [`Self`] will rename
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   397
/// the temporary copy to the original name, making the changes
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   398
/// visible. If the object is destroyed without being closed, all your
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   399
/// writes are discarded.
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   400
#[derive(Debug)]
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   401
pub struct AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   402
    /// The temporary file to write to
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   403
    fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   404
    /// Path of the temp file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   405
    temp_path: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   406
    /// Used when stat'ing the file, is useful only if the target file is
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   407
    /// guarded by any lock (e.g. repo.lock or repo.wlock).
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   408
    check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   409
    /// Path of the target file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   410
    target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   411
    /// Whether the file is open or not
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   412
    is_open: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   413
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   414
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   415
impl AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   416
    pub fn new(
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   417
        target_path: impl AsRef<Path>,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   418
        empty: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   419
        check_ambig: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   420
    ) -> Result<Self, HgError> {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   421
        let target_path = target_path.as_ref().to_owned();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   422
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   423
        let random_id =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   424
            Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   425
        let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   426
            target_path.file_name().expect("target has no filename");
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   427
        let filename = get_bytes_from_path(filename);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   428
        let temp_filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   429
            format_bytes!(b".{}-{}~", filename, random_id.as_bytes());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   430
        let temp_path =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   431
            target_path.with_file_name(get_path_from_bytes(&temp_filename));
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   432
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   433
        if !empty {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   434
            std::fs::copy(&target_path, &temp_path)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   435
                .with_context(|| IoErrorContext::CopyingFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   436
                    from: target_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   437
                    to: temp_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   438
                })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   439
                // If it doesn't exist, create it on open
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   440
                .io_not_found_as_none()?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   441
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   442
        let fp = std::fs::OpenOptions::new()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   443
            .write(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   444
            .create(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   445
            .truncate(empty)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   446
            .open(&temp_path)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   447
            .when_writing_file(&temp_path)?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   448
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   449
        Ok(Self {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   450
            fp,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   451
            temp_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   452
            check_ambig,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   453
            target_name: target_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   454
            is_open: true,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   455
        })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   456
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   457
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   458
    pub fn from_file(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   459
        fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   460
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   461
        temp_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   462
        target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   463
    ) -> Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   464
        Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   465
            fp,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   466
            check_ambig,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   467
            temp_path: temp_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   468
            target_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   469
            is_open: true,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   470
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   471
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   472
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   473
    /// Write `buf` to the temporary file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   474
    pub fn write_all(&mut self, buf: &[u8]) -> Result<(), std::io::Error> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   475
        self.fp.write_all(buf)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   476
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   477
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   478
    fn target(&self) -> PathBuf {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   479
        self.temp_path
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   480
            .parent()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   481
            .expect("should not be at the filesystem root")
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   482
            .join(&self.target_name)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   483
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   484
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   485
    /// Close the temporary file and rename to the target
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   486
    pub fn close(mut self) -> Result<(), std::io::Error> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   487
        self.fp.flush()?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   488
        let target = self.target();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   489
        if self.check_ambig {
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   490
            if let Ok(stat) = target.metadata() {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   491
                std::fs::rename(&self.temp_path, &target)?;
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   492
                avoid_timestamp_ambiguity(&target, &stat);
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   493
            } else {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   494
                std::fs::rename(&self.temp_path, target)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   495
            }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   496
        } else {
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   497
            std::fs::rename(&self.temp_path, target)?;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   498
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   499
        self.is_open = false;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   500
        Ok(())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   501
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   502
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   503
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   504
impl Seek for AtomicFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   505
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   506
        self.fp.seek(pos)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   507
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   508
}
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
   509
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   510
impl Drop for AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   511
    fn drop(&mut self) {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   512
        if self.is_open {
52179
82007b8c189e rust-vfs: delete the temp file and not the target on drop
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52178
diff changeset
   513
            std::fs::remove_file(&self.temp_path).ok();
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   514
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   515
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   516
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   517
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   518
/// Abstracts over the VFS to allow for different implementations of the
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   519
/// filesystem layer (like passing one from Python).
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   520
pub trait Vfs: Sync + Send + DynClone {
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   521
    /// Open a [`VfsFile::Normal`] for reading the file at `filename`,
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   522
    /// relative to this VFS's root.
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   523
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   524
    /// Open a [`VfsFile::Normal`] for writing and reading the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   525
    /// `filename`, relative to this VFS's root.
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   526
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   527
    /// Open a [`VfsFile::Normal`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   528
    /// `filename`, relative to this VFS's root. This file will be checked
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   529
    /// for an ambiguous mtime on [`drop`]. See [`is_filetime_ambiguous`].
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   530
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   531
    /// Create a [`VfsFile::Normal`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   532
    /// `filename`, relative to this VFS's root. If the file already exists,
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   533
    /// it will be truncated to 0 bytes.
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   534
    fn create(
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   535
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   536
        filename: &Path,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   537
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   538
    ) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   539
    /// Create a [`VfsFile::Atomic`] for reading and writing the file at
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   540
    /// `filename`, relative to this VFS's root. If the file already exists,
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   541
    /// it will be truncated to 0 bytes.
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   542
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   543
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   544
        filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   545
        check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   546
    ) -> Result<VfsFile, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   547
    /// Return the total file size in bytes of the open `file`. Errors are
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   548
    /// usual IO errors (invalid file handle, permissions, etc.)
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   549
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   550
    /// Return `true` if `filename` exists relative to this VFS's root. Errors
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   551
    /// will coerce to `false`, to this also returns `false` if there are
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   552
    /// IO problems. This is fine because any operation that actually tries
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   553
    /// to do anything with this path will get the same error.
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   554
    fn exists(&self, filename: &Path) -> bool;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   555
    /// Remove the file at `filename` relative to this VFS's root. Errors
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   556
    /// are the usual IO errors (lacking permission, file does not exist, etc.)
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   557
    fn unlink(&self, filename: &Path) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   558
    /// Rename the file `from` to `to`, both relative to this VFS's root.
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   559
    /// Errors are the usual IO errors (lacking permission, file does not
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   560
    /// exist, etc.). If `check_ambig` is `true`, the VFS will check for an
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   561
    /// ambiguous mtime on rename. See [`is_filetime_ambiguous`].
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   562
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   563
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   564
        from: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   565
        to: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   566
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   567
    ) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   568
    /// Rename the file `from` to `to`, both relative to this VFS's root.
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   569
    /// Errors are the usual IO errors (lacking permission, file does not
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   570
    /// exist, etc.). If `check_ambig` is passed, the VFS will check for an
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   571
    /// ambiguous mtime on rename. See [`is_filetime_ambiguous`].
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   572
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError>;
52182
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   573
    /// Returns the absolute root path of this VFS, relative to which all
85bff84f0ad5 rust-vfs: add docstrings to all VFS methods on the trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52181
diff changeset
   574
    /// operations are done.
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   575
    fn base(&self) -> &Path;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   576
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   577
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   578
/// These methods will need to be implemented once `rhg` (and other) non-Python
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   579
/// users of `hg-core` start doing more on their own, like writing to files.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   580
impl Vfs for VfsImpl {
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   581
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   582
        // TODO auditpath
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   583
        let path = self.base.join(filename);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   584
        Ok(VfsFile::normal(
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   585
            std::fs::File::open(&path).when_reading_file(&path)?,
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   586
            filename.to_owned(),
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   587
        ))
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   588
    }
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   589
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   590
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   591
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   592
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   593
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   594
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   595
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   596
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   597
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   598
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   599
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   600
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   601
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   602
        Ok(VfsFile::normal(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   603
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   604
                .create(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   605
                .create_new(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   606
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   607
                .read(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   608
                .open(&path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   609
                .when_writing_file(&path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   610
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   611
        ))
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   612
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   613
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   614
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   615
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   616
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   617
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   618
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   619
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   620
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   621
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   622
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   623
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   624
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   625
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   626
        // TODO auditpath
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   627
        VfsFile::normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   628
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   629
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   630
                .read(true) // Can be used for reading to save on `open` calls
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   631
                .create(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   632
                .open(&path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   633
                .when_reading_file(&path)?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   634
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   635
        )
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   636
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   637
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   638
    fn create(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   639
        &self,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   640
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   641
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   642
    ) -> Result<VfsFile, HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   643
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   644
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   645
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   646
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   647
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   648
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   649
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   650
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   651
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   652
        let parent = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   653
        std::fs::create_dir_all(parent).when_writing_file(parent)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   654
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   655
        let file = OpenOptions::new()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   656
            .create(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   657
            .truncate(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   658
            .write(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   659
            .read(true)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   660
            .open(&path)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   661
            .when_writing_file(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   662
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   663
        if let Some(mode) = self.mode {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   664
            // Creating the file with the right permission (with `.mode()`)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   665
            // may not work since umask takes effect for file creation.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   666
            // So we need to fix the permission after creating the file.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   667
            fix_directory_permissions(&self.base, &path, mode)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   668
            let perm = std::fs::Permissions::from_mode(mode & 0o666);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   669
            std::fs::set_permissions(&path, perm).when_writing_file(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   670
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   671
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   672
        Ok(VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   673
            file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   674
            check_ambig: if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   675
                Some(path.metadata().when_reading_file(&path)?)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   676
            } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   677
                None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   678
            },
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   679
            path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   680
        })
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   681
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   682
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   683
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   684
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   685
        _filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   686
        _check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   687
    ) -> Result<VfsFile, HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   688
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   689
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   690
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   691
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   692
        Ok(file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   693
            .metadata()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   694
            .map_err(|e| {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   695
                HgError::abort(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   696
                    format!("Could not get file metadata: {}", e),
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   697
                    exit_codes::ABORT,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   698
                    None,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   699
                )
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   700
            })?
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   701
            .size())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   702
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   703
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   704
    fn exists(&self, filename: &Path) -> bool {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   705
        self.base.join(filename).exists()
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   706
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   707
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   708
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   709
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   710
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   711
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   712
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   713
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   714
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   715
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   716
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   717
        std::fs::remove_file(&path)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   718
            .with_context(|| IoErrorContext::RemovingFile(path))
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   719
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   720
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   721
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   722
        &self,
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   723
        from: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   724
        to: &Path,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   725
        check_ambig: bool,
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   726
    ) -> Result<(), HgError> {
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   727
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   728
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   729
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   730
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   731
                None,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   732
            ));
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   733
        }
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   734
        let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   735
            Some(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   736
                from.metadata()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   737
                    .when_reading_file(from)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   738
                    .io_not_found_as_none()?,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   739
            )
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   740
        } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   741
            None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   742
        };
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   743
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   744
        let to = self.base.join(to);
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   745
        std::fs::rename(&from, &to).with_context(|| {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   746
            IoErrorContext::RenamingFile {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   747
                from,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   748
                to: to.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   749
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   750
        })?;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   751
        if let Some(Some(old)) = old_stat {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   752
            avoid_timestamp_ambiguity(&to, &old);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   753
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   754
        Ok(())
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   755
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   756
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   757
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   758
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   759
        let to = self.base.join(to);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   760
        std::fs::copy(&from, &to)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   761
            .with_context(|| IoErrorContext::CopyingFile { from, to })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   762
            .map(|_| ())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   763
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   764
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   765
    fn base(&self) -> &Path {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   766
        &self.base
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   767
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   768
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   769
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   770
fn fix_directory_permissions(
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   771
    base: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   772
    path: &Path,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   773
    mode: u32,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   774
) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   775
    let mut ancestors = path.ancestors();
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   776
    ancestors.next(); // yields the path itself
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   777
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   778
    for ancestor in ancestors {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   779
        if ancestor == base {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   780
            break;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   781
        }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   782
        let perm = std::fs::Permissions::from_mode(mode);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   783
        std::fs::set_permissions(ancestor, perm)
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   784
            .when_writing_file(ancestor)?;
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   785
    }
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   786
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   787
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   788
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   789
/// A VFS that understands the `fncache` store layout (file encoding), and
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   790
/// adds new entries to the `fncache`.
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   791
/// TODO Only works when using from Python for now.
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   792
pub struct FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   793
    inner: VfsImpl,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   794
    fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   795
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   796
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   797
impl Clone for FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   798
    fn clone(&self) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   799
        Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   800
            inner: self.inner.clone(),
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   801
            fncache: dyn_clone::clone_box(&*self.fncache),
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   802
        }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   803
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   804
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   805
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   806
impl FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   807
    pub fn new(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   808
        base: PathBuf,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   809
        readonly: bool,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   810
        fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   811
    ) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   812
        let inner = VfsImpl::new(base, readonly);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   813
        Self { inner, fncache }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   814
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   815
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   816
    fn maybe_add_to_fncache(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   817
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   818
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   819
        encoded_path: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   820
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   821
        let relevant_file = (filename.starts_with("data/")
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   822
            || filename.starts_with("meta/"))
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   823
            && is_revlog_file(filename);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   824
        if relevant_file {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   825
            let not_load = !self.fncache.is_loaded()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   826
                && (self.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   827
                    && self
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   828
                        .inner
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   829
                        .join(encoded_path)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   830
                        .metadata()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   831
                        .when_reading_file(encoded_path)?
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   832
                        .size()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   833
                        != 0);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   834
            if !not_load {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   835
                self.fncache.add(filename);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   836
            }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   837
        };
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   838
        Ok(())
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   839
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   840
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   841
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   842
impl Vfs for FnCacheVfs {
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   843
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   844
        let encoded = path_encode(&get_bytes_from_path(filename));
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   845
        let filename = get_path_from_bytes(&encoded);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   846
        self.inner.open(filename)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   847
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   848
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   849
    fn open_write(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   850
        let encoded = path_encode(&get_bytes_from_path(filename));
52294
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   851
        let encoded_path = get_path_from_bytes(&encoded);
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   852
        self.maybe_add_to_fncache(filename, encoded_path)?;
645d247d4c75 rust-vfs: rename `open` to `open_write` and `open_read` to `open`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52182
diff changeset
   853
        self.inner.open_write(encoded_path)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   854
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   855
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   856
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   857
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   858
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   859
        self.inner.open_check_ambig(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   860
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   861
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   862
    fn create(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   863
        &self,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   864
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   865
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   866
    ) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   867
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   868
        let encoded_path = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   869
        self.maybe_add_to_fncache(filename, encoded_path)?;
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   870
        self.inner.create(encoded_path, check_ambig)
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   871
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   872
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   873
    fn create_atomic(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   874
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   875
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   876
        check_ambig: bool,
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   877
    ) -> Result<VfsFile, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   878
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   879
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   880
        self.inner.create_atomic(filename, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   881
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   882
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
   883
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
52168
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   884
        self.inner.file_size(file)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   885
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   886
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   887
    fn exists(&self, filename: &Path) -> bool {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   888
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   889
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   890
        self.inner.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   891
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   892
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   893
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   894
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   895
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   896
        self.inner.unlink(filename)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   897
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   898
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   899
    fn rename(
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   900
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   901
        from: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   902
        to: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   903
        check_ambig: bool,
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   904
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   905
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   906
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   907
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   908
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   909
        self.inner.rename(from, to, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   910
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   911
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   912
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   913
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   914
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   915
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   916
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   917
        self.inner.copy(from, to)
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   918
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   919
    fn base(&self) -> &Path {
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   920
        self.inner.base()
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   921
    }
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   922
}
067ec8574c33 hg-core: add FnCacheVFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52167
diff changeset
   923
52167
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   924
/// Detects whether `path` is a hardlink and does a tmp copy + rename erase
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   925
/// to turn it into its own file. Revlogs are usually hardlinked when doing
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   926
/// a local clone, and we don't want to modify the original repo.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   927
fn copy_in_place_if_hardlink(path: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   928
    let metadata = path.metadata().when_writing_file(path)?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   929
    if metadata.nlink() > 0 {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   930
        // If it's hardlinked, copy it and rename it back before changing it.
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   931
        let tmpdir = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   932
        let name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   933
        let tmpfile = tmpdir.join(name);
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   934
        std::fs::create_dir_all(tmpfile.parent().expect("file at root"))
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   935
            .with_context(|| IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   936
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   937
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   938
            })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   939
        std::fs::copy(path, &tmpfile).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   940
            IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   941
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   942
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   943
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   944
        })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   945
        std::fs::rename(&tmpfile, path).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   946
            IoErrorContext::RenamingFile {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   947
                from: tmpfile,
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   948
                to: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   949
            }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   950
        })?;
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   951
    }
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   952
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   953
}
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   954
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   955
pub fn is_revlog_file(path: impl AsRef<Path>) -> bool {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   956
    path.as_ref()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   957
        .extension()
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   958
        .map(|ext| {
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   959
            ["i", "idx", "d", "dat", "n", "nd", "sda"]
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   960
                .contains(&ext.to_string_lossy().as_ref())
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   961
        })
7be39c5110c9 hg-core: add a complete VFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52157
diff changeset
   962
        .unwrap_or(false)
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   963
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   964
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   965
pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   966
    Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   967
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   968
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   969
pub(crate) fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   970
    Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file()))
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   971
}
50180
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   972
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   973
/// Returns whether the given `path` is on a network file system.
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   974
/// Taken from `cargo`'s codebase.
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   975
#[cfg(target_os = "linux")]
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   976
pub(crate) fn is_on_nfs_mount(path: impl AsRef<Path>) -> bool {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   977
    use std::ffi::CString;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   978
    use std::mem;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   979
    use std::os::unix::prelude::*;
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   980
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   981
    let path = match CString::new(path.as_ref().as_os_str().as_bytes()) {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   982
        Ok(path) => path,
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   983
        Err(_) => return false,
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   984
    };
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   985
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   986
    unsafe {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   987
        let mut buf: libc::statfs = mem::zeroed();
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   988
        let r = libc::statfs(path.as_ptr(), &mut buf);
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   989
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   990
        r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   991
    }
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   992
}
50274
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   993
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   994
/// Similar to what Cargo does; although detecting NFS (or non-local
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   995
/// file systems) _should_ be possible on other operating systems,
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   996
/// we'll just assume that mmap() works there, for now; after all,
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   997
/// _some_ functionality is better than a compile error, i.e. none at
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   998
/// all
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   999
#[cfg(not(target_os = "linux"))]
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1000
pub(crate) fn is_on_nfs_mount(_path: impl AsRef<Path>) -> bool {
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1001
    false
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
  1002
}
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1003
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1004
#[cfg(test)]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1005
mod tests {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1006
    use super::*;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1007
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1008
    #[test]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1009
    fn test_atomic_file() {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1010
        let dir = tempfile::tempdir().unwrap().into_path();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1011
        let target_path = dir.join("sometargetname");
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1012
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1013
        for empty in [true, false] {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1014
            let file = AtomicFile::new(&target_path, empty, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1015
            assert!(file.is_open);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1016
            let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1017
                file.temp_path.file_name().unwrap().to_str().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1018
            // Make sure we have a coherent temp name
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1019
            assert_eq!(filename.len(), 29, "{}", filename);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1020
            assert!(filename.contains("sometargetname"));
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1021
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1022
            // Make sure the temp file is created in the same folder
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1023
            assert_eq!(target_path.parent(), file.temp_path.parent());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1024
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1025
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1026
        assert!(!target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1027
        std::fs::write(&target_path, "version 1").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1028
        let mut file = AtomicFile::new(&target_path, false, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1029
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1030
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1031
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1032
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1033
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1034
        let temp_path = file.temp_path.to_owned();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1035
        // test that dropping the file should discard the temp file and not
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1036
        // affect the target path.
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1037
        drop(file);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1038
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1039
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1040
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1041
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1042
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1043
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1044
        let mut file = AtomicFile::new(&target_path, false, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1045
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1046
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1047
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1048
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1049
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1050
        file.close().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1051
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1052
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1053
            b"version 2!".to_vec(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1054
            "{}",
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1055
            std::fs::read_to_string(&target_path).unwrap()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1056
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1057
        assert!(target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1058
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1059
    }
52181
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1060
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1061
    #[test]
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1062
    fn test_vfs_file_check_ambig() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1063
        let dir = tempfile::tempdir().unwrap().into_path();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1064
        let file_path = dir.join("file");
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1065
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1066
        fn vfs_file_write(file_path: &Path, check_ambig: bool) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1067
            let file = std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1068
                .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1069
                .open(file_path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1070
                .unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1071
            let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1072
                Some(file.metadata().unwrap())
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1073
            } else {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1074
                None
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1075
            };
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1076
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1077
            let mut vfs_file = VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1078
                file,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1079
                path: file_path.to_owned(),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1080
                check_ambig: old_stat,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1081
            };
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1082
            vfs_file.write_all(b"contents").unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1083
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1084
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1085
        std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1086
            .write(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1087
            .create(true)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1088
            .truncate(false)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1089
            .open(&file_path)
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1090
            .unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1091
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1092
        let number_of_writes = 3;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1093
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1094
        // Try multiple times, because reproduction of an ambiguity depends
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1095
        // on "filesystem time"
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1096
        for _ in 0..5 {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1097
            TIMESTAMP_FIXES_CALLS.store(0, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1098
            vfs_file_write(&file_path, false);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1099
            let old_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1100
            if old_stat.ctime() != old_stat.mtime() {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1101
                // subsequent changing never causes ambiguity
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1102
                continue;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1103
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1104
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1105
            // Repeat atomic write with `check_ambig == true`, to examine
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1106
            // whether the mtime is advanced multiple times as expected
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1107
            for _ in 0..number_of_writes {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1108
                vfs_file_write(&file_path, true);
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1109
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1110
            let new_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1111
            if !is_filetime_ambiguous(&new_stat, &old_stat) {
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1112
                // timestamp ambiguity was naturally avoided while repetition
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1113
                continue;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1114
            }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1115
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1116
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1117
                TIMESTAMP_FIXES_CALLS.load(Ordering::Relaxed),
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1118
                number_of_writes
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1119
            );
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1120
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1121
                old_stat.mtime() + number_of_writes as i64,
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1122
                file_path.metadata().unwrap().mtime()
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1123
            );
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1124
            break;
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1125
        }
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1126
        // If we've arrived here without breaking, we might not have
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1127
        // tested anything because the platform is too slow. This test will
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1128
        // still work on fast platforms.
8d35941689af rust-vfs: support checkambig
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52180
diff changeset
  1129
    }
52180
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 52179
diff changeset
  1130
}