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 rng = fastrand::Rng::new();
|
||||
let clock = LocalTime::now();
|
||||
let storage = Storage::open(
|
||||
home.storage(),
|
||||
git::UserInfo {
|
||||
alias: alias.clone(),
|
||||
key: id,
|
||||
},
|
||||
)?;
|
||||
let storage = Storage::open(home.storage(), git::UserInfo { alias, key: id })?;
|
||||
let address_db = node_dir.join(ADDRESS_DB_FILE);
|
||||
let routing_db = node_dir.join(ROUTING_DB_FILE);
|
||||
let tracking_db = node_dir.join(TRACKING_DB_FILE);
|
||||
|
|
@ -254,7 +248,6 @@ impl Runtime {
|
|||
scope,
|
||||
tracking_db,
|
||||
limit: FetchLimit::default(),
|
||||
info: git::UserInfo { alias, key: nid },
|
||||
local: nid,
|
||||
expiry: worker::garbage::Expiry::default(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use radicle::identity::Id;
|
|||
use radicle::prelude::NodeId;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
use radicle::storage::{ReadRepository, ReadStorage};
|
||||
use radicle::{crypto, git, Storage};
|
||||
use radicle::{crypto, Storage};
|
||||
use radicle_fetch::FetchLimit;
|
||||
|
||||
use crate::runtime::{thread, Handle};
|
||||
|
|
@ -159,8 +159,6 @@ pub struct FetchConfig {
|
|||
pub tracking_db: PathBuf,
|
||||
/// Data limits when fetching from a remote.
|
||||
pub limit: FetchLimit,
|
||||
/// Information of the local peer.
|
||||
pub info: git::UserInfo,
|
||||
/// Public key of the local peer.
|
||||
pub local: crypto::PublicKey,
|
||||
/// Configuration for `git gc` garbage collection. Defaults to `1
|
||||
|
|
@ -281,7 +279,6 @@ impl Worker {
|
|||
scope,
|
||||
tracking_db,
|
||||
limit,
|
||||
info,
|
||||
local,
|
||||
expiry,
|
||||
} = &self.fetch_config;
|
||||
|
|
@ -292,15 +289,7 @@ impl Worker {
|
|||
let tracked = radicle_fetch::Tracked::from_config(rid, &tracking)?;
|
||||
let blocked = radicle_fetch::BlockList::from_config(&tracking)?;
|
||||
|
||||
let handle = fetch::Handle::new(
|
||||
rid,
|
||||
*local,
|
||||
info.clone(),
|
||||
&self.storage,
|
||||
tracked,
|
||||
blocked,
|
||||
channels,
|
||||
)?;
|
||||
let handle = fetch::Handle::new(rid, *local, &self.storage, tracked, blocked, channels)?;
|
||||
let result = handle.fetch(rid, &self.storage, *limit, remote, refs_at)?;
|
||||
|
||||
if let Err(e) = garbage::collect(&self.storage, rid, *expiry) {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ pub mod error;
|
|||
use std::collections::HashSet;
|
||||
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::UserInfo;
|
||||
use radicle::prelude::Id;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
use radicle::storage::{ReadStorage as _, RefUpdate, WriteRepository as _};
|
||||
use radicle::Storage;
|
||||
|
|
@ -35,7 +33,6 @@ impl Handle {
|
|||
pub fn new(
|
||||
rid: Id,
|
||||
local: PublicKey,
|
||||
info: UserInfo,
|
||||
storage: &Storage,
|
||||
tracked: Tracked,
|
||||
blocked: BlockList,
|
||||
|
|
@ -47,8 +44,7 @@ impl Handle {
|
|||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
||||
Ok(Handle::Pull { handle })
|
||||
} else {
|
||||
let tmp = tempfile::tempdir()?;
|
||||
let repo = Repository::create(tmp.path(), rid, &info)?;
|
||||
let (repo, tmp) = storage.lock_repository(rid)?;
|
||||
let handle = radicle_fetch::Handle::new(local, repo, tracked, blocked, channels)?;
|
||||
Ok(Handle::Clone { handle, tmp })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use std::{fs, io};
|
|||
|
||||
use crypto::{Signer, Verified};
|
||||
use once_cell::sync::Lazy;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::crypto::Unverified;
|
||||
use crate::git;
|
||||
|
|
@ -152,6 +153,27 @@ impl Storage {
|
|||
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 {
|
||||
self.path.as_path()
|
||||
}
|
||||
|
|
@ -170,6 +192,12 @@ impl Storage {
|
|||
if path.file_name().to_string_lossy().starts_with('.') {
|
||||
continue;
|
||||
}
|
||||
// Skip lock files.
|
||||
if let Some(ext) = path.path().extension() {
|
||||
if ext == "lock" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let rid =
|
||||
Id::try_from(path.file_name()).map_err(|_| Error::InvalidId(path.file_name()))?;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue