rust/hg-core/src/vfs.rs
author Rapha?l Gom?s <rgomes@octobus.net>
Mon, 29 Jul 2024 20:28:42 +0200
changeset 52166 1a8466fd904a
parent 52157 46c68c0fe137
child 52167 7be39c5110c9
permissions -rw-r--r--
hg-core: add fncache module For now it's only a super simple trait. It will be used for calling back into Python soon, and later will be fleshed out into a full fncache.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     1
use crate::errors::{HgError, IoErrorContext, IoResultExt};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     2
use crate::exit_codes;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     3
use dyn_clone::DynClone;
47955
e834b79def74 rust: Switch to the memmap2-rs crate
Simon Sapin <simon.sapin@octobus.net>
parents: 47952
diff changeset
     4
use memmap2::{Mmap, MmapOptions};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     5
use std::fs::File;
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
     6
use std::io::{ErrorKind, Write};
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
     7
use std::os::unix::fs::MetadataExt;
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     8
use std::path::{Path, PathBuf};
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
     9
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    10
/// Filesystem access abstraction for the contents of a given "base" diretory
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    11
#[derive(Clone)]
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    12
pub struct VfsImpl {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    13
    pub(crate) base: PathBuf,
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    14
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    15
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    16
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
    17
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    18
impl VfsImpl {
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    19
    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
    20
        self.base.join(relative_path)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    21
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    22
48345
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    23
    pub fn symlink_metadata(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    24
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    25
        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
    26
    ) -> 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
    27
        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
    28
        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
    29
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    30
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    31
    pub fn read_link(
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    32
        &self,
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    33
        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
    34
    ) -> Result<PathBuf, HgError> {
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    35
        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
    36
        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
    37
    }
d5a91701f7dc rhg: Fix status desambiguation of symlinks and executable files
Simon Sapin <simon.sapin@octobus.net>
parents: 48199
diff changeset
    38
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    39
    pub fn read(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    40
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    41
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    42
    ) -> Result<Vec<u8>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    43
        let path = self.join(relative_path);
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    44
        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
    45
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    46
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    47
    /// 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
    48
    pub fn try_read(
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    49
        &self,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    50
        relative_path: impl AsRef<Path>,
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    51
    ) -> Result<Option<Vec<u8>>, HgError> {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    52
        match self.read(relative_path) {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    53
            Err(e) => match &e {
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    54
                HgError::IoError { error, .. } => match error.kind() {
49914
58074252db3c rust: run `cargo clippy`
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
    55
                    ErrorKind::NotFound => Ok(None),
49485
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    56
                    _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    57
                },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    58
                _ => Err(e),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    59
            },
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    60
            Ok(v) => Ok(Some(v)),
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    61
        }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    62
    }
ffd4b1f1c9cb rhg: add sparse support
Rapha?l Gom?s <rgomes@octobus.net>
parents: 48418
diff changeset
    63
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    64
    fn mmap_open_gen(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    65
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    66
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    67
    ) -> Result<Result<Mmap, FileNotFound>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    68
        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
    69
        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
    70
            Err(err) => {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    71
                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
    72
                    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
    73
                };
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    74
                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
    75
            }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    76
            Ok(file) => file,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    77
        };
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    78
        // 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
    79
        // 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
    80
        // 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
    81
        // 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
    82
        // mitigating.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
    83
        // 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
    84
        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
    85
            .when_reading_file(&path)?;
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    86
        Ok(Ok(mmap))
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    87
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    88
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    89
    pub fn mmap_open_opt(
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    90
        &self,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    91
        relative_path: impl AsRef<Path>,
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    92
    ) -> Result<Option<Mmap>, HgError> {
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    93
        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
    94
    }
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
    95
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    96
    pub fn mmap_open(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    97
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    98
        relative_path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
    99
    ) -> Result<Mmap, HgError> {
48199
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   100
        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
   101
            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
   102
            Ok(res) => Ok(res),
9d0e5629cfbf rhg: do not fail when the repo is empty
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents: 47955
diff changeset
   103
        }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   104
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   105
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   106
    pub fn rename(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   107
        &self,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   108
        relative_from: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   109
        relative_to: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   110
    ) -> Result<(), HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   111
        let from = self.join(relative_from);
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   112
        let to = self.join(relative_to);
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   113
        std::fs::rename(&from, &to)
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   114
            .with_context(|| IoErrorContext::RenamingFile { from, to })
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   115
    }
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   116
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   117
    pub fn remove_file(
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   118
        &self,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   119
        relative_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   120
    ) -> Result<(), HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   121
        let path = self.join(relative_path);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   122
        std::fs::remove_file(&path)
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   123
            .with_context(|| IoErrorContext::RemovingFile(path))
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   124
    }
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   125
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   126
    #[cfg(unix)]
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   127
    pub fn create_symlink(
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   128
        &self,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   129
        relative_link_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   130
        target_path: impl AsRef<Path>,
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   131
    ) -> Result<(), HgError> {
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   132
        let link_path = self.join(relative_link_path);
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   133
        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
   134
            .when_writing_file(&link_path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   135
    }
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   136
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   137
    /// 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
   138
    /// 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
   139
    /// 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
   140
    /// content, never a partial write.
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   141
    pub fn atomic_write(
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   142
        &self,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   143
        relative_path: impl AsRef<Path>,
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   144
        contents: &[u8],
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   145
    ) -> Result<(), HgError> {
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   146
        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
   147
            .when_writing_file(&self.base)?;
48418
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   148
        tmp.write_all(contents)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   149
            .and_then(|()| tmp.flush())
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   150
            .when_writing_file(tmp.path())?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   151
        let path = self.join(relative_path);
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   152
        tmp.persist(&path)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   153
            .map_err(|e| e.error)
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   154
            .when_writing_file(&path)?;
abeae090ce67 rust: Add Vfs::write_atomic
Simon Sapin <simon.sapin@octobus.net>
parents: 48417
diff changeset
   155
        Ok(())
48417
5734b03ecf3e rhg: Initial repository locking
Simon Sapin <simon.sapin@octobus.net>
parents: 48345
diff changeset
   156
    }
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   157
}
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
fn fs_metadata(
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   160
    path: impl AsRef<Path>,
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   161
) -> Result<Option<std::fs::Metadata>, HgError> {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   162
    let path = path.as_ref();
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   163
    match std::fs::metadata(path) {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   164
        Ok(meta) => Ok(Some(meta)),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   165
        Err(error) => match error.kind() {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   166
            // 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
   167
            // 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
   168
            // and propagate any other error.
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   169
            ErrorKind::PermissionDenied => Err(error).with_context(|| {
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   170
                IoErrorContext::ReadingMetadata(path.to_owned())
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   171
            }),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   172
            _ => Ok(None),
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   173
        },
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   174
    }
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   175
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   176
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   177
/// Writable file object that atomically updates a file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   178
///
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   179
/// 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
   180
/// [`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
   181
/// 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
   182
/// 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
   183
/// writes are discarded.
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   184
pub struct AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   185
    /// The temporary file to write to
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   186
    fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   187
    /// Path of the temp file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   188
    temp_path: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   189
    /// 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
   190
    /// 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
   191
    check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   192
    /// Path of the target file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   193
    target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   194
    /// Whether the file is open or not
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   195
    is_open: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   196
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   197
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   198
impl AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   199
    pub fn new(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   200
        fp: std::fs::File,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   201
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   202
        temp_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   203
        target_name: PathBuf,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   204
    ) -> Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   205
        Self {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   206
            fp,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   207
            check_ambig,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   208
            temp_path: temp_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   209
            target_name,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   210
            is_open: true,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   211
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   212
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   213
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   214
    /// Write `buf` to the temporary file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   215
    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
   216
        self.fp.write_all(buf)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   217
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   218
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   219
    fn target(&self) -> PathBuf {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   220
        self.temp_path
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   221
            .parent()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   222
            .expect("should not be at the filesystem root")
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   223
            .join(&self.target_name)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   224
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   225
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   226
    /// 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
   227
    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
   228
        self.fp.flush()?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   229
        let target = self.target();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   230
        if self.check_ambig {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   231
            if let Ok(stat) = std::fs::metadata(&target) {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   232
                std::fs::rename(&self.temp_path, &target)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   233
                let new_stat = std::fs::metadata(&target)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   234
                let ctime = new_stat.ctime();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   235
                let is_ambiguous = ctime == stat.ctime();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   236
                if is_ambiguous {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   237
                    let advanced =
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   238
                        filetime::FileTime::from_unix_time(ctime + 1, 0);
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   239
                    filetime::set_file_times(target, advanced, advanced)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   240
                }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   241
            } else {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   242
                std::fs::rename(&self.temp_path, target)?;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   243
            }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   244
        } else {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   245
            std::fs::rename(&self.temp_path, target).unwrap();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   246
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   247
        self.is_open = false;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   248
        Ok(())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   249
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   250
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   251
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   252
impl Drop for AtomicFile {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   253
    fn drop(&mut self) {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   254
        if self.is_open {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   255
            std::fs::remove_file(self.target()).ok();
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   256
        }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   257
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   258
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   259
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   260
/// 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
   261
/// filesystem layer (like passing one from Python).
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   262
pub trait Vfs: Sync + Send + DynClone {
52157
46c68c0fe137 rust-vfs: add a TODO to remember a decision taken about naming
Rapha?l Gom?s <rgomes@octobus.net>
parents: 51864
diff changeset
   263
    // TODO make `open` readonly and make `open_read` an `open_write`
51864
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   264
    fn open(&self, filename: &Path) -> Result<std::fs::File, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   265
    fn open_read(&self, filename: &Path) -> Result<std::fs::File, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   266
    fn open_check_ambig(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   267
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   268
        filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   269
    ) -> Result<std::fs::File, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   270
    fn create(&self, filename: &Path) -> Result<std::fs::File, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   271
    /// Must truncate the new file if exist
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   272
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   273
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   274
        filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   275
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   276
    ) -> Result<AtomicFile, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   277
    fn file_size(&self, file: &File) -> Result<u64, HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   278
    fn exists(&self, filename: &Path) -> bool;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   279
    fn unlink(&self, filename: &Path) -> Result<(), HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   280
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   281
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   282
        from: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   283
        to: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   284
        check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   285
    ) -> Result<(), HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   286
    fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError>;
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   287
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   288
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   289
/// 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
   290
/// 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
   291
impl Vfs for VfsImpl {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   292
    fn open(&self, _filename: &Path) -> Result<std::fs::File, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   293
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   294
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   295
    fn open_read(&self, filename: &Path) -> Result<std::fs::File, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   296
        let path = self.base.join(filename);
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   297
        std::fs::File::open(&path).when_reading_file(&path)
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   298
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   299
    fn open_check_ambig(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   300
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   301
        _filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   302
    ) -> Result<std::fs::File, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   303
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   304
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   305
    fn create(&self, _filename: &Path) -> Result<std::fs::File, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   306
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   307
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   308
    fn create_atomic(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   309
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   310
        _filename: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   311
        _check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   312
    ) -> Result<AtomicFile, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   313
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   314
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   315
    fn file_size(&self, file: &File) -> Result<u64, HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   316
        Ok(file
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   317
            .metadata()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   318
            .map_err(|e| {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   319
                HgError::abort(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   320
                    format!("Could not get file metadata: {}", e),
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   321
                    exit_codes::ABORT,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   322
                    None,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   323
                )
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   324
            })?
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   325
            .size())
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   326
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   327
    fn exists(&self, _filename: &Path) -> bool {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   328
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   329
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   330
    fn unlink(&self, _filename: &Path) -> Result<(), HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   331
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   332
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   333
    fn rename(
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   334
        &self,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   335
        _from: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   336
        _to: &Path,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   337
        _check_ambig: bool,
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   338
    ) -> Result<(), HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   339
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   340
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   341
    fn copy(&self, _from: &Path, _to: &Path) -> Result<(), HgError> {
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   342
        todo!()
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   343
    }
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   344
}
db7dbe6f7bb2 rust: add Vfs trait
Rapha?l Gom?s <rgomes@octobus.net>
parents: 50274
diff changeset
   345
47952
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   346
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
   347
    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
   348
}
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   349
9cd35c8c6044 rust: Move VFS code to its own module
Simon Sapin <simon.sapin@octobus.net>
parents:
diff changeset
   350
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
   351
    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
   352
}
50180
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   353
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   354
/// 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
   355
/// 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
   356
#[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
   357
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
   358
    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
   359
    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
   360
    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
   361
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   362
    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
   363
        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
   364
        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
   365
    };
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   366
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   367
    unsafe {
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   368
        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
   369
        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
   370
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   371
        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
   372
    }
be019ac8c1e4 dirstate-v2: don't mmap the data file when on NFS
Rapha?l Gom?s <rgomes@octobus.net>
parents: 49485
diff changeset
   373
}
50274
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   374
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   375
/// 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
   376
/// 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
   377
/// 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
   378
/// _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
   379
/// all
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   380
#[cfg(not(target_os = "linux"))]
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   381
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
   382
    false
0cc19a53cef4 rust: fix building on macOS (issue6801)
Dan Villiom Podlaski Christiansen <danchr@gmail.com>
parents: 50252
diff changeset
   383
}