rust/hg-core/src/vfs.rs
author Raphaël Gomès <rgomes@octobus.net>
Thu, 10 Oct 2024 15:54:45 +0200
changeset 52185 8d35941689af
parent 52184 735bf027dd1d
child 52186 85bff84f0ad5
permissions -rw-r--r--
rust-vfs: support checkambig This was missing from the Rust code, which means worse caching. See https://wiki.mercurial-scm.org/ExactCacheValidationPlan. Explanations on what ambiguity means inline.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
     1
use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
     2
use crate::exit_codes;
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
     3
use crate::fncache::FnCache;
52182
bd8081e9fd62 rust: don't star export from the `revlog` module
Raphaël Gomès <rgomes@octobus.net>
parents: 52172
diff changeset
     4
use crate::revlog::path_encode::path_encode;
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
     5
use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
     6
use dyn_clone::DynClone;
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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};
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
     9
use rand::distributions::{Alphanumeric, DistString};
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    10
use std::fs::{File, Metadata, OpenOptions};
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    11
use std::io::{ErrorKind, Read, Seek, Write};
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    12
use std::os::fd::AsRawFd;
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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};
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    15
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    16
use std::sync::atomic::AtomicUsize;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    17
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
    18
use std::sync::atomic::Ordering;
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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
51868
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,
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    25
    pub readonly: bool,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
diff changeset
    32
static UMASK: OnceLock<u32> = OnceLock::new();
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    33
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    34
fn get_umask() -> u32 {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    35
    *UMASK.get_or_init(|| unsafe {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
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: 52161
diff changeset
    38
        let mask = libc::umask(0);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    39
        libc::umask(mask);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    40
        mask & 0o777
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    41
    })
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    42
}
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    43
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
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: 52161
diff changeset
    46
    match base.as_ref().metadata() {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    47
        Ok(meta) => {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
diff changeset
    49
            let mode = meta.mode();
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    50
            // avoid some useless chmods
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    51
            if (0o777 & !get_umask()) == (0o777 & mode) {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    52
                None
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    53
            } else {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    54
                Some(mode)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    55
            }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    56
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    57
        Err(_) => None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    58
    }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    59
}
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    60
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
    61
impl VfsImpl {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
diff changeset
    63
        let mode = get_mode(&base);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    64
        Self {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    65
            base,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    66
            readonly,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    67
            mode,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    68
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    69
    }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
    70
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
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: 52161
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
        };
51868
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> {
51868
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();
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   211
/// Abstraction over the files handled by a [`Vfs`].
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   212
#[derive(Debug)]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   213
pub enum VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   214
    Atomic(AtomicFile),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   215
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   216
    Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   217
        file: File,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   218
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   220
        /// See [`is_filetime_ambiguous`].
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   221
        check_ambig: Option<Metadata>,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   222
    },
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   223
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   224
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   225
impl VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   226
    pub fn normal(file: File, path: PathBuf) -> Self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   227
        Self::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   228
            file,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   229
            check_ambig: None,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   230
            path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   231
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   232
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   233
    pub fn normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   234
        file: File,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   235
        path: PathBuf,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   236
    ) -> Result<Self, HgError> {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   237
        Ok(Self::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   238
            file,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   240
            path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   241
        })
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   242
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   243
    pub fn try_clone(&self) -> Result<VfsFile, HgError> {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   244
        Ok(match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   245
            VfsFile::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   246
                fp,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   247
                temp_path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   248
                check_ambig,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   249
                target_name,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   250
                is_open,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   251
            }) => Self::Atomic(AtomicFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   253
                temp_path: temp_path.clone(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   254
                check_ambig: *check_ambig,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   255
                target_name: target_name.clone(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   256
                is_open: *is_open,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   257
            }),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   258
            VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   259
                file,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   260
                check_ambig,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   261
                path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   262
            } => Self::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   263
                file: file.try_clone().when_reading_file(path)?,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   264
                check_ambig: check_ambig.clone(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   265
                path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   266
            },
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   267
        })
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   268
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   270
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   272
            VfsFile::Normal { file, .. } => file.set_len(len),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   273
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   274
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   275
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   277
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   278
            VfsFile::Atomic(atomic_file) => atomic_file.fp.metadata(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   279
            VfsFile::Normal { file, .. } => file.metadata(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   280
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   281
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   282
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   283
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   284
impl AsRawFd for VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   286
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   288
            VfsFile::Normal { file, .. } => file.as_raw_fd(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   289
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   290
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   291
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   292
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   293
impl Seek for VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   295
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   296
            VfsFile::Atomic(atomic_file) => atomic_file.seek(pos),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   297
            VfsFile::Normal { file, .. } => file.seek(pos),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   298
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   299
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   300
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   301
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   302
impl Read for VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   304
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   306
            VfsFile::Normal { file, .. } => file.read(buf),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   307
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   308
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   309
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   310
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   311
impl Write for VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   313
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   315
            VfsFile::Normal { file, .. } => file.write(buf),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   316
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   317
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   318
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   319
    fn flush(&mut self) -> std::io::Result<()> {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   320
        match self {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   321
            VfsFile::Atomic(atomic_file) => atomic_file.fp.flush(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   322
            VfsFile::Normal { file, .. } => file.flush(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   323
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   324
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   325
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   326
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   327
impl Drop for VfsFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   328
    fn drop(&mut self) {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   329
        if let VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   330
            path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   331
            check_ambig: Some(old),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   332
            ..
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   333
        } = self
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   334
        {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   335
            avoid_timestamp_ambiguity(path, old)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   336
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   337
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   338
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   339
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   341
/// applicable for tests.
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   342
#[cfg(test)]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   343
static TIMESTAMP_FIXES_CALLS: AtomicUsize = AtomicUsize::new(0);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   344
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   345
fn avoid_timestamp_ambiguity(path: &Path, old: &Metadata) {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   346
    if let Ok(new) = path.metadata() {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   347
        let is_ambiguous = is_filetime_ambiguous(&new, old);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   348
        if is_ambiguous {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   349
            let advanced =
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
diff changeset
   352
                #[cfg(test)]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   353
                {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   354
                    TIMESTAMP_FIXES_CALLS.fetch_add(1, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   355
                }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   356
            }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   357
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   358
    }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   359
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   360
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   362
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   364
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   366
/// - S[n-1].ctime == S[n].ctime
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
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: 52184
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: 52184
diff changeset
   371
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
diff changeset
   374
/// is ambiguous.
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   375
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
diff changeset
   377
/// timestamp is ambiguous".
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   378
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
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: 52184
diff changeset
   382
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
diff changeset
   385
/// by confliction between such mtime.
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   386
///
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
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: 52184
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: 52184
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: 52184
diff changeset
   390
    new.ctime() == old.ctime()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   391
}
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   392
51868
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.
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   400
#[derive(Debug)]
51868
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(
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   417
        target_path: impl AsRef<Path>,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   418
        empty: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   419
        check_ambig: bool,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   420
    ) -> Result<Self, HgError> {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   422
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   423
        let random_id =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   425
        let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
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: 52183
diff changeset
   428
        let temp_filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   430
        let temp_path =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   432
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   433
        if !empty {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   435
                .with_context(|| IoErrorContext::CopyingFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   436
                    from: target_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   437
                    to: temp_path.to_owned(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   438
                })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   440
                .io_not_found_as_none()?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   441
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   442
        let fp = std::fs::OpenOptions::new()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   443
            .write(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   444
            .create(true)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   445
            .truncate(empty)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   446
            .open(&temp_path)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   447
            .when_writing_file(&temp_path)?;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   448
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   449
        Ok(Self {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   450
            fp,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   451
            temp_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   452
            check_ambig,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   453
            target_name: target_path,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   454
            is_open: true,
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   455
        })
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   456
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   457
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   458
    pub fn from_file(
51868
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 {
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   490
            if let Ok(stat) = target.metadata() {
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   491
                std::fs::rename(&self.temp_path, &target)?;
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   492
                avoid_timestamp_ambiguity(&target, &stat);
51868
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 {
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   497
            std::fs::rename(&self.temp_path, target)?;
51868
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
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   504
impl Seek for AtomicFile {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
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: 52183
diff changeset
   506
        self.fp.seek(pos)
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   507
    }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   508
}
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   509
51868
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 {
52183
82007b8c189e rust-vfs: delete the temp file and not the target on drop
Raphaël Gomès <rgomes@octobus.net>
parents: 52182
diff changeset
   513
            std::fs::remove_file(&self.temp_path).ok();
51868
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 {
52161
46c68c0fe137 rust-vfs: add a TODO to remember a decision taken about naming
Raphaël Gomès <rgomes@octobus.net>
parents: 51868
diff changeset
   521
    // TODO make `open` readonly and make `open_read` an `open_write`
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   522
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError>;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   523
    fn open_read(&self, filename: &Path) -> Result<VfsFile, HgError>;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   524
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError>;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   525
    fn create(
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   526
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   527
        filename: &Path,
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   528
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   529
    ) -> Result<VfsFile, HgError>;
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   530
    /// Must truncate the new file if exist
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   531
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   532
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   533
        filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   534
        check_ambig: bool,
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   535
    ) -> Result<VfsFile, HgError>;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   536
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError>;
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   537
    fn exists(&self, filename: &Path) -> bool;
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   538
    fn unlink(&self, filename: &Path) -> Result<(), HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   539
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   540
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   541
        from: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   542
        to: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   543
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   544
    ) -> Result<(), HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   545
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError>;
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   546
    fn base(&self) -> &Path;
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   547
}
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   548
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   549
/// 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
   550
/// 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
   551
impl Vfs for VfsImpl {
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   552
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   553
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   554
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   555
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   556
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   557
                None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   558
            ));
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   559
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   560
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   561
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   562
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   563
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   564
        Ok(VfsFile::normal(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   565
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   566
                .create(false)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   567
                .create_new(false)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   568
                .write(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   569
                .read(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   570
                .open(&path)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   571
                .when_writing_file(&path)?,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   572
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   573
        ))
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   574
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   575
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   576
    fn open_read(&self, filename: &Path) -> Result<VfsFile, HgError> {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   577
        // TODO auditpath
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   578
        let path = self.base.join(filename);
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   579
        Ok(VfsFile::normal(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   580
            std::fs::File::open(&path).when_reading_file(&path)?,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   581
            filename.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   582
        ))
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   583
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   584
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   585
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   586
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   587
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   588
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   589
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   590
                None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   591
            ));
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   592
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   593
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   594
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   595
        copy_in_place_if_hardlink(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   596
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   597
        // TODO auditpath
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   598
        VfsFile::normal_check_ambig(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   599
            OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   600
                .write(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   601
                .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: 52184
diff changeset
   602
                .create(false)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   603
                .open(&path)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   604
                .when_reading_file(&path)?,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   605
            path.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   606
        )
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   607
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   608
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   609
    fn create(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   610
        &self,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   611
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   612
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   613
    ) -> Result<VfsFile, HgError> {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   614
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   615
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   616
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   617
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   618
                None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   619
            ));
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   620
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   621
        // TODO auditpath
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   622
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   623
        let parent = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   624
        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: 52161
diff changeset
   625
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   626
        let file = OpenOptions::new()
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   627
            .create(true)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   628
            .truncate(true)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   629
            .write(true)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   630
            .read(true)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   631
            .open(&path)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   632
            .when_writing_file(&path)?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   633
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   634
        if let Some(mode) = self.mode {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   635
            // Creating the file with the right permission (with `.mode()`)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   636
            // 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: 52161
diff changeset
   637
            // 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: 52161
diff changeset
   638
            fix_directory_permissions(&self.base, &path, mode)?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   639
            let perm = std::fs::Permissions::from_mode(mode & 0o666);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   640
            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: 52161
diff changeset
   641
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   642
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   643
        Ok(VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   644
            file,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   645
            check_ambig: if check_ambig {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   646
                Some(path.metadata().when_reading_file(&path)?)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   647
            } else {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   648
                None
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   649
            },
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   650
            path: path.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   651
        })
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   652
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   653
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   654
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   655
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   656
        _filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   657
        _check_ambig: bool,
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   658
    ) -> Result<VfsFile, HgError> {
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   659
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   660
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   661
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   662
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   663
        Ok(file
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   664
            .metadata()
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   665
            .map_err(|e| {
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   666
                HgError::abort(
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   667
                    format!("Could not get file metadata: {}", e),
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   668
                    exit_codes::ABORT,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   669
                    None,
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   670
                )
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   671
            })?
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   672
            .size())
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   673
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   674
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   675
    fn exists(&self, filename: &Path) -> bool {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   676
        self.base.join(filename).exists()
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   677
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   678
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   679
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   680
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   681
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   682
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   683
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   684
                None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   685
            ));
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   686
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   687
        let path = self.base.join(filename);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   688
        std::fs::remove_file(&path)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   689
            .with_context(|| IoErrorContext::RemovingFile(path))
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   690
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   691
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   692
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   693
        &self,
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   694
        from: &Path,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   695
        to: &Path,
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   696
        check_ambig: bool,
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   697
    ) -> Result<(), HgError> {
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   698
        if self.readonly {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   699
            return Err(HgError::abort(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   700
                "write access in a readonly vfs",
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   701
                exit_codes::ABORT,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   702
                None,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   703
            ));
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   704
        }
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   705
        let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   706
            Some(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   707
                from.metadata()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   708
                    .when_reading_file(from)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   709
                    .io_not_found_as_none()?,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   710
            )
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   711
        } else {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   712
            None
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   713
        };
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   714
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   715
        let to = self.base.join(to);
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   716
        std::fs::rename(&from, &to).with_context(|| {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   717
            IoErrorContext::RenamingFile {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   718
                from,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   719
                to: to.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   720
            }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   721
        })?;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   722
        if let Some(Some(old)) = old_stat {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   723
            avoid_timestamp_ambiguity(&to, &old);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   724
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   725
        Ok(())
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   726
    }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   727
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   728
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   729
        let from = self.base.join(from);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   730
        let to = self.base.join(to);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   731
        std::fs::copy(&from, &to)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   732
            .with_context(|| IoErrorContext::CopyingFile { from, to })
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   733
            .map(|_| ())
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   734
    }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   735
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   736
    fn base(&self) -> &Path {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   737
        &self.base
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   738
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   739
}
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   740
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   741
fn fix_directory_permissions(
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   742
    base: &Path,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   743
    path: &Path,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   744
    mode: u32,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   745
) -> Result<(), HgError> {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   746
    let mut ancestors = path.ancestors();
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   747
    ancestors.next(); // yields the path itself
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   748
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   749
    for ancestor in ancestors {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   750
        if ancestor == base {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   751
            break;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   752
        }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   753
        let perm = std::fs::Permissions::from_mode(mode);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   754
        std::fs::set_permissions(ancestor, perm)
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   755
            .when_writing_file(ancestor)?;
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   756
    }
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   757
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   758
}
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   759
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   760
/// A VFS that understands the `fncache` store layout (file encoding), and
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   761
/// adds new entries to the `fncache`.
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   762
/// TODO Only works when using from Python for now.
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   763
pub struct FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   764
    inner: VfsImpl,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   765
    fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   766
}
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   767
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   768
impl Clone for FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   769
    fn clone(&self) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   770
        Self {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   771
            inner: self.inner.clone(),
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   772
            fncache: dyn_clone::clone_box(&*self.fncache),
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   773
        }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   774
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   775
}
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   776
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   777
impl FnCacheVfs {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   778
    pub fn new(
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   779
        base: PathBuf,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   780
        readonly: bool,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   781
        fncache: Box<dyn FnCache>,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   782
    ) -> Self {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   783
        let inner = VfsImpl::new(base, readonly);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   784
        Self { inner, fncache }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   785
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   786
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   787
    fn maybe_add_to_fncache(
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   788
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   789
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   790
        encoded_path: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   791
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   792
        let relevant_file = (filename.starts_with("data/")
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   793
            || filename.starts_with("meta/"))
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   794
            && is_revlog_file(filename);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   795
        if relevant_file {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   796
            let not_load = !self.fncache.is_loaded()
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   797
                && (self.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   798
                    && self
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   799
                        .inner
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   800
                        .join(encoded_path)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   801
                        .metadata()
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   802
                        .when_reading_file(encoded_path)?
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   803
                        .size()
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   804
                        != 0);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   805
            if !not_load {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   806
                self.fncache.add(filename);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   807
            }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   808
        };
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   809
        Ok(())
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   810
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   811
}
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   812
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   813
impl Vfs for FnCacheVfs {
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   814
    fn open(&self, filename: &Path) -> Result<VfsFile, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   815
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   816
        let encoded_path = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   817
        self.maybe_add_to_fncache(filename, encoded_path)?;
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   818
        self.inner.open(encoded_path)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   819
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   820
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   821
    fn open_read(&self, filename: &Path) -> Result<VfsFile, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   822
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   823
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   824
        self.inner.open_read(filename)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   825
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   826
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   827
    fn open_check_ambig(&self, filename: &Path) -> Result<VfsFile, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   828
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   829
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   830
        self.inner.open_check_ambig(filename)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   831
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   832
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   833
    fn create(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   834
        &self,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   835
        filename: &Path,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   836
        check_ambig: bool,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   837
    ) -> Result<VfsFile, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   838
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   839
        let encoded_path = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   840
        self.maybe_add_to_fncache(filename, encoded_path)?;
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   841
        self.inner.create(encoded_path, check_ambig)
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   842
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   843
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   844
    fn create_atomic(
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   845
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   846
        filename: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   847
        check_ambig: bool,
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   848
    ) -> Result<VfsFile, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   849
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   850
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   851
        self.inner.create_atomic(filename, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   852
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   853
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
   854
    fn file_size(&self, file: &VfsFile) -> Result<u64, HgError> {
52172
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   855
        self.inner.file_size(file)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   856
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   857
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   858
    fn exists(&self, filename: &Path) -> bool {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   859
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   860
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   861
        self.inner.exists(filename)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   862
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   863
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   864
    fn unlink(&self, filename: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   865
        let encoded = path_encode(&get_bytes_from_path(filename));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   866
        let filename = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   867
        self.inner.unlink(filename)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   868
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   869
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   870
    fn rename(
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   871
        &self,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   872
        from: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   873
        to: &Path,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   874
        check_ambig: bool,
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   875
    ) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   876
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   877
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   878
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   879
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   880
        self.inner.rename(from, to, check_ambig)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   881
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   882
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   883
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError> {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   884
        let encoded = path_encode(&get_bytes_from_path(from));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   885
        let from = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   886
        let encoded = path_encode(&get_bytes_from_path(to));
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   887
        let to = get_path_from_bytes(&encoded);
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   888
        self.inner.copy(from, to)
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   889
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   890
    fn base(&self) -> &Path {
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   891
        self.inner.base()
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   892
    }
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   893
}
067ec8574c33 hg-core: add FnCacheVFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52171
diff changeset
   894
52171
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   895
/// 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: 52161
diff changeset
   896
/// 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: 52161
diff changeset
   897
/// 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: 52161
diff changeset
   898
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: 52161
diff changeset
   899
    let metadata = path.metadata().when_writing_file(path)?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   900
    if metadata.nlink() > 0 {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   901
        // 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: 52161
diff changeset
   902
        let tmpdir = path.parent().expect("file at root");
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   903
        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: 52161
diff changeset
   904
        let tmpfile = tmpdir.join(name);
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   905
        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: 52161
diff changeset
   906
            .with_context(|| IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   907
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   908
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   909
            })?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   910
        std::fs::copy(path, &tmpfile).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   911
            IoErrorContext::CopyingFile {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   912
                from: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   913
                to: tmpfile.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   914
            }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   915
        })?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   916
        std::fs::rename(&tmpfile, path).with_context(|| {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   917
            IoErrorContext::RenamingFile {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   918
                from: tmpfile,
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   919
                to: path.to_owned(),
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   920
            }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   921
        })?;
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   922
    }
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   923
    Ok(())
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   924
}
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   925
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   926
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: 52161
diff changeset
   927
    path.as_ref()
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   928
        .extension()
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   929
        .map(|ext| {
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   930
            ["i", "idx", "d", "dat", "n", "nd", "sda"]
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   931
                .contains(&ext.to_string_lossy().as_ref())
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   932
        })
7be39c5110c9 hg-core: add a complete VFS
Raphaël Gomès <rgomes@octobus.net>
parents: 52161
diff changeset
   933
        .unwrap_or(false)
51868
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   934
}
db7dbe6f7bb2 rust: add Vfs trait
Raphaël Gomès <rgomes@octobus.net>
parents: 50274
diff changeset
   935
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   936
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
   937
    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
   938
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   939
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   940
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
   941
    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
   942
}
50180
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   943
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   944
/// 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
   945
/// 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
   946
#[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
   947
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
   948
    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
   949
    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
   950
    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
   951
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   952
    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
   953
        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
   954
        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
   955
    };
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   956
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   957
    unsafe {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   958
        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
   959
        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
   960
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   961
        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
   962
    }
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Raphaël Gomès <rgomes@octobus.net>
parents: 49485
diff changeset
   963
}
50274
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   964
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   965
/// 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
   966
/// 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
   967
/// 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
   968
/// _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
   969
/// all
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   970
#[cfg(not(target_os = "linux"))]
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   971
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
   972
    false
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   973
}
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   974
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   975
#[cfg(test)]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   976
mod tests {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   977
    use super::*;
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   978
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   979
    #[test]
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   980
    fn test_atomic_file() {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   981
        let dir = tempfile::tempdir().unwrap().into_path();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   982
        let target_path = dir.join("sometargetname");
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   983
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   984
        for empty in [true, false] {
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   985
            let file = AtomicFile::new(&target_path, empty, false).unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   986
            assert!(file.is_open);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   987
            let filename =
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   988
                file.temp_path.file_name().unwrap().to_str().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   989
            // Make sure we have a coherent temp name
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   990
            assert_eq!(filename.len(), 29, "{}", filename);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   991
            assert!(filename.contains("sometargetname"));
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   992
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   993
            // 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: 52183
diff changeset
   994
            assert_eq!(target_path.parent(), file.temp_path.parent());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   995
        }
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   996
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   997
        assert!(!target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   998
        std::fs::write(&target_path, "version 1").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
   999
        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: 52183
diff changeset
  1000
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1001
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1002
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1003
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1004
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1005
        let temp_path = file.temp_path.to_owned();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1006
        // 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: 52183
diff changeset
  1007
        // affect the target path.
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1008
        drop(file);
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1009
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1010
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1011
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1012
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1013
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1014
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1015
        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: 52183
diff changeset
  1016
        file.write_all(b"version 2!").unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1017
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1018
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1019
            b"version 1".to_vec()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1020
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1021
        file.close().unwrap();
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1022
        assert_eq!(
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1023
            std::fs::read(&target_path).unwrap(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1024
            b"version 2!".to_vec(),
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1025
            "{}",
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1026
            std::fs::read_to_string(&target_path).unwrap()
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1027
        );
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1028
        assert!(target_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1029
        assert!(!temp_path.exists());
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1030
    }
52185
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1031
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1032
    #[test]
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1033
    fn test_vfs_file_check_ambig() {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1034
        let dir = tempfile::tempdir().unwrap().into_path();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1035
        let file_path = dir.join("file");
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1036
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1037
        fn vfs_file_write(file_path: &Path, check_ambig: bool) {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1038
            let file = std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1039
                .write(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1040
                .open(file_path)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1041
                .unwrap();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1042
            let old_stat = if check_ambig {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1043
                Some(file.metadata().unwrap())
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1044
            } else {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1045
                None
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1046
            };
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1047
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1048
            let mut vfs_file = VfsFile::Normal {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1049
                file,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1050
                path: file_path.to_owned(),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1051
                check_ambig: old_stat,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1052
            };
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1053
            vfs_file.write_all(b"contents").unwrap();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1054
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1055
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1056
        std::fs::OpenOptions::new()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1057
            .write(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1058
            .create(true)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1059
            .truncate(false)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1060
            .open(&file_path)
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1061
            .unwrap();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1062
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1063
        let number_of_writes = 3;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1064
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1065
        // Try multiple times, because reproduction of an ambiguity depends
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1066
        // on "filesystem time"
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1067
        for _ in 0..5 {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1068
            TIMESTAMP_FIXES_CALLS.store(0, Ordering::Relaxed);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1069
            vfs_file_write(&file_path, false);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1070
            let old_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1071
            if old_stat.ctime() != old_stat.mtime() {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1072
                // subsequent changing never causes ambiguity
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1073
                continue;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1074
            }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1075
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1076
            // Repeat atomic write with `check_ambig == true`, to examine
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1077
            // whether the mtime is advanced multiple times as expected
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1078
            for _ in 0..number_of_writes {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1079
                vfs_file_write(&file_path, true);
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1080
            }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1081
            let new_stat = file_path.metadata().unwrap();
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1082
            if !is_filetime_ambiguous(&new_stat, &old_stat) {
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1083
                // timestamp ambiguity was naturally avoided while repetition
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1084
                continue;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1085
            }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1086
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1087
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1088
                TIMESTAMP_FIXES_CALLS.load(Ordering::Relaxed),
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1089
                number_of_writes
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1090
            );
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1091
            assert_eq!(
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1092
                old_stat.mtime() + number_of_writes as i64,
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1093
                file_path.metadata().unwrap().mtime()
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1094
            );
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1095
            break;
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1096
        }
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1097
        // If we've arrived here without breaking, we might not have
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1098
        // tested anything because the platform is too slow. This test will
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1099
        // still work on fast platforms.
8d35941689af rust-vfs: support checkambig
Raphaël Gomès <rgomes@octobus.net>
parents: 52184
diff changeset
  1100
    }
52184
735bf027dd1d rust-vfs: add tests to `AtomicFile`
Raphaël Gomès <rgomes@octobus.net>
parents: 52183
diff changeset
  1101
}