Skip to content

Commit 3266178

Browse files
committed
Adding tests for the mount archive feature
1 parent bc5e81f commit 3266178

File tree

5 files changed

+356
-42
lines changed

5 files changed

+356
-42
lines changed

src/bin/conserve.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
1616
use std::cell::RefCell;
1717
use std::fs::{File, OpenOptions};
18-
use std::io::{BufWriter, Write};
18+
use std::io::{self, BufWriter, Read, Write};
1919
use std::path::{Path, PathBuf};
2020
use std::sync::{Arc, RwLock};
2121
use std::time::Instant;
@@ -188,7 +188,7 @@ enum Command {
188188
/// Target folder where the archive should be mounted to
189189
destination: PathBuf,
190190

191-
/// Create the target folder and remove all temporarly created
191+
/// Create the target folder and remove all temporarily created
192192
/// files on exit
193193
#[arg(long, default_value_t = true)]
194194
cleanup: bool,
@@ -490,7 +490,33 @@ impl Command {
490490
} => {
491491
let archive = Archive::open(open_transport(archive)?)?;
492492
let options = MountOptions { clean: *cleanup };
493-
mount(archive, destination, options)?;
493+
let projection = match mount(archive, destination, options) {
494+
Ok(handle) => handle,
495+
Err(Error::MountDestinationExists) => {
496+
error!("The destination already exists.");
497+
error!("Please ensure, that the destination does not exists.");
498+
return Ok(ExitCode::Failure);
499+
}
500+
Err(Error::MountDestinationDoesNotExists) => {
501+
error!("The destination does not exists.");
502+
error!("Please ensure, that the destination does exist prior mounting.");
503+
return Ok(ExitCode::Failure);
504+
}
505+
Err(error) => return Err(error),
506+
};
507+
508+
info!(
509+
"Projection started at {}.",
510+
projection.mount_root().display()
511+
);
512+
{
513+
info!("Press any key to stop the projection...");
514+
let mut stdin = io::stdin();
515+
let _ = stdin.read(&mut [0u8]).unwrap();
516+
}
517+
518+
info!("Stopping projection.");
519+
drop(projection);
494520
}
495521
Command::Restore {
496522
archive,

src/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ pub enum Error {
170170
#[error("This feature is not implemented")]
171171
NotImplemented,
172172

173+
#[error("The destination already exists")]
174+
MountDestinationExists,
175+
176+
#[error("The destination does not exists")]
177+
MountDestinationDoesNotExists,
178+
173179
/// Generic IO error.
174180
#[error(transparent)]
175181
IOError {

src/mount/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,18 @@ pub struct MountOptions {
1313
pub clean: bool,
1414
}
1515

16-
pub fn mount(archive: Archive, destination: &Path, options: MountOptions) -> Result<()> {
16+
/// Handle for the mount controller.
17+
/// Once dropped, the projection will be stopped and if specified so by MountOptions cleaned.
18+
pub trait MountHandle {
19+
/// Returns the root path where the archive has been mounted.
20+
fn mount_root(&self) -> &Path;
21+
}
22+
23+
pub fn mount(
24+
archive: Archive,
25+
destination: &Path,
26+
options: MountOptions,
27+
) -> Result<Box<dyn MountHandle>> {
1728
#[cfg(windows)]
1829
return projfs::mount(archive, destination, options);
1930

src/mount/projfs.rs

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{
1414
use bytes::Bytes;
1515
use itertools::Itertools;
1616
use lru::LruCache;
17-
use tracing::{debug, error, info, warn};
17+
use tracing::{debug, info, warn};
1818
use windows_projfs::{
1919
DirectoryEntry, DirectoryInfo, FileInfo, Notification, ProjectedFileSystem,
2020
ProjectedFileSystemSource,
@@ -23,10 +23,10 @@ use windows_projfs::{
2323
use crate::{
2424
hunk_index::IndexHunkIndex,
2525
monitor::{void::VoidMonitor, Monitor},
26-
Apath, Archive, BandId, BandSelectionPolicy, IndexEntry, Kind, Result, StoredTree,
26+
Apath, Archive, BandId, BandSelectionPolicy, Error, IndexEntry, Kind, Result, StoredTree,
2727
};
2828

29-
use super::MountOptions;
29+
use super::{MountHandle, MountOptions};
3030

3131
struct StoredFileReader {
3232
iter: Peekable<Box<dyn Iterator<Item = Result<Bytes>>>>,
@@ -161,7 +161,7 @@ struct ArchiveProjectionSource {
161161
hunk_index_cache: Mutex<LruCache<BandId, Arc<IndexHunkIndex>>>,
162162

163163
/*
164-
* Cache the last accessed hunks to improve directory travesal speed.
164+
* Cache the last accessed hunks to improve directory traversal speed.
165165
*/
166166
#[allow(clippy::type_complexity)]
167167
hunk_content_cache: Mutex<LruCache<(BandId, u32), Arc<Vec<IndexEntry>>>>,
@@ -216,7 +216,7 @@ impl ArchiveProjectionSource {
216216
.lock()
217217
.unwrap()
218218
.try_get_or_insert(band_id, || {
219-
/* Inform the user that this band has been cached as this is most likely a heavy operaton (cpu and memory wise) */
219+
/* Inform the user that this band has been cached as this is most likely a heavy operation (cpu and memory wise) */
220220
info!("Caching files for band {}", stored_tree.band().id());
221221

222222
let helper = IndexHunkIndex::from_index(&stored_tree.band().index())?;
@@ -450,7 +450,7 @@ impl ProjectedFileSystemSource for ArchiveProjectionSource {
450450
if notification.is_cancelable()
451451
&& !matches!(notification, Notification::FilePreConvertToFull(_))
452452
{
453-
/* try to cancel everything, except retriving data */
453+
/* try to cancel everything, except retrieving data */
454454
ControlFlow::Break(())
455455
} else {
456456
ControlFlow::Continue(())
@@ -459,19 +459,51 @@ impl ProjectedFileSystemSource for ArchiveProjectionSource {
459459
}
460460

461461
const ERROR_CODE_VIRTUALIZATION_TEMPORARILY_UNAVAILABLE: i32 = 369;
462-
pub fn mount(archive: Archive, destination: &Path, options: MountOptions) -> Result<()> {
462+
struct WindowsMountHandle {
463+
_projection: ProjectedFileSystem,
464+
path: PathBuf,
465+
cleanup: bool,
466+
}
467+
468+
impl Drop for WindowsMountHandle {
469+
fn drop(&mut self) {
470+
if self.cleanup {
471+
debug!("Removing destination {}", self.path.display());
472+
let mut attempt_count = 0;
473+
while let Err(err) = fs::remove_dir_all(&self.path) {
474+
attempt_count += 1;
475+
if err.raw_os_error().unwrap_or_default()
476+
!= ERROR_CODE_VIRTUALIZATION_TEMPORARILY_UNAVAILABLE
477+
|| attempt_count > 5
478+
{
479+
warn!("Failed to clean up projection destination: {}", err);
480+
break;
481+
}
482+
std::thread::sleep(Duration::from_secs(1));
483+
}
484+
}
485+
}
486+
}
487+
488+
impl MountHandle for WindowsMountHandle {
489+
fn mount_root(&self) -> &Path {
490+
&self.path
491+
}
492+
}
493+
494+
pub fn mount(
495+
archive: Archive,
496+
destination: &Path,
497+
options: MountOptions,
498+
) -> Result<Box<dyn MountHandle>> {
463499
if options.clean {
464500
if destination.exists() {
465-
error!("The destination already exists.");
466-
error!("Please ensure, that the destination does not exists.");
467-
return Ok(());
501+
return Err(Error::MountDestinationExists);
468502
}
469503

470504
fs::create_dir_all(destination)?;
471505
} else if !destination.exists() {
472-
error!("The destination does not exists.");
473-
error!("Please ensure, that the destination does exist prior mounting.");
474-
return Ok(());
506+
return Err(Error::MountDestinationDoesNotExists);
475507
}
476508

477509
let source = ArchiveProjectionSource {
@@ -486,31 +518,12 @@ pub fn mount(archive: Archive, destination: &Path, options: MountOptions) -> Res
486518
};
487519

488520
let projection = ProjectedFileSystem::new(destination, source)?;
489-
info!("Projection started at {}.", destination.display());
490-
{
491-
info!("Press any key to stop the projection...");
492-
let mut stdin = io::stdin();
493-
let _ = stdin.read(&mut [0u8]).unwrap();
494-
}
495-
496-
info!("Stopping projection.");
497-
drop(projection);
521+
let handle: Box<dyn MountHandle> = Box::new(WindowsMountHandle {
522+
_projection: projection,
498523

499-
if options.clean {
500-
debug!("Removing destination {}", destination.display());
501-
let mut attempt_count = 0;
502-
while let Err(err) = fs::remove_dir_all(destination) {
503-
attempt_count += 1;
504-
if err.raw_os_error().unwrap_or_default()
505-
!= ERROR_CODE_VIRTUALIZATION_TEMPORARILY_UNAVAILABLE
506-
|| attempt_count > 5
507-
{
508-
warn!("Failed to clean up projection destination: {}", err);
509-
break;
510-
}
511-
std::thread::sleep(Duration::from_secs(1));
512-
}
513-
}
524+
path: destination.to_owned(),
525+
cleanup: options.clean,
526+
});
514527

515-
Ok(())
528+
Ok(handle)
516529
}

0 commit comments

Comments
 (0)