radicle: temporary repository helper
It's possible that a temporary directory can be created on a different mount, and when moved during a clone, creates an I/O error. To prevent this, add a method to `Storage` which creates a temporary directory and `Repository` with the path in the form `<rid>.lock`. This `.lock` path name can easily be skipped when listing repositories. Further tooling can be added for detecting leftover `.lock` directories if, for some reason, they were not cleaned up by the `tempfile` crate. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
25ed6b8b28
commit
e71f07e592
|
|
@ -136,13 +136,7 @@ impl Runtime {
|
||||||
let network = config.network;
|
let network = config.network;
|
||||||
let rng = fastrand::Rng::new();
|
let rng = fastrand::Rng::new();
|
||||||
let clock = LocalTime::now();
|
let clock = LocalTime::now();
|
||||||
let storage = Storage::open(
|
let storage = Storage::open(home.storage(), git::UserInfo { alias, key: id })?;
|
||||||
home.storage(),
|
|
||||||
git::UserInfo {
|
|
||||||
alias: alias.clone(),
|
|
||||||
key: id,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let address_db = node_dir.join(ADDRESS_DB_FILE);
|
let address_db = node_dir.join(ADDRESS_DB_FILE);
|
||||||
let routing_db = node_dir.join(ROUTING_DB_FILE);
|
let routing_db = node_dir.join(ROUTING_DB_FILE);
|
||||||
let tracking_db = node_dir.join(TRACKING_DB_FILE);
|
let tracking_db = node_dir.join(TRACKING_DB_FILE);
|
||||||
|
|
@ -254,7 +248,6 @@ impl Runtime {
|
||||||
scope,
|
scope,
|
||||||
tracking_db,
|
tracking_db,
|
||||||
limit: FetchLimit::default(),
|
limit: FetchLimit::default(),
|
||||||
info: git::UserInfo { alias, key: nid },
|
|
||||||
local: nid,
|
local: nid,
|
||||||
expiry: worker::garbage::Expiry::default(),
|
expiry: worker::garbage::Expiry::default(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use radicle::identity::Id;
|
||||||
use radicle::prelude::NodeId;
|
use radicle::prelude::NodeId;
|
||||||
use radicle::storage::refs::RefsAt;
|
use radicle::storage::refs::RefsAt;
|
||||||
use radicle::storage::{ReadRepository, ReadStorage};
|
use radicle::storage::{ReadRepository, ReadStorage};
|
||||||
use radicle::{crypto, git, Storage};
|
use radicle::{crypto, Storage};
|
||||||
use radicle_fetch::FetchLimit;
|
use radicle_fetch::FetchLimit;
|
||||||
|
|
||||||
use crate::runtime::{thread, Handle};
|
use crate::runtime::{thread, Handle};
|
||||||
|
|
@ -159,8 +159,6 @@ pub struct FetchConfig {
|
||||||
pub tracking_db: PathBuf,
|
pub tracking_db: PathBuf,
|
||||||
/// Data limits when fetching from a remote.
|
/// Data limits when fetching from a remote.
|
||||||
pub limit: FetchLimit,
|
pub limit: FetchLimit,
|
||||||
/// Information of the local peer.
|
|
||||||
pub info: git::UserInfo,
|
|
||||||
/// Public key of the local peer.
|
/// Public key of the local peer.
|
||||||
pub local: crypto::PublicKey,
|
pub local: crypto::PublicKey,
|
||||||
/// Configuration for `git gc` garbage collection. Defaults to `1
|
/// Configuration for `git gc` garbage collection. Defaults to `1
|
||||||
|
|
@ -281,7 +279,6 @@ impl Worker {
|
||||||
scope,
|
scope,
|
||||||
tracking_db,
|
tracking_db,
|
||||||
limit,
|
limit,
|
||||||
info,
|
|
||||||
local,
|
local,
|
||||||
expiry,
|
expiry,
|
||||||
} = &self.fetch_config;
|
} = &self.fetch_config;
|
||||||
|
|
@ -292,15 +289,7 @@ impl Worker {
|
||||||
let tracked = radicle_fetch::Tracked::from_config(rid, &tracking)?;
|
let tracked = radicle_fetch::Tracked::from_config(rid, &tracking)?;
|
||||||
let blocked = radicle_fetch::BlockList::from_config(&tracking)?;
|
let blocked = radicle_fetch::BlockList::from_config(&tracking)?;
|
||||||
|
|
||||||
let handle = fetch::Handle::new(
|
let handle = fetch::Handle::new(rid, *local, &self.storage, tracked, blocked, channels)?;
|
||||||
rid,
|
|
||||||
*local,
|
|
||||||
info.clone(),
|
|
||||||
&self.storage,
|
|
||||||
tracked,
|
|
||||||
blocked,
|
|
||||||
channels,
|
|
||||||
)?;
|
|
||||||
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
||||||
|
|
||||||
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ pub mod error;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use radicle::crypto::PublicKey;
|
use radicle::crypto::PublicKey;
|
||||||
use radicle::git::UserInfo;
|
|
||||||
use radicle::prelude::Id;
|
use radicle::prelude::Id;
|
||||||
use radicle::storage::git::Repository;
|
|
||||||
use radicle::storage::refs::RefsAt;
|
use radicle::storage::refs::RefsAt;
|
||||||
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
|
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
|
||||||
use radicle::Storage;
|
use radicle::Storage;
|
||||||
|
|
@ -35,7 +33,6 @@ impl Handle {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
rid: Id,
|
rid: Id,
|
||||||
local: PublicKey,
|
local: PublicKey,
|
||||||
info: UserInfo,
|
|
||||||
storage: &Storage,
|
storage: &Storage,
|
||||||
tracked: Tracked,
|
tracked: Tracked,
|
||||||
blocked: BlockList,
|
blocked: BlockList,
|
||||||
|
|
@ -47,8 +44,7 @@ impl Handle {
|
||||||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
||||||
Ok(Handle::Pull { handle })
|
Ok(Handle::Pull { handle })
|
||||||
} else {
|
} else {
|
||||||
let tmp = tempfile::tempdir()?;
|
let (repo, tmp) = storage.lock_repository(rid)?;
|
||||||
let repo = Repository::create(tmp.path(), rid, &info)?;
|
|
||||||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
||||||
Ok(Handle::Clone { handle, tmp })
|
Ok(Handle::Clone { handle, tmp })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use std::{fs, io};
|
||||||
|
|
||||||
use crypto::{Signer, Verified};
|
use crypto::{Signer, Verified};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::crypto::Unverified;
|
use crate::crypto::Unverified;
|
||||||
use crate::git;
|
use crate::git;
|
||||||
|
|
@ -152,6 +153,27 @@ impl Storage {
|
||||||
Ok(Self { path, info })
|
Ok(Self { path, info })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a [`Repository`] in a temporary directory.
|
||||||
|
///
|
||||||
|
/// N.b. it is important to keep the [`TempDir`] in scope while
|
||||||
|
/// using the [`Repository`]. If it is dropped, any action on the
|
||||||
|
/// `Repository` will fail.
|
||||||
|
pub fn lock_repository(&self, rid: Id) -> Result<(Repository, TempDir), RepositoryError> {
|
||||||
|
if self.contains(&rid)? {
|
||||||
|
return Err(Error::Io(io::Error::new(
|
||||||
|
io::ErrorKind::AlreadyExists,
|
||||||
|
format!("refusing to create '{}.lock'", rid),
|
||||||
|
))
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let tmp = tempfile::Builder::new()
|
||||||
|
.prefix(&rid.canonical())
|
||||||
|
.suffix(".lock")
|
||||||
|
.tempdir_in(self.path())
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
Ok((Repository::create(tmp.path(), rid, &self.info)?, tmp))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &Path {
|
||||||
self.path.as_path()
|
self.path.as_path()
|
||||||
}
|
}
|
||||||
|
|
@ -170,6 +192,12 @@ impl Storage {
|
||||||
if path.file_name().to_string_lossy().starts_with('.') {
|
if path.file_name().to_string_lossy().starts_with('.') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip lock files.
|
||||||
|
if let Some(ext) = path.path().extension() {
|
||||||
|
if ext == "lock" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
let rid =
|
let rid =
|
||||||
Id::try_from(path.file_name()).map_err(|_| Error::InvalidId(path.file_name()))?;
|
Id::try_from(path.file_name()).map_err(|_| Error::InvalidId(path.file_name()))?;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue