Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing a read-only projection of the conserve archive for Windows #240

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8dac50d
Initial implementation of archive mounting on Windows
WolverinDEV Dec 29, 2023
58f84a5
Adding a hunk metadata cache to speed up index entry querying
WolverinDEV Dec 30, 2023
0b7e53a
Caching the opened bands as well
WolverinDEV Dec 30, 2023
e88514b
Supporting file timestamps, mark files read only and improved project…
WolverinDEV Dec 30, 2023
16a37aa
Enabling the mount command by default for all Windows platforms
WolverinDEV Dec 30, 2023
6b7d7b6
Improving cache to speed up directory and file serving
WolverinDEV Dec 30, 2023
eb1e814
Indicating backup timestamp trough directory modify timestamps
WolverinDEV Dec 30, 2023
76535be
Bumped windows-projfs version to v0.1.6
WolverinDEV Dec 30, 2023
bc6cd1b
Mount implementation polishing
WolverinDEV Dec 31, 2023
e2d2f07
Moving unix specific test imports into the function itself to avoid u…
WolverinDEV Dec 31, 2023
eddd369
Bumped the minimal required rust version to 1.75
WolverinDEV Dec 31, 2023
1fd937b
Incorporate PR feedback
WolverinDEV Jan 1, 2024
195974a
Merge remote-tracking branch 'original/main' into windows-mount
WolverinDEV Jan 1, 2024
bc5e81f
Merged with upstream
WolverinDEV Aug 1, 2024
3266178
Adding tests for the mount archive feature
WolverinDEV Aug 1, 2024
4b48c8c
Using Rust 1.76 as required by aws-sdk-sso
WolverinDEV Aug 1, 2024
57942e3
Disable tests for mount on non windows systems
WolverinDEV Aug 1, 2024
1918fa5
Seperating mount tests from normal tests any reduce the number of tes…
WolverinDEV Aug 10, 2024
ef27f3a
Skip some files from mutants
sourcefrog Oct 20, 2024
8bee9c1
Merge remote-tracking branch 'origin/main' into windows-mount
sourcefrog Oct 21, 2024
4a13602
fix: New Transport API
sourcefrog Oct 21, 2024
941a82c
fix: Better error messages
sourcefrog Oct 21, 2024
985ce15
Merge branch 'main' into pr/WolverinDEV/240
sourcefrog Oct 23, 2024
fb09fa0
Separate out no-op Unix mount
sourcefrog Oct 23, 2024
81ddb74
Merge branch 'main' into pr/WolverinDEV/240
sourcefrog Oct 23, 2024
8863d09
rustfmt
sourcefrog Oct 23, 2024
85d495c
clippy: some things only needed on Windows
sourcefrog Oct 23, 2024
f66d6b0
Only build hunk_index on Windows
sourcefrog Oct 23, 2024
210d790
Suppress unused warnings on Unix for code only used on Windows
sourcefrog Oct 23, 2024
c6a0e91
Rename option to --cleanup-projfs and add help
sourcefrog Oct 23, 2024
987edf3
merge main
sourcefrog Jan 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ exclude_globs = [
"backup.rs", # almost well tested but some gaps
"metric_recorder.rs",
"progress.rs",
"src/hunk_index.rs", # not well tested yet?
"src/mount/projfs.rs", # mostly for Windows
"src/owner/windows.rs",
"src/progress/term.rs",
"src/transport.rs", # almost well tested but some gaps
"src/transport/s3.rs",
"src/ui/termui.rs",
"src/owner/windows.rs",
"stats.rs",
]
18 changes: 13 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,19 @@ jobs:
cargo --version
rustc --version
- name: Build
run: >
cargo build --all-targets --features fail/failpoints
- name: Test
run: >
cargo test --features fail/failpoints -- --include-ignored
run: cargo build --all-targets --features fail/failpoints
- name: Test (without mount)
run:
cargo test --features fail/failpoints -- --skip mount
--include-ignored
- name: Test (mount)
run:
cargo test --features fail/failpoints --test mount --
--include-ignored
env:
# Running multiple instances in parallel might cause a crash on low-end environments
# when executing the mounting tests on Windows due to projfs.
RUST_TEST_THREADS: 1

# Run rustfmt separately so that it does not block test results
rustfmt:
Expand Down
45 changes: 44 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "GPL-2.0"
name = "conserve"
readme = "README.md"
repository = "https://github.com/sourcefrog/conserve/"
version = "24.8.0"
version = "23.11.0"
rust-version = "1.79"

[features]
Expand Down Expand Up @@ -81,6 +81,9 @@ whoami = "1.5.2"
uzers = "0.11"
nix = { version = "0.28", features = ["fs", "user"] }

[target.'cfg(windows)'.dependencies]
windows-projfs = { version = "0.1.6", features = ["dynamic-import"] }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this pull in a lot of stuff? Maybe it should be optional even on Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "pull in a lot of stuff"?
Do you mean memory wise, dependency wise, file wise, etc?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant mostly a lot of new dependencies and therefore build time; hopefully it's not having any runtime effect unless it's used. Perhaps it's fine by default.


[dependencies.clap]
version = "4.3"
features = ["derive", "deprecated", "wrap_help"]
Expand Down
70 changes: 68 additions & 2 deletions src/bin/conserve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::io::{self, BufWriter, Write};
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::Instant;
Expand Down Expand Up @@ -180,6 +180,36 @@ enum Command {
long_listing: bool,
},

/// Mount the archive as a filesystem.
///
/// Files and directories from all previous backups are visible.
///
/// This is currently only supported on Windows.
///
/// On Windows you must first enable the Projected Filesystem feature by running this command
/// in an elevated PowerShell:
///
/// Enable-WindowsOptionalFeature -Online -FeatureName Client-ProjFS -NoRestart
///
/// ProjFS by default retains extracted files in the destination directory. This can make
/// access to the archive faster on subsequent mounts, but will use more disk space.
///
/// If `--cleanup-projfs` is set, then the directory will be deleted when the projection is stopped.
/// Also, if this option is set, the destination directory must not exist.
#[cfg(windows)]
Mount {
/// The archive to mount
archive: String,

/// Target folder where the archive should be mounted to
destination: PathBuf,

/// Create the target folder and remove all temporarily created
/// files on exit
sourcefrog marked this conversation as resolved.
Show resolved Hide resolved
#[arg(long)]
cleanup_projfs: bool,
},

/// Copy a stored tree to a restore directory.
Restore {
archive: String,
Expand Down Expand Up @@ -306,7 +336,7 @@ impl std::process::Termination for ExitCode {

impl Command {
fn run(&self, monitor: Arc<TermUiMonitor>) -> Result<ExitCode> {
let mut stdout = std::io::stdout();
let mut stdout = io::stdout();
match self {
Command::Backup {
archive,
Expand Down Expand Up @@ -468,6 +498,42 @@ impl Command {
show::show_entry_names(entry_iter, &mut stdout, *long_listing)?;
}
}
#[cfg(windows)]
Command::Mount {
archive,
destination,
cleanup_projfs: cleanup,
} => {
use std::io::Read;

let archive = Archive::open(Transport::new(archive)?)?;
let options = MountOptions { clean: *cleanup };
let projection = match mount(archive, destination, options) {
Ok(handle) => handle,
Err(Error::MountDestinationExists) => {
error!("Mount point {} already exists", destination.display());
return Ok(ExitCode::Failure);
}
Err(Error::MountDestinationDoesNotExists) => {
error!("Mount destination {} does not exist", destination.display());
return Ok(ExitCode::Failure);
}
Err(error) => return Err(error),
};

info!(
"Projection started at {}.",
projection.mount_root().display()
);
{
info!("Press any key to stop the projection...");
let mut stdin = io::stdin();
let _ = stdin.read(&mut [0u8]).unwrap();
}

info!("Stopping projection.");
drop(projection);
}
Command::Restore {
archive,
destination,
Expand Down
16 changes: 16 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ pub enum Error {
#[error("Unexpected file {path:?} in archive directory")]
UnexpectedFile { path: String },

#[error("This feature is not implemented")]
NotImplemented,

#[error("The destination already exists")]
MountDestinationExists,

#[error("The destination does not exists")]
MountDestinationDoesNotExists,

/// Generic IO error.
#[error(transparent)]
IOError {
Expand All @@ -189,6 +198,13 @@ pub enum Error {
#[from]
source: transport::Error,
},

#[cfg(windows)]
#[error(transparent)]
Projection {
#[from]
source: windows_projfs::Error,
},
}

impl From<jsonio::Error> for Error {
Expand Down
Loading
Loading