##// END OF EJS Templates
rust: add Vfs trait...
Raphaël Gomès -
r52761:db7dbe6f default
parent child Browse files
Show More
@@ -180,7 +180,7 dependencies = [
180 180 "js-sys",
181 181 "num-traits",
182 182 "wasm-bindgen",
183 "windows-targets 0.52.0",
183 "windows-targets 0.52.6",
184 184 ]
185 185
186 186 [[package]]
@@ -435,10 +435,16 dependencies = [
435 435 "libc",
436 436 "option-ext",
437 437 "redox_users",
438 "windows-sys",
438 "windows-sys 0.48.0",
439 439 ]
440 440
441 441 [[package]]
442 name = "dyn-clone"
443 version = "1.0.17"
444 source = "registry+https://github.com/rust-lang/crates.io-index"
445 checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
446
447 [[package]]
442 448 name = "either"
443 449 version = "1.8.0"
444 450 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -467,6 +473,18 dependencies = [
467 473 ]
468 474
469 475 [[package]]
476 name = "filetime"
477 version = "0.2.25"
478 source = "registry+https://github.com/rust-lang/crates.io-index"
479 checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
480 dependencies = [
481 "cfg-if",
482 "libc",
483 "libredox",
484 "windows-sys 0.59.0",
485 ]
486
487 [[package]]
470 488 name = "flate2"
471 489 version = "1.0.24"
472 490 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -584,6 +602,8 dependencies = [
584 602 "clap",
585 603 "crossbeam-channel",
586 604 "derive_more",
605 "dyn-clone",
606 "filetime",
587 607 "flate2",
588 608 "format-bytes",
589 609 "hashbrown 0.13.1",
@@ -752,6 +772,7 checksum = "c0ff37bd590ca25063e35af745c3
752 772 dependencies = [
753 773 "bitflags 2.6.0",
754 774 "libc",
775 "redox_syscall 0.5.3",
755 776 ]
756 777
757 778 [[package]]
@@ -1123,6 +1144,15 dependencies = [
1123 1144 ]
1124 1145
1125 1146 [[package]]
1147 name = "redox_syscall"
1148 version = "0.5.3"
1149 source = "registry+https://github.com/rust-lang/crates.io-index"
1150 checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
1151 dependencies = [
1152 "bitflags 2.6.0",
1153 ]
1154
1155 [[package]]
1126 1156 name = "redox_users"
1127 1157 version = "0.4.5"
1128 1158 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1348,7 +1378,7 dependencies = [
1348 1378 "cfg-if",
1349 1379 "fastrand",
1350 1380 "libc",
1351 "redox_syscall",
1381 "redox_syscall 0.2.16",
1352 1382 "remove_dir_all",
1353 1383 "winapi",
1354 1384 ]
@@ -1615,6 +1645,15 dependencies = [
1615 1645 ]
1616 1646
1617 1647 [[package]]
1648 name = "windows-sys"
1649 version = "0.59.0"
1650 source = "registry+https://github.com/rust-lang/crates.io-index"
1651 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
1652 dependencies = [
1653 "windows-targets 0.52.6",
1654 ]
1655
1656 [[package]]
1618 1657 name = "windows-targets"
1619 1658 version = "0.48.5"
1620 1659 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1631,17 +1670,18 dependencies = [
1631 1670
1632 1671 [[package]]
1633 1672 name = "windows-targets"
1634 version = "0.52.0"
1673 version = "0.52.6"
1635 1674 source = "registry+https://github.com/rust-lang/crates.io-index"
1636 checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
1675 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
1637 1676 dependencies = [
1638 "windows_aarch64_gnullvm 0.52.0",
1639 "windows_aarch64_msvc 0.52.0",
1640 "windows_i686_gnu 0.52.0",
1641 "windows_i686_msvc 0.52.0",
1642 "windows_x86_64_gnu 0.52.0",
1643 "windows_x86_64_gnullvm 0.52.0",
1644 "windows_x86_64_msvc 0.52.0",
1677 "windows_aarch64_gnullvm 0.52.6",
1678 "windows_aarch64_msvc 0.52.6",
1679 "windows_i686_gnu 0.52.6",
1680 "windows_i686_gnullvm",
1681 "windows_i686_msvc 0.52.6",
1682 "windows_x86_64_gnu 0.52.6",
1683 "windows_x86_64_gnullvm 0.52.6",
1684 "windows_x86_64_msvc 0.52.6",
1645 1685 ]
1646 1686
1647 1687 [[package]]
@@ -1652,9 +1692,9 checksum = "2b38e32f0abccf9987a4e3079dfb
1652 1692
1653 1693 [[package]]
1654 1694 name = "windows_aarch64_gnullvm"
1655 version = "0.52.0"
1695 version = "0.52.6"
1656 1696 source = "registry+https://github.com/rust-lang/crates.io-index"
1657 checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
1697 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1658 1698
1659 1699 [[package]]
1660 1700 name = "windows_aarch64_msvc"
@@ -1664,9 +1704,9 checksum = "dc35310971f3b2dbbf3f0690a219
1664 1704
1665 1705 [[package]]
1666 1706 name = "windows_aarch64_msvc"
1667 version = "0.52.0"
1707 version = "0.52.6"
1668 1708 source = "registry+https://github.com/rust-lang/crates.io-index"
1669 checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
1709 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1670 1710
1671 1711 [[package]]
1672 1712 name = "windows_i686_gnu"
@@ -1676,9 +1716,15 checksum = "a75915e7def60c94dcef72200b9a
1676 1716
1677 1717 [[package]]
1678 1718 name = "windows_i686_gnu"
1679 version = "0.52.0"
1719 version = "0.52.6"
1680 1720 source = "registry+https://github.com/rust-lang/crates.io-index"
1681 checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
1721 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
1722
1723 [[package]]
1724 name = "windows_i686_gnullvm"
1725 version = "0.52.6"
1726 source = "registry+https://github.com/rust-lang/crates.io-index"
1727 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
1682 1728
1683 1729 [[package]]
1684 1730 name = "windows_i686_msvc"
@@ -1688,9 +1734,9 checksum = "8f55c233f70c4b27f66c523580f7
1688 1734
1689 1735 [[package]]
1690 1736 name = "windows_i686_msvc"
1691 version = "0.52.0"
1737 version = "0.52.6"
1692 1738 source = "registry+https://github.com/rust-lang/crates.io-index"
1693 checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
1739 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1694 1740
1695 1741 [[package]]
1696 1742 name = "windows_x86_64_gnu"
@@ -1700,9 +1746,9 checksum = "53d40abd2583d23e4718fddf1ebe
1700 1746
1701 1747 [[package]]
1702 1748 name = "windows_x86_64_gnu"
1703 version = "0.52.0"
1749 version = "0.52.6"
1704 1750 source = "registry+https://github.com/rust-lang/crates.io-index"
1705 checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
1751 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1706 1752
1707 1753 [[package]]
1708 1754 name = "windows_x86_64_gnullvm"
@@ -1712,9 +1758,9 checksum = "0b7b52767868a23d5bab768e390d
1712 1758
1713 1759 [[package]]
1714 1760 name = "windows_x86_64_gnullvm"
1715 version = "0.52.0"
1761 version = "0.52.6"
1716 1762 source = "registry+https://github.com/rust-lang/crates.io-index"
1717 checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
1763 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1718 1764
1719 1765 [[package]]
1720 1766 name = "windows_x86_64_msvc"
@@ -1724,9 +1770,9 checksum = "ed94fce61571a4006852b7389a06
1724 1770
1725 1771 [[package]]
1726 1772 name = "windows_x86_64_msvc"
1727 version = "0.52.0"
1773 version = "0.52.6"
1728 1774 source = "registry+https://github.com/rust-lang/crates.io-index"
1729 checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
1775 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1730 1776
1731 1777 [[package]]
1732 1778 name = "wyz"
@@ -41,6 +41,8 format-bytes = "0.3.0"
41 41 once_cell = "1.16.0"
42 42 bitvec = "1.0.1"
43 43 chrono = "0.4.34"
44 dyn-clone = "1.0.16"
45 filetime = "0.2.23"
44 46
45 47 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
46 48 # we have a clearer view of which backend is the fastest.
@@ -2,7 +2,7
2 2
3 3 use crate::errors::HgError;
4 4 use crate::errors::HgResultExt;
5 use crate::vfs::Vfs;
5 use crate::vfs::VfsImpl;
6 6 use std::io;
7 7 use std::io::ErrorKind;
8 8
@@ -21,7 +21,7 pub enum LockError {
21 21 /// The return value of `f` is dropped in that case. If all is successful, the
22 22 /// return value of `f` is forwarded.
23 23 pub fn try_with_lock_no_wait<R>(
24 hg_vfs: Vfs,
24 hg_vfs: &VfsImpl,
25 25 lock_filename: &str,
26 26 f: impl FnOnce() -> R,
27 27 ) -> Result<R, LockError> {
@@ -57,7 +57,7 pub fn try_with_lock_no_wait<R>(
57 57 Err(LockError::AlreadyHeld)
58 58 }
59 59
60 fn break_lock(hg_vfs: Vfs, lock_filename: &str) -> Result<(), LockError> {
60 fn break_lock(hg_vfs: &VfsImpl, lock_filename: &str) -> Result<(), LockError> {
61 61 try_with_lock_no_wait(hg_vfs, &format!("{}.break", lock_filename), || {
62 62 // Check again in case some other process broke and
63 63 // acquired the lock in the meantime
@@ -71,7 +71,7 fn break_lock(hg_vfs: Vfs, lock_filename
71 71
72 72 #[cfg(unix)]
73 73 fn make_lock(
74 hg_vfs: Vfs,
74 hg_vfs: &VfsImpl,
75 75 lock_filename: &str,
76 76 data: &str,
77 77 ) -> Result<(), HgError> {
@@ -82,7 +82,7 fn make_lock(
82 82 }
83 83
84 84 fn read_lock(
85 hg_vfs: Vfs,
85 hg_vfs: &VfsImpl,
86 86 lock_filename: &str,
87 87 ) -> Result<Option<String>, HgError> {
88 88 let link_target =
@@ -98,7 +98,7 fn read_lock(
98 98 }
99 99 }
100 100
101 fn unlock(hg_vfs: Vfs, lock_filename: &str) -> Result<(), HgError> {
101 fn unlock(hg_vfs: &VfsImpl, lock_filename: &str) -> Result<(), HgError> {
102 102 hg_vfs.remove_file(lock_filename)
103 103 }
104 104
@@ -1,5 +1,5
1 1 use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
2 use crate::vfs::Vfs;
2 use crate::vfs::VfsImpl;
3 3 use std::io::Write;
4 4
5 5 /// An utility to append to a log file with the given name, and optionally
@@ -9,14 +9,14 use std::io::Write;
9 9 /// "example.log.1" to "example.log.2" etc up to the given maximum number of
10 10 /// files.
11 11 pub struct LogFile<'a> {
12 vfs: Vfs<'a>,
12 vfs: VfsImpl,
13 13 name: &'a str,
14 14 max_size: Option<u64>,
15 15 max_files: u32,
16 16 }
17 17
18 18 impl<'a> LogFile<'a> {
19 pub fn new(vfs: Vfs<'a>, name: &'a str) -> Self {
19 pub fn new(vfs: VfsImpl, name: &'a str) -> Self {
20 20 Self {
21 21 vfs,
22 22 name,
@@ -87,8 +87,12 impl<'a> LogFile<'a> {
87 87 #[test]
88 88 fn test_rotation() {
89 89 let temp = tempfile::tempdir().unwrap();
90 let vfs = Vfs { base: temp.path() };
91 let logger = LogFile::new(vfs, "log").max_size(Some(3)).max_files(2);
90 let vfs = VfsImpl {
91 base: temp.path().to_owned(),
92 };
93 let logger = LogFile::new(vfs.clone(), "log")
94 .max_size(Some(3))
95 .max_files(2);
92 96 logger.write(b"one\n").unwrap();
93 97 logger.write(b"two\n").unwrap();
94 98 logger.write(b"3\n").unwrap();
@@ -18,7 +18,7 use crate::utils::debug::debug_wait_for_
18 18 use crate::utils::files::get_path_from_bytes;
19 19 use crate::utils::hg_path::HgPath;
20 20 use crate::utils::SliceExt;
21 use crate::vfs::{is_dir, is_file, Vfs};
21 use crate::vfs::{is_dir, is_file, VfsImpl};
22 22 use crate::{
23 23 requirements, NodePrefix, RevlogDataConfig, RevlogDeltaConfig,
24 24 RevlogFeatureConfig, RevlogType, RevlogVersionOptions, UncheckedRevision,
@@ -121,8 +121,10 impl Repo {
121 121 let mut repo_config_files =
122 122 vec![dot_hg.join("hgrc"), dot_hg.join("hgrc-not-shared")];
123 123
124 let hg_vfs = Vfs { base: &dot_hg };
125 let mut reqs = requirements::load_if_exists(hg_vfs)?;
124 let hg_vfs = VfsImpl {
125 base: dot_hg.to_owned(),
126 };
127 let mut reqs = requirements::load_if_exists(&hg_vfs)?;
126 128 let relative =
127 129 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
128 130 let shared =
@@ -163,9 +165,10 impl Repo {
163 165
164 166 store_path = shared_path.join("store");
165 167
166 let source_is_share_safe =
167 requirements::load(Vfs { base: &shared_path })?
168 .contains(requirements::SHARESAFE_REQUIREMENT);
168 let source_is_share_safe = requirements::load(VfsImpl {
169 base: shared_path.to_owned(),
170 })?
171 .contains(requirements::SHARESAFE_REQUIREMENT);
169 172
170 173 if share_safe != source_is_share_safe {
171 174 return Err(HgError::unsupported("share-safe mismatch").into());
@@ -176,7 +179,9 impl Repo {
176 179 }
177 180 }
178 181 if share_safe {
179 reqs.extend(requirements::load(Vfs { base: &store_path })?);
182 reqs.extend(requirements::load(VfsImpl {
183 base: store_path.to_owned(),
184 })?);
180 185 }
181 186
182 187 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
@@ -216,19 +221,23 impl Repo {
216 221
217 222 /// For accessing repository files (in `.hg`), except for the store
218 223 /// (`.hg/store`).
219 pub fn hg_vfs(&self) -> Vfs<'_> {
220 Vfs { base: &self.dot_hg }
224 pub fn hg_vfs(&self) -> VfsImpl {
225 VfsImpl {
226 base: self.dot_hg.to_owned(),
227 }
221 228 }
222 229
223 230 /// For accessing repository store files (in `.hg/store`)
224 pub fn store_vfs(&self) -> Vfs<'_> {
225 Vfs { base: &self.store }
231 pub fn store_vfs(&self) -> VfsImpl {
232 VfsImpl {
233 base: self.store.to_owned(),
234 }
226 235 }
227 236
228 237 /// For accessing the working copy
229 pub fn working_directory_vfs(&self) -> Vfs<'_> {
230 Vfs {
231 base: &self.working_directory,
238 pub fn working_directory_vfs(&self) -> VfsImpl {
239 VfsImpl {
240 base: self.working_directory.to_owned(),
232 241 }
233 242 }
234 243
@@ -236,7 +245,7 impl Repo {
236 245 &self,
237 246 f: impl FnOnce() -> R,
238 247 ) -> Result<R, LockError> {
239 try_with_lock_no_wait(self.hg_vfs(), "wlock", f)
248 try_with_lock_no_wait(&self.hg_vfs(), "wlock", f)
240 249 }
241 250
242 251 /// Whether this repo should use dirstate-v2.
@@ -1,7 +1,7
1 1 use crate::errors::{HgError, HgResultExt};
2 2 use crate::repo::Repo;
3 3 use crate::utils::join_display;
4 use crate::vfs::Vfs;
4 use crate::vfs::VfsImpl;
5 5 use std::collections::HashSet;
6 6
7 7 fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> {
@@ -24,11 +24,13 fn parse(bytes: &[u8]) -> Result<HashSet
24 24 .collect()
25 25 }
26 26
27 pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
27 pub(crate) fn load(hg_vfs: VfsImpl) -> Result<HashSet<String>, HgError> {
28 28 parse(&hg_vfs.read("requires")?)
29 29 }
30 30
31 pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
31 pub(crate) fn load_if_exists(
32 hg_vfs: &VfsImpl,
33 ) -> Result<HashSet<String>, HgError> {
32 34 if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? {
33 35 parse(&bytes)
34 36 } else {
@@ -13,7 +13,7 use crate::revlog::Revision;
13 13 use crate::revlog::{Node, NodePrefix};
14 14 use crate::revlog::{Revlog, RevlogEntry, RevlogError};
15 15 use crate::utils::hg_path::HgPath;
16 use crate::vfs::Vfs;
16 use crate::vfs::VfsImpl;
17 17 use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision};
18 18
19 19 /// A specialized `Revlog` to work with changelog data format.
@@ -25,7 +25,7 pub struct Changelog {
25 25 impl Changelog {
26 26 /// Open the `changelog` of a repository given by its root.
27 27 pub fn open(
28 store_vfs: &Vfs,
28 store_vfs: &VfsImpl,
29 29 options: RevlogOpenOptions,
30 30 ) -> Result<Self, HgError> {
31 31 let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?;
@@ -500,7 +500,7 fn unescape_extra(bytes: &[u8]) -> Vec<u
500 500 #[cfg(test)]
501 501 mod tests {
502 502 use super::*;
503 use crate::vfs::Vfs;
503 use crate::vfs::VfsImpl;
504 504 use crate::{
505 505 RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig,
506 506 NULL_REVISION,
@@ -563,7 +563,9 message",
563 563 fn test_data_from_rev_null() -> Result<(), RevlogError> {
564 564 // an empty revlog will be enough for this case
565 565 let temp = tempfile::tempdir().unwrap();
566 let vfs = Vfs { base: temp.path() };
566 let vfs = VfsImpl {
567 base: temp.path().to_owned(),
568 };
567 569 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
568 570 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
569 571 let revlog = Revlog::open(
@@ -29,7 +29,7 impl Graph for Filelog {
29 29
30 30 impl Filelog {
31 31 pub fn open_vfs(
32 store_vfs: &crate::vfs::Vfs<'_>,
32 store_vfs: &crate::vfs::VfsImpl,
33 33 file_path: &HgPath,
34 34 options: RevlogOpenOptions,
35 35 ) -> Result<Self, HgError> {
@@ -3,7 +3,7 use crate::revlog::{Node, NodePrefix};
3 3 use crate::revlog::{Revlog, RevlogError};
4 4 use crate::utils::hg_path::HgPath;
5 5 use crate::utils::SliceExt;
6 use crate::vfs::Vfs;
6 use crate::vfs::VfsImpl;
7 7 use crate::{
8 8 Graph, GraphError, Revision, RevlogOpenOptions, UncheckedRevision,
9 9 };
@@ -23,7 +23,7 impl Graph for Manifestlog {
23 23 impl Manifestlog {
24 24 /// Open the `manifest` of a repository given by its root.
25 25 pub fn open(
26 store_vfs: &Vfs,
26 store_vfs: &VfsImpl,
27 27 options: RevlogOpenOptions,
28 28 ) -> Result<Self, HgError> {
29 29 let revlog = Revlog::open(store_vfs, "00manifest.i", None, options)?;
@@ -38,7 +38,7 use crate::exit_codes;
38 38 use crate::requirements::{
39 39 GENERALDELTA_REQUIREMENT, NARROW_REQUIREMENT, SPARSEREVLOG_REQUIREMENT,
40 40 };
41 use crate::vfs::Vfs;
41 use crate::vfs::VfsImpl;
42 42
43 43 /// As noted in revlog.c, revision numbers are actually encoded in
44 44 /// 4 bytes, and are liberally converted to ints, whence the i32
@@ -708,7 +708,8 impl Revlog {
708 708 /// It will also open the associated data file if index and data are not
709 709 /// interleaved.
710 710 pub fn open(
711 store_vfs: &Vfs,
711 // Todo use the `Vfs` trait here once we create a function for mmap
712 store_vfs: &VfsImpl,
712 713 index_path: impl AsRef<Path>,
713 714 data_path: Option<&Path>,
714 715 options: RevlogOpenOptions,
@@ -717,7 +718,8 impl Revlog {
717 718 }
718 719
719 720 fn open_gen(
720 store_vfs: &Vfs,
721 // Todo use the `Vfs` trait here once we create a function for mmap
722 store_vfs: &VfsImpl,
721 723 index_path: impl AsRef<Path>,
722 724 data_path: Option<&Path>,
723 725 options: RevlogOpenOptions,
@@ -1298,7 +1300,9 mod tests {
1298 1300 #[test]
1299 1301 fn test_empty() {
1300 1302 let temp = tempfile::tempdir().unwrap();
1301 let vfs = Vfs { base: temp.path() };
1303 let vfs = VfsImpl {
1304 base: temp.path().to_owned(),
1305 };
1302 1306 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
1303 1307 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
1304 1308 let revlog =
@@ -1320,7 +1324,9 mod tests {
1320 1324 #[test]
1321 1325 fn test_inline() {
1322 1326 let temp = tempfile::tempdir().unwrap();
1323 let vfs = Vfs { base: temp.path() };
1327 let vfs = VfsImpl {
1328 base: temp.path().to_owned(),
1329 };
1324 1330 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
1325 1331 .unwrap();
1326 1332 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
@@ -1387,7 +1393,9 mod tests {
1387 1393 #[test]
1388 1394 fn test_nodemap() {
1389 1395 let temp = tempfile::tempdir().unwrap();
1390 let vfs = Vfs { base: temp.path() };
1396 let vfs = VfsImpl {
1397 base: temp.path().to_owned(),
1398 };
1391 1399
1392 1400 // building a revlog with a forced Node starting with zeros
1393 1401 // This is a corruption, but it does not preclude using the nodemap
@@ -3,7 +3,7 use bytes_cast::{unaligned, BytesCast};
3 3 use memmap2::Mmap;
4 4 use std::path::{Path, PathBuf};
5 5
6 use crate::vfs::Vfs;
6 use crate::vfs::VfsImpl;
7 7
8 8 const ONDISK_VERSION: u8 = 1;
9 9
@@ -33,7 +33,7 impl NodeMapDocket {
33 33 /// * The docket file points to a missing (likely deleted) data file (this
34 34 /// can happen in a rare race condition).
35 35 pub fn read_from_file(
36 store_vfs: &Vfs,
36 store_vfs: &VfsImpl,
37 37 index_path: &Path,
38 38 ) -> Result<Option<(Self, Mmap)>, HgError> {
39 39 let docket_path = index_path.with_extension("n");
@@ -1,17 +1,21
1 1 use crate::errors::{HgError, IoErrorContext, IoResultExt};
2 use crate::exit_codes;
3 use dyn_clone::DynClone;
2 4 use memmap2::{Mmap, MmapOptions};
5 use std::fs::File;
3 6 use std::io::{ErrorKind, Write};
7 use std::os::unix::fs::MetadataExt;
4 8 use std::path::{Path, PathBuf};
5 9
6 10 /// Filesystem access abstraction for the contents of a given "base" diretory
7 #[derive(Clone, Copy)]
8 pub struct Vfs<'a> {
9 pub(crate) base: &'a Path,
11 #[derive(Clone)]
12 pub struct VfsImpl {
13 pub(crate) base: PathBuf,
10 14 }
11 15
12 16 struct FileNotFound(std::io::Error, PathBuf);
13 17
14 impl Vfs<'_> {
18 impl VfsImpl {
15 19 pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
16 20 self.base.join(relative_path)
17 21 }
@@ -71,7 +75,12 impl Vfs<'_> {
71 75 }
72 76 Ok(file) => file,
73 77 };
74 // TODO: what are the safety requirements here?
78 // Safety is "enforced" by locks and assuming other processes are
79 // well-behaved. If any misbehaving or malicious process does touch
80 // the index, it could lead to corruption. This is inherent
81 // to file-based `mmap`, though some platforms have some ways of
82 // mitigating.
83 // TODO linux: set the immutable flag with `chattr(1)`?
75 84 let mmap = unsafe { MmapOptions::new().map(&file) }
76 85 .when_reading_file(&path)?;
77 86 Ok(Ok(mmap))
@@ -134,8 +143,8 impl Vfs<'_> {
134 143 relative_path: impl AsRef<Path>,
135 144 contents: &[u8],
136 145 ) -> Result<(), HgError> {
137 let mut tmp = tempfile::NamedTempFile::new_in(self.base)
138 .when_writing_file(self.base)?;
146 let mut tmp = tempfile::NamedTempFile::new_in(&self.base)
147 .when_writing_file(&self.base)?;
139 148 tmp.write_all(contents)
140 149 .and_then(|()| tmp.flush())
141 150 .when_writing_file(tmp.path())?;
@@ -165,6 +174,174 fn fs_metadata(
165 174 }
166 175 }
167 176
177 /// Writable file object that atomically updates a file
178 ///
179 /// All writes will go to a temporary copy of the original file. Call
180 /// [`Self::close`] when you are done writing, and [`Self`] will rename
181 /// the temporary copy to the original name, making the changes
182 /// visible. If the object is destroyed without being closed, all your
183 /// writes are discarded.
184 pub struct AtomicFile {
185 /// The temporary file to write to
186 fp: std::fs::File,
187 /// Path of the temp file
188 temp_path: PathBuf,
189 /// Used when stat'ing the file, is useful only if the target file is
190 /// guarded by any lock (e.g. repo.lock or repo.wlock).
191 check_ambig: bool,
192 /// Path of the target file
193 target_name: PathBuf,
194 /// Whether the file is open or not
195 is_open: bool,
196 }
197
198 impl AtomicFile {
199 pub fn new(
200 fp: std::fs::File,
201 check_ambig: bool,
202 temp_name: PathBuf,
203 target_name: PathBuf,
204 ) -> Self {
205 Self {
206 fp,
207 check_ambig,
208 temp_path: temp_name,
209 target_name,
210 is_open: true,
211 }
212 }
213
214 /// Write `buf` to the temporary file
215 pub fn write_all(&mut self, buf: &[u8]) -> Result<(), std::io::Error> {
216 self.fp.write_all(buf)
217 }
218
219 fn target(&self) -> PathBuf {
220 self.temp_path
221 .parent()
222 .expect("should not be at the filesystem root")
223 .join(&self.target_name)
224 }
225
226 /// Close the temporary file and rename to the target
227 pub fn close(mut self) -> Result<(), std::io::Error> {
228 self.fp.flush()?;
229 let target = self.target();
230 if self.check_ambig {
231 if let Ok(stat) = std::fs::metadata(&target) {
232 std::fs::rename(&self.temp_path, &target)?;
233 let new_stat = std::fs::metadata(&target)?;
234 let ctime = new_stat.ctime();
235 let is_ambiguous = ctime == stat.ctime();
236 if is_ambiguous {
237 let advanced =
238 filetime::FileTime::from_unix_time(ctime + 1, 0);
239 filetime::set_file_times(target, advanced, advanced)?;
240 }
241 } else {
242 std::fs::rename(&self.temp_path, target)?;
243 }
244 } else {
245 std::fs::rename(&self.temp_path, target).unwrap();
246 }
247 self.is_open = false;
248 Ok(())
249 }
250 }
251
252 impl Drop for AtomicFile {
253 fn drop(&mut self) {
254 if self.is_open {
255 std::fs::remove_file(self.target()).ok();
256 }
257 }
258 }
259
260 /// Abstracts over the VFS to allow for different implementations of the
261 /// filesystem layer (like passing one from Python).
262 pub trait Vfs: Sync + Send + DynClone {
263 fn open(&self, filename: &Path) -> Result<std::fs::File, HgError>;
264 fn open_read(&self, filename: &Path) -> Result<std::fs::File, HgError>;
265 fn open_check_ambig(
266 &self,
267 filename: &Path,
268 ) -> Result<std::fs::File, HgError>;
269 fn create(&self, filename: &Path) -> Result<std::fs::File, HgError>;
270 /// Must truncate the new file if exist
271 fn create_atomic(
272 &self,
273 filename: &Path,
274 check_ambig: bool,
275 ) -> Result<AtomicFile, HgError>;
276 fn file_size(&self, file: &File) -> Result<u64, HgError>;
277 fn exists(&self, filename: &Path) -> bool;
278 fn unlink(&self, filename: &Path) -> Result<(), HgError>;
279 fn rename(
280 &self,
281 from: &Path,
282 to: &Path,
283 check_ambig: bool,
284 ) -> Result<(), HgError>;
285 fn copy(&self, from: &Path, to: &Path) -> Result<(), HgError>;
286 }
287
288 /// These methods will need to be implemented once `rhg` (and other) non-Python
289 /// users of `hg-core` start doing more on their own, like writing to files.
290 impl Vfs for VfsImpl {
291 fn open(&self, _filename: &Path) -> Result<std::fs::File, HgError> {
292 todo!()
293 }
294 fn open_read(&self, filename: &Path) -> Result<std::fs::File, HgError> {
295 let path = self.base.join(filename);
296 std::fs::File::open(&path).when_reading_file(&path)
297 }
298 fn open_check_ambig(
299 &self,
300 _filename: &Path,
301 ) -> Result<std::fs::File, HgError> {
302 todo!()
303 }
304 fn create(&self, _filename: &Path) -> Result<std::fs::File, HgError> {
305 todo!()
306 }
307 fn create_atomic(
308 &self,
309 _filename: &Path,
310 _check_ambig: bool,
311 ) -> Result<AtomicFile, HgError> {
312 todo!()
313 }
314 fn file_size(&self, file: &File) -> Result<u64, HgError> {
315 Ok(file
316 .metadata()
317 .map_err(|e| {
318 HgError::abort(
319 format!("Could not get file metadata: {}", e),
320 exit_codes::ABORT,
321 None,
322 )
323 })?
324 .size())
325 }
326 fn exists(&self, _filename: &Path) -> bool {
327 todo!()
328 }
329 fn unlink(&self, _filename: &Path) -> Result<(), HgError> {
330 todo!()
331 }
332 fn rename(
333 &self,
334 _from: &Path,
335 _to: &Path,
336 _check_ambig: bool,
337 ) -> Result<(), HgError> {
338 todo!()
339 }
340 fn copy(&self, _from: &Path, _to: &Path) -> Result<(), HgError> {
341 todo!()
342 }
343 }
344
168 345 pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
169 346 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
170 347 }
@@ -393,8 +393,8 pub fn run(invocation: &crate::CliInvoca
393 393 // + map_err + collect, so let's just inline some of the
394 394 // logic.
395 395 match unsure_is_modified(
396 working_directory_vfs,
397 store_vfs,
396 &working_directory_vfs,
397 &store_vfs,
398 398 check_exec,
399 399 &manifest,
400 400 &to_check.path,
@@ -748,8 +748,8 enum UnsureOutcome {
748 748 /// This meant to be used for those that the dirstate cannot resolve, due
749 749 /// to time resolution limits.
750 750 fn unsure_is_modified(
751 working_directory_vfs: hg::vfs::Vfs,
752 store_vfs: hg::vfs::Vfs,
751 working_directory_vfs: &hg::vfs::VfsImpl,
752 store_vfs: &hg::vfs::VfsImpl,
753 753 check_exec: bool,
754 754 manifest: &Manifest,
755 755 hg_path: &HgPath,
@@ -786,7 +786,7 fn unsure_is_modified(
786 786 return Ok(UnsureOutcome::Modified);
787 787 }
788 788 let filelog = hg::filelog::Filelog::open_vfs(
789 &store_vfs,
789 store_vfs,
790 790 hg_path,
791 791 revlog_open_options,
792 792 )?;
General Comments 0
You need to be logged in to leave comments. Login now