Skip to content

Commit

Permalink
feat(links): add support for hard linking from the cache
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Mar 5, 2023
1 parent 80e5b41 commit e2695a9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 1 deletion.
3 changes: 2 additions & 1 deletion benches/benchmarks.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#[cfg(feature = "async-std")]
use async_std::fs as afs;
#[cfg(feature = "link_to")]
use std::path::PathBuf;
#[cfg(all(test, feature = "tokio"))]
use tokio::fs as afs;

Expand All @@ -22,7 +24,6 @@ where

use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;

use criterion::{black_box, criterion_group, criterion_main, Criterion};

Expand Down
52 changes: 52 additions & 0 deletions src/content/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,58 @@ pub async fn copy_async<'a>(cache: &'a Path, sri: &'a Integrity, to: &'a Path) -
Ok(size as u64)
}

pub fn hard_link_unchecked(cache: &Path, sri: &Integrity, to: &Path) -> Result<()> {
let cpath = path::content_path(cache, sri);
std::fs::hard_link(cpath, to).with_context(|| {
format!(
"Failed to link cache contents from {} to {}",
path::content_path(cache, sri).display(),
to.display()
)
})?;
Ok(())
}

pub fn hard_link(cache: &Path, sri: &Integrity, to: &Path) -> Result<()> {
hard_link_unchecked(cache, sri, to)?;
let mut reader = open(cache, sri.clone())?;
let mut buf = [0u8; 1024 * 8];
loop {
let read = reader.read(&mut buf).with_context(|| {
format!(
"Failed to read cache contents while verifying integrity for {}",
path::content_path(cache, sri).display()
)
})?;
if read == 0 {
break;
}
}
reader.check()?;
Ok(())
}

pub async fn hard_link_async(cache: &Path, sri: &Integrity, to: &Path) -> Result<()> {
hard_link_unchecked(cache, sri, to)?;
let mut reader = open_async(cache, sri.clone()).await?;
let mut buf = [0u8; 1024 * 8];
loop {
let read = AsyncReadExt::read(&mut reader, &mut buf)
.await
.with_context(|| {
format!(
"Failed to read cache contents while verifying integrity for {}",
path::content_path(cache, sri).display()
)
})?;
if read == 0 {
break;
}
}
reader.check()?;
Ok(())
}

pub fn has_content(cache: &Path, sri: &Integrity) -> Option<Integrity> {
if path::content_path(cache, sri).exists() {
Some(sri.clone())
Expand Down
66 changes: 66 additions & 0 deletions src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,23 @@ where
read::copy_unchecked_async(cache.as_ref(), sri, to.as_ref()).await
}

/// Hard links a cache entry by key to a specified location.
pub async fn hard_link<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
async fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
read::hard_link_async(cache, &entry.integrity, to).await
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref()).await
}

/// Gets the metadata entry for a certain key.
///
/// Note that the existence of a metadata entry is not a guarantee that the
Expand Down Expand Up @@ -573,6 +590,55 @@ where
read::copy_unchecked(cache.as_ref(), sri, to.as_ref())
}

/// Hard links a cache entry by key to a specified location. The cache entry
/// contents will not be checked, and all the usual caveats of hard links
/// apply: The potentially-shared cache might be corrupted if the hard link is
/// modified.
pub fn hard_link_unchecked_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
hard_link_hash_unchecked_sync(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}

/// Hard links a cache entry by key to a specified location.
pub fn hard_link_sync<P, K, Q>(cache: P, key: K, to: Q) -> Result<()>
where
P: AsRef<Path>,
K: AsRef<str>,
Q: AsRef<Path>,
{
fn inner(cache: &Path, key: &str, to: &Path) -> Result<()> {
if let Some(entry) = index::find(cache, key)? {
read::hard_link(cache, &entry.integrity, to)
} else {
Err(Error::EntryNotFound(cache.to_path_buf(), key.into()))
}
}
inner(cache.as_ref(), key.as_ref(), to.as_ref())
}

/// Hard links a cache entry by integrity address to a specified location. The
/// cache entry contents will not be checked, and all the usual caveats of
/// hard links apply: The potentially-shared cache might be corrupted if the
/// hard link is modified.
pub fn hard_link_hash_unchecked_sync<P, Q>(cache: P, sri: &Integrity, to: Q) -> Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
read::hard_link_unchecked(cache.as_ref(), sri, to.as_ref())
}

/// Gets metadata for a certain key.
///
/// Note that the existence of a metadata entry is not a guarantee that the
Expand Down

0 comments on commit e2695a9

Please sign in to comment.