##// 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 "js-sys",
180 "js-sys",
181 "num-traits",
181 "num-traits",
182 "wasm-bindgen",
182 "wasm-bindgen",
183 "windows-targets 0.52.0",
183 "windows-targets 0.52.6",
184 ]
184 ]
185
185
186 [[package]]
186 [[package]]
@@ -435,10 +435,16 dependencies = [
435 "libc",
435 "libc",
436 "option-ext",
436 "option-ext",
437 "redox_users",
437 "redox_users",
438 "windows-sys",
438 "windows-sys 0.48.0",
439 ]
439 ]
440
440
441 [[package]]
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 name = "either"
448 name = "either"
443 version = "1.8.0"
449 version = "1.8.0"
444 source = "registry+https://github.com/rust-lang/crates.io-index"
450 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -467,6 +473,18 dependencies = [
467 ]
473 ]
468
474
469 [[package]]
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 name = "flate2"
488 name = "flate2"
471 version = "1.0.24"
489 version = "1.0.24"
472 source = "registry+https://github.com/rust-lang/crates.io-index"
490 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -584,6 +602,8 dependencies = [
584 "clap",
602 "clap",
585 "crossbeam-channel",
603 "crossbeam-channel",
586 "derive_more",
604 "derive_more",
605 "dyn-clone",
606 "filetime",
587 "flate2",
607 "flate2",
588 "format-bytes",
608 "format-bytes",
589 "hashbrown 0.13.1",
609 "hashbrown 0.13.1",
@@ -752,6 +772,7 checksum = "c0ff37bd590ca25063e35af745c3
752 dependencies = [
772 dependencies = [
753 "bitflags 2.6.0",
773 "bitflags 2.6.0",
754 "libc",
774 "libc",
775 "redox_syscall 0.5.3",
755 ]
776 ]
756
777
757 [[package]]
778 [[package]]
@@ -1123,6 +1144,15 dependencies = [
1123 ]
1144 ]
1124
1145
1125 [[package]]
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 name = "redox_users"
1156 name = "redox_users"
1127 version = "0.4.5"
1157 version = "0.4.5"
1128 source = "registry+https://github.com/rust-lang/crates.io-index"
1158 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1348,7 +1378,7 dependencies = [
1348 "cfg-if",
1378 "cfg-if",
1349 "fastrand",
1379 "fastrand",
1350 "libc",
1380 "libc",
1351 "redox_syscall",
1381 "redox_syscall 0.2.16",
1352 "remove_dir_all",
1382 "remove_dir_all",
1353 "winapi",
1383 "winapi",
1354 ]
1384 ]
@@ -1615,6 +1645,15 dependencies = [
1615 ]
1645 ]
1616
1646
1617 [[package]]
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 name = "windows-targets"
1657 name = "windows-targets"
1619 version = "0.48.5"
1658 version = "0.48.5"
1620 source = "registry+https://github.com/rust-lang/crates.io-index"
1659 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1631,17 +1670,18 dependencies = [
1631
1670
1632 [[package]]
1671 [[package]]
1633 name = "windows-targets"
1672 name = "windows-targets"
1634 version = "0.52.0"
1673 version = "0.52.6"
1635 source = "registry+https://github.com/rust-lang/crates.io-index"
1674 source = "registry+https://github.com/rust-lang/crates.io-index"
1636 checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
1675 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
1637 dependencies = [
1676 dependencies = [
1638 "windows_aarch64_gnullvm 0.52.0",
1677 "windows_aarch64_gnullvm 0.52.6",
1639 "windows_aarch64_msvc 0.52.0",
1678 "windows_aarch64_msvc 0.52.6",
1640 "windows_i686_gnu 0.52.0",
1679 "windows_i686_gnu 0.52.6",
1641 "windows_i686_msvc 0.52.0",
1680 "windows_i686_gnullvm",
1642 "windows_x86_64_gnu 0.52.0",
1681 "windows_i686_msvc 0.52.6",
1643 "windows_x86_64_gnullvm 0.52.0",
1682 "windows_x86_64_gnu 0.52.6",
1644 "windows_x86_64_msvc 0.52.0",
1683 "windows_x86_64_gnullvm 0.52.6",
1684 "windows_x86_64_msvc 0.52.6",
1645 ]
1685 ]
1646
1686
1647 [[package]]
1687 [[package]]
@@ -1652,9 +1692,9 checksum = "2b38e32f0abccf9987a4e3079dfb
1652
1692
1653 [[package]]
1693 [[package]]
1654 name = "windows_aarch64_gnullvm"
1694 name = "windows_aarch64_gnullvm"
1655 version = "0.52.0"
1695 version = "0.52.6"
1656 source = "registry+https://github.com/rust-lang/crates.io-index"
1696 source = "registry+https://github.com/rust-lang/crates.io-index"
1657 checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
1697 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1658
1698
1659 [[package]]
1699 [[package]]
1660 name = "windows_aarch64_msvc"
1700 name = "windows_aarch64_msvc"
@@ -1664,9 +1704,9 checksum = "dc35310971f3b2dbbf3f0690a219
1664
1704
1665 [[package]]
1705 [[package]]
1666 name = "windows_aarch64_msvc"
1706 name = "windows_aarch64_msvc"
1667 version = "0.52.0"
1707 version = "0.52.6"
1668 source = "registry+https://github.com/rust-lang/crates.io-index"
1708 source = "registry+https://github.com/rust-lang/crates.io-index"
1669 checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
1709 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1670
1710
1671 [[package]]
1711 [[package]]
1672 name = "windows_i686_gnu"
1712 name = "windows_i686_gnu"
@@ -1676,9 +1716,15 checksum = "a75915e7def60c94dcef72200b9a
1676
1716
1677 [[package]]
1717 [[package]]
1678 name = "windows_i686_gnu"
1718 name = "windows_i686_gnu"
1679 version = "0.52.0"
1719 version = "0.52.6"
1680 source = "registry+https://github.com/rust-lang/crates.io-index"
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 [[package]]
1729 [[package]]
1684 name = "windows_i686_msvc"
1730 name = "windows_i686_msvc"
@@ -1688,9 +1734,9 checksum = "8f55c233f70c4b27f66c523580f7
1688
1734
1689 [[package]]
1735 [[package]]
1690 name = "windows_i686_msvc"
1736 name = "windows_i686_msvc"
1691 version = "0.52.0"
1737 version = "0.52.6"
1692 source = "registry+https://github.com/rust-lang/crates.io-index"
1738 source = "registry+https://github.com/rust-lang/crates.io-index"
1693 checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
1739 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1694
1740
1695 [[package]]
1741 [[package]]
1696 name = "windows_x86_64_gnu"
1742 name = "windows_x86_64_gnu"
@@ -1700,9 +1746,9 checksum = "53d40abd2583d23e4718fddf1ebe
1700
1746
1701 [[package]]
1747 [[package]]
1702 name = "windows_x86_64_gnu"
1748 name = "windows_x86_64_gnu"
1703 version = "0.52.0"
1749 version = "0.52.6"
1704 source = "registry+https://github.com/rust-lang/crates.io-index"
1750 source = "registry+https://github.com/rust-lang/crates.io-index"
1705 checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
1751 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1706
1752
1707 [[package]]
1753 [[package]]
1708 name = "windows_x86_64_gnullvm"
1754 name = "windows_x86_64_gnullvm"
@@ -1712,9 +1758,9 checksum = "0b7b52767868a23d5bab768e390d
1712
1758
1713 [[package]]
1759 [[package]]
1714 name = "windows_x86_64_gnullvm"
1760 name = "windows_x86_64_gnullvm"
1715 version = "0.52.0"
1761 version = "0.52.6"
1716 source = "registry+https://github.com/rust-lang/crates.io-index"
1762 source = "registry+https://github.com/rust-lang/crates.io-index"
1717 checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
1763 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1718
1764
1719 [[package]]
1765 [[package]]
1720 name = "windows_x86_64_msvc"
1766 name = "windows_x86_64_msvc"
@@ -1724,9 +1770,9 checksum = "ed94fce61571a4006852b7389a06
1724
1770
1725 [[package]]
1771 [[package]]
1726 name = "windows_x86_64_msvc"
1772 name = "windows_x86_64_msvc"
1727 version = "0.52.0"
1773 version = "0.52.6"
1728 source = "registry+https://github.com/rust-lang/crates.io-index"
1774 source = "registry+https://github.com/rust-lang/crates.io-index"
1729 checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
1775 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1730
1776
1731 [[package]]
1777 [[package]]
1732 name = "wyz"
1778 name = "wyz"
@@ -41,6 +41,8 format-bytes = "0.3.0"
41 once_cell = "1.16.0"
41 once_cell = "1.16.0"
42 bitvec = "1.0.1"
42 bitvec = "1.0.1"
43 chrono = "0.4.34"
43 chrono = "0.4.34"
44 dyn-clone = "1.0.16"
45 filetime = "0.2.23"
44
46
45 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
47 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
46 # we have a clearer view of which backend is the fastest.
48 # we have a clearer view of which backend is the fastest.
@@ -2,7 +2,7
2
2
3 use crate::errors::HgError;
3 use crate::errors::HgError;
4 use crate::errors::HgResultExt;
4 use crate::errors::HgResultExt;
5 use crate::vfs::Vfs;
5 use crate::vfs::VfsImpl;
6 use std::io;
6 use std::io;
7 use std::io::ErrorKind;
7 use std::io::ErrorKind;
8
8
@@ -21,7 +21,7 pub enum LockError {
21 /// The return value of `f` is dropped in that case. If all is successful, the
21 /// The return value of `f` is dropped in that case. If all is successful, the
22 /// return value of `f` is forwarded.
22 /// return value of `f` is forwarded.
23 pub fn try_with_lock_no_wait<R>(
23 pub fn try_with_lock_no_wait<R>(
24 hg_vfs: Vfs,
24 hg_vfs: &VfsImpl,
25 lock_filename: &str,
25 lock_filename: &str,
26 f: impl FnOnce() -> R,
26 f: impl FnOnce() -> R,
27 ) -> Result<R, LockError> {
27 ) -> Result<R, LockError> {
@@ -57,7 +57,7 pub fn try_with_lock_no_wait<R>(
57 Err(LockError::AlreadyHeld)
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 try_with_lock_no_wait(hg_vfs, &format!("{}.break", lock_filename), || {
61 try_with_lock_no_wait(hg_vfs, &format!("{}.break", lock_filename), || {
62 // Check again in case some other process broke and
62 // Check again in case some other process broke and
63 // acquired the lock in the meantime
63 // acquired the lock in the meantime
@@ -71,7 +71,7 fn break_lock(hg_vfs: Vfs, lock_filename
71
71
72 #[cfg(unix)]
72 #[cfg(unix)]
73 fn make_lock(
73 fn make_lock(
74 hg_vfs: Vfs,
74 hg_vfs: &VfsImpl,
75 lock_filename: &str,
75 lock_filename: &str,
76 data: &str,
76 data: &str,
77 ) -> Result<(), HgError> {
77 ) -> Result<(), HgError> {
@@ -82,7 +82,7 fn make_lock(
82 }
82 }
83
83
84 fn read_lock(
84 fn read_lock(
85 hg_vfs: Vfs,
85 hg_vfs: &VfsImpl,
86 lock_filename: &str,
86 lock_filename: &str,
87 ) -> Result<Option<String>, HgError> {
87 ) -> Result<Option<String>, HgError> {
88 let link_target =
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 hg_vfs.remove_file(lock_filename)
102 hg_vfs.remove_file(lock_filename)
103 }
103 }
104
104
@@ -1,5 +1,5
1 use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
1 use crate::errors::{HgError, HgResultExt, IoErrorContext, IoResultExt};
2 use crate::vfs::Vfs;
2 use crate::vfs::VfsImpl;
3 use std::io::Write;
3 use std::io::Write;
4
4
5 /// An utility to append to a log file with the given name, and optionally
5 /// An utility to append to a log file with the given name, and optionally
@@ -9,14 +9,14 use std::io::Write;
9 /// "example.log.1" to "example.log.2" etc up to the given maximum number of
9 /// "example.log.1" to "example.log.2" etc up to the given maximum number of
10 /// files.
10 /// files.
11 pub struct LogFile<'a> {
11 pub struct LogFile<'a> {
12 vfs: Vfs<'a>,
12 vfs: VfsImpl,
13 name: &'a str,
13 name: &'a str,
14 max_size: Option<u64>,
14 max_size: Option<u64>,
15 max_files: u32,
15 max_files: u32,
16 }
16 }
17
17
18 impl<'a> LogFile<'a> {
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 Self {
20 Self {
21 vfs,
21 vfs,
22 name,
22 name,
@@ -87,8 +87,12 impl<'a> LogFile<'a> {
87 #[test]
87 #[test]
88 fn test_rotation() {
88 fn test_rotation() {
89 let temp = tempfile::tempdir().unwrap();
89 let temp = tempfile::tempdir().unwrap();
90 let vfs = Vfs { base: temp.path() };
90 let vfs = VfsImpl {
91 let logger = LogFile::new(vfs, "log").max_size(Some(3)).max_files(2);
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 logger.write(b"one\n").unwrap();
96 logger.write(b"one\n").unwrap();
93 logger.write(b"two\n").unwrap();
97 logger.write(b"two\n").unwrap();
94 logger.write(b"3\n").unwrap();
98 logger.write(b"3\n").unwrap();
@@ -18,7 +18,7 use crate::utils::debug::debug_wait_for_
18 use crate::utils::files::get_path_from_bytes;
18 use crate::utils::files::get_path_from_bytes;
19 use crate::utils::hg_path::HgPath;
19 use crate::utils::hg_path::HgPath;
20 use crate::utils::SliceExt;
20 use crate::utils::SliceExt;
21 use crate::vfs::{is_dir, is_file, Vfs};
21 use crate::vfs::{is_dir, is_file, VfsImpl};
22 use crate::{
22 use crate::{
23 requirements, NodePrefix, RevlogDataConfig, RevlogDeltaConfig,
23 requirements, NodePrefix, RevlogDataConfig, RevlogDeltaConfig,
24 RevlogFeatureConfig, RevlogType, RevlogVersionOptions, UncheckedRevision,
24 RevlogFeatureConfig, RevlogType, RevlogVersionOptions, UncheckedRevision,
@@ -121,8 +121,10 impl Repo {
121 let mut repo_config_files =
121 let mut repo_config_files =
122 vec![dot_hg.join("hgrc"), dot_hg.join("hgrc-not-shared")];
122 vec![dot_hg.join("hgrc"), dot_hg.join("hgrc-not-shared")];
123
123
124 let hg_vfs = Vfs { base: &dot_hg };
124 let hg_vfs = VfsImpl {
125 let mut reqs = requirements::load_if_exists(hg_vfs)?;
125 base: dot_hg.to_owned(),
126 };
127 let mut reqs = requirements::load_if_exists(&hg_vfs)?;
126 let relative =
128 let relative =
127 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
129 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
128 let shared =
130 let shared =
@@ -163,9 +165,10 impl Repo {
163
165
164 store_path = shared_path.join("store");
166 store_path = shared_path.join("store");
165
167
166 let source_is_share_safe =
168 let source_is_share_safe = requirements::load(VfsImpl {
167 requirements::load(Vfs { base: &shared_path })?
169 base: shared_path.to_owned(),
168 .contains(requirements::SHARESAFE_REQUIREMENT);
170 })?
171 .contains(requirements::SHARESAFE_REQUIREMENT);
169
172
170 if share_safe != source_is_share_safe {
173 if share_safe != source_is_share_safe {
171 return Err(HgError::unsupported("share-safe mismatch").into());
174 return Err(HgError::unsupported("share-safe mismatch").into());
@@ -176,7 +179,9 impl Repo {
176 }
179 }
177 }
180 }
178 if share_safe {
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 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
187 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
@@ -216,19 +221,23 impl Repo {
216
221
217 /// For accessing repository files (in `.hg`), except for the store
222 /// For accessing repository files (in `.hg`), except for the store
218 /// (`.hg/store`).
223 /// (`.hg/store`).
219 pub fn hg_vfs(&self) -> Vfs<'_> {
224 pub fn hg_vfs(&self) -> VfsImpl {
220 Vfs { base: &self.dot_hg }
225 VfsImpl {
226 base: self.dot_hg.to_owned(),
227 }
221 }
228 }
222
229
223 /// For accessing repository store files (in `.hg/store`)
230 /// For accessing repository store files (in `.hg/store`)
224 pub fn store_vfs(&self) -> Vfs<'_> {
231 pub fn store_vfs(&self) -> VfsImpl {
225 Vfs { base: &self.store }
232 VfsImpl {
233 base: self.store.to_owned(),
234 }
226 }
235 }
227
236
228 /// For accessing the working copy
237 /// For accessing the working copy
229 pub fn working_directory_vfs(&self) -> Vfs<'_> {
238 pub fn working_directory_vfs(&self) -> VfsImpl {
230 Vfs {
239 VfsImpl {
231 base: &self.working_directory,
240 base: self.working_directory.to_owned(),
232 }
241 }
233 }
242 }
234
243
@@ -236,7 +245,7 impl Repo {
236 &self,
245 &self,
237 f: impl FnOnce() -> R,
246 f: impl FnOnce() -> R,
238 ) -> Result<R, LockError> {
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 /// Whether this repo should use dirstate-v2.
251 /// Whether this repo should use dirstate-v2.
@@ -1,7 +1,7
1 use crate::errors::{HgError, HgResultExt};
1 use crate::errors::{HgError, HgResultExt};
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::utils::join_display;
3 use crate::utils::join_display;
4 use crate::vfs::Vfs;
4 use crate::vfs::VfsImpl;
5 use std::collections::HashSet;
5 use std::collections::HashSet;
6
6
7 fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> {
7 fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> {
@@ -24,11 +24,13 fn parse(bytes: &[u8]) -> Result<HashSet
24 .collect()
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 parse(&hg_vfs.read("requires")?)
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 if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? {
34 if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? {
33 parse(&bytes)
35 parse(&bytes)
34 } else {
36 } else {
@@ -13,7 +13,7 use crate::revlog::Revision;
13 use crate::revlog::{Node, NodePrefix};
13 use crate::revlog::{Node, NodePrefix};
14 use crate::revlog::{Revlog, RevlogEntry, RevlogError};
14 use crate::revlog::{Revlog, RevlogEntry, RevlogError};
15 use crate::utils::hg_path::HgPath;
15 use crate::utils::hg_path::HgPath;
16 use crate::vfs::Vfs;
16 use crate::vfs::VfsImpl;
17 use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision};
17 use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision};
18
18
19 /// A specialized `Revlog` to work with changelog data format.
19 /// A specialized `Revlog` to work with changelog data format.
@@ -25,7 +25,7 pub struct Changelog {
25 impl Changelog {
25 impl Changelog {
26 /// Open the `changelog` of a repository given by its root.
26 /// Open the `changelog` of a repository given by its root.
27 pub fn open(
27 pub fn open(
28 store_vfs: &Vfs,
28 store_vfs: &VfsImpl,
29 options: RevlogOpenOptions,
29 options: RevlogOpenOptions,
30 ) -> Result<Self, HgError> {
30 ) -> Result<Self, HgError> {
31 let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?;
31 let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?;
@@ -500,7 +500,7 fn unescape_extra(bytes: &[u8]) -> Vec<u
500 #[cfg(test)]
500 #[cfg(test)]
501 mod tests {
501 mod tests {
502 use super::*;
502 use super::*;
503 use crate::vfs::Vfs;
503 use crate::vfs::VfsImpl;
504 use crate::{
504 use crate::{
505 RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig,
505 RevlogDataConfig, RevlogDeltaConfig, RevlogFeatureConfig,
506 NULL_REVISION,
506 NULL_REVISION,
@@ -563,7 +563,9 message",
563 fn test_data_from_rev_null() -> Result<(), RevlogError> {
563 fn test_data_from_rev_null() -> Result<(), RevlogError> {
564 // an empty revlog will be enough for this case
564 // an empty revlog will be enough for this case
565 let temp = tempfile::tempdir().unwrap();
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 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
569 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
568 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
570 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
569 let revlog = Revlog::open(
571 let revlog = Revlog::open(
@@ -29,7 +29,7 impl Graph for Filelog {
29
29
30 impl Filelog {
30 impl Filelog {
31 pub fn open_vfs(
31 pub fn open_vfs(
32 store_vfs: &crate::vfs::Vfs<'_>,
32 store_vfs: &crate::vfs::VfsImpl,
33 file_path: &HgPath,
33 file_path: &HgPath,
34 options: RevlogOpenOptions,
34 options: RevlogOpenOptions,
35 ) -> Result<Self, HgError> {
35 ) -> Result<Self, HgError> {
@@ -3,7 +3,7 use crate::revlog::{Node, NodePrefix};
3 use crate::revlog::{Revlog, RevlogError};
3 use crate::revlog::{Revlog, RevlogError};
4 use crate::utils::hg_path::HgPath;
4 use crate::utils::hg_path::HgPath;
5 use crate::utils::SliceExt;
5 use crate::utils::SliceExt;
6 use crate::vfs::Vfs;
6 use crate::vfs::VfsImpl;
7 use crate::{
7 use crate::{
8 Graph, GraphError, Revision, RevlogOpenOptions, UncheckedRevision,
8 Graph, GraphError, Revision, RevlogOpenOptions, UncheckedRevision,
9 };
9 };
@@ -23,7 +23,7 impl Graph for Manifestlog {
23 impl Manifestlog {
23 impl Manifestlog {
24 /// Open the `manifest` of a repository given by its root.
24 /// Open the `manifest` of a repository given by its root.
25 pub fn open(
25 pub fn open(
26 store_vfs: &Vfs,
26 store_vfs: &VfsImpl,
27 options: RevlogOpenOptions,
27 options: RevlogOpenOptions,
28 ) -> Result<Self, HgError> {
28 ) -> Result<Self, HgError> {
29 let revlog = Revlog::open(store_vfs, "00manifest.i", None, options)?;
29 let revlog = Revlog::open(store_vfs, "00manifest.i", None, options)?;
@@ -38,7 +38,7 use crate::exit_codes;
38 use crate::requirements::{
38 use crate::requirements::{
39 GENERALDELTA_REQUIREMENT, NARROW_REQUIREMENT, SPARSEREVLOG_REQUIREMENT,
39 GENERALDELTA_REQUIREMENT, NARROW_REQUIREMENT, SPARSEREVLOG_REQUIREMENT,
40 };
40 };
41 use crate::vfs::Vfs;
41 use crate::vfs::VfsImpl;
42
42
43 /// As noted in revlog.c, revision numbers are actually encoded in
43 /// As noted in revlog.c, revision numbers are actually encoded in
44 /// 4 bytes, and are liberally converted to ints, whence the i32
44 /// 4 bytes, and are liberally converted to ints, whence the i32
@@ -708,7 +708,8 impl Revlog {
708 /// It will also open the associated data file if index and data are not
708 /// It will also open the associated data file if index and data are not
709 /// interleaved.
709 /// interleaved.
710 pub fn open(
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 index_path: impl AsRef<Path>,
713 index_path: impl AsRef<Path>,
713 data_path: Option<&Path>,
714 data_path: Option<&Path>,
714 options: RevlogOpenOptions,
715 options: RevlogOpenOptions,
@@ -717,7 +718,8 impl Revlog {
717 }
718 }
718
719
719 fn open_gen(
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 index_path: impl AsRef<Path>,
723 index_path: impl AsRef<Path>,
722 data_path: Option<&Path>,
724 data_path: Option<&Path>,
723 options: RevlogOpenOptions,
725 options: RevlogOpenOptions,
@@ -1298,7 +1300,9 mod tests {
1298 #[test]
1300 #[test]
1299 fn test_empty() {
1301 fn test_empty() {
1300 let temp = tempfile::tempdir().unwrap();
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 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
1306 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
1303 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
1307 std::fs::write(temp.path().join("foo.d"), b"").unwrap();
1304 let revlog =
1308 let revlog =
@@ -1320,7 +1324,9 mod tests {
1320 #[test]
1324 #[test]
1321 fn test_inline() {
1325 fn test_inline() {
1322 let temp = tempfile::tempdir().unwrap();
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 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
1330 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
1325 .unwrap();
1331 .unwrap();
1326 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
1332 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
@@ -1387,7 +1393,9 mod tests {
1387 #[test]
1393 #[test]
1388 fn test_nodemap() {
1394 fn test_nodemap() {
1389 let temp = tempfile::tempdir().unwrap();
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 // building a revlog with a forced Node starting with zeros
1400 // building a revlog with a forced Node starting with zeros
1393 // This is a corruption, but it does not preclude using the nodemap
1401 // This is a corruption, but it does not preclude using the nodemap
@@ -3,7 +3,7 use bytes_cast::{unaligned, BytesCast};
3 use memmap2::Mmap;
3 use memmap2::Mmap;
4 use std::path::{Path, PathBuf};
4 use std::path::{Path, PathBuf};
5
5
6 use crate::vfs::Vfs;
6 use crate::vfs::VfsImpl;
7
7
8 const ONDISK_VERSION: u8 = 1;
8 const ONDISK_VERSION: u8 = 1;
9
9
@@ -33,7 +33,7 impl NodeMapDocket {
33 /// * The docket file points to a missing (likely deleted) data file (this
33 /// * The docket file points to a missing (likely deleted) data file (this
34 /// can happen in a rare race condition).
34 /// can happen in a rare race condition).
35 pub fn read_from_file(
35 pub fn read_from_file(
36 store_vfs: &Vfs,
36 store_vfs: &VfsImpl,
37 index_path: &Path,
37 index_path: &Path,
38 ) -> Result<Option<(Self, Mmap)>, HgError> {
38 ) -> Result<Option<(Self, Mmap)>, HgError> {
39 let docket_path = index_path.with_extension("n");
39 let docket_path = index_path.with_extension("n");
@@ -1,17 +1,21
1 use crate::errors::{HgError, IoErrorContext, IoResultExt};
1 use crate::errors::{HgError, IoErrorContext, IoResultExt};
2 use crate::exit_codes;
3 use dyn_clone::DynClone;
2 use memmap2::{Mmap, MmapOptions};
4 use memmap2::{Mmap, MmapOptions};
5 use std::fs::File;
3 use std::io::{ErrorKind, Write};
6 use std::io::{ErrorKind, Write};
7 use std::os::unix::fs::MetadataExt;
4 use std::path::{Path, PathBuf};
8 use std::path::{Path, PathBuf};
5
9
6 /// Filesystem access abstraction for the contents of a given "base" diretory
10 /// Filesystem access abstraction for the contents of a given "base" diretory
7 #[derive(Clone, Copy)]
11 #[derive(Clone)]
8 pub struct Vfs<'a> {
12 pub struct VfsImpl {
9 pub(crate) base: &'a Path,
13 pub(crate) base: PathBuf,
10 }
14 }
11
15
12 struct FileNotFound(std::io::Error, PathBuf);
16 struct FileNotFound(std::io::Error, PathBuf);
13
17
14 impl Vfs<'_> {
18 impl VfsImpl {
15 pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
19 pub fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
16 self.base.join(relative_path)
20 self.base.join(relative_path)
17 }
21 }
@@ -71,7 +75,12 impl Vfs<'_> {
71 }
75 }
72 Ok(file) => file,
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 let mmap = unsafe { MmapOptions::new().map(&file) }
84 let mmap = unsafe { MmapOptions::new().map(&file) }
76 .when_reading_file(&path)?;
85 .when_reading_file(&path)?;
77 Ok(Ok(mmap))
86 Ok(Ok(mmap))
@@ -134,8 +143,8 impl Vfs<'_> {
134 relative_path: impl AsRef<Path>,
143 relative_path: impl AsRef<Path>,
135 contents: &[u8],
144 contents: &[u8],
136 ) -> Result<(), HgError> {
145 ) -> Result<(), HgError> {
137 let mut tmp = tempfile::NamedTempFile::new_in(self.base)
146 let mut tmp = tempfile::NamedTempFile::new_in(&self.base)
138 .when_writing_file(self.base)?;
147 .when_writing_file(&self.base)?;
139 tmp.write_all(contents)
148 tmp.write_all(contents)
140 .and_then(|()| tmp.flush())
149 .and_then(|()| tmp.flush())
141 .when_writing_file(tmp.path())?;
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 pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
345 pub(crate) fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
169 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
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 // + map_err + collect, so let's just inline some of the
393 // + map_err + collect, so let's just inline some of the
394 // logic.
394 // logic.
395 match unsure_is_modified(
395 match unsure_is_modified(
396 working_directory_vfs,
396 &working_directory_vfs,
397 store_vfs,
397 &store_vfs,
398 check_exec,
398 check_exec,
399 &manifest,
399 &manifest,
400 &to_check.path,
400 &to_check.path,
@@ -748,8 +748,8 enum UnsureOutcome {
748 /// This meant to be used for those that the dirstate cannot resolve, due
748 /// This meant to be used for those that the dirstate cannot resolve, due
749 /// to time resolution limits.
749 /// to time resolution limits.
750 fn unsure_is_modified(
750 fn unsure_is_modified(
751 working_directory_vfs: hg::vfs::Vfs,
751 working_directory_vfs: &hg::vfs::VfsImpl,
752 store_vfs: hg::vfs::Vfs,
752 store_vfs: &hg::vfs::VfsImpl,
753 check_exec: bool,
753 check_exec: bool,
754 manifest: &Manifest,
754 manifest: &Manifest,
755 hg_path: &HgPath,
755 hg_path: &HgPath,
@@ -786,7 +786,7 fn unsure_is_modified(
786 return Ok(UnsureOutcome::Modified);
786 return Ok(UnsureOutcome::Modified);
787 }
787 }
788 let filelog = hg::filelog::Filelog::open_vfs(
788 let filelog = hg::filelog::Filelog::open_vfs(
789 &store_vfs,
789 store_vfs,
790 hg_path,
790 hg_path,
791 revlog_open_options,
791 revlog_open_options,
792 )?;
792 )?;
General Comments 0
You need to be logged in to leave comments. Login now