- imported pbs-api-types/src/common_regex.rs from old proxmox crate - use hex crate to generate/parse hex digest - remove all reference to proxmox crate (use proxmox-sys and proxmox-serde instead) Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
110 lines
3.3 KiB
Rust
110 lines
3.3 KiB
Rust
use std::path::Path;
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
use anyhow::{format_err, bail, Error};
|
|
|
|
use proxmox_sys::fs::CreateOptions;
|
|
|
|
use crate::tape::{MediaCatalog, MediaId};
|
|
|
|
/// Returns a list of (store, snapshot) for a given MediaId
|
|
///
|
|
/// To speedup things for large catalogs, we cache the list of
|
|
/// snapshots into a separate file.
|
|
pub fn media_catalog_snapshot_list(
|
|
base_path: &Path,
|
|
media_id: &MediaId,
|
|
) -> Result<Vec<(String, String)>, Error> {
|
|
|
|
let uuid = &media_id.label.uuid;
|
|
|
|
let mut cache_path = base_path.to_owned();
|
|
cache_path.push(uuid.to_string());
|
|
let mut catalog_path = cache_path.clone();
|
|
cache_path.set_extension("index");
|
|
catalog_path.set_extension("log");
|
|
|
|
let stat = match nix::sys::stat::stat(&catalog_path) {
|
|
Ok(stat) => stat,
|
|
Err(err) => bail!("unable to stat media catalog {:?} - {}", catalog_path, err),
|
|
};
|
|
|
|
let cache_id = format!("{:016X}-{:016X}-{:016X}", stat.st_ino, stat.st_size as u64, stat.st_mtime as u64);
|
|
|
|
match std::fs::OpenOptions::new().read(true).open(&cache_path) {
|
|
Ok(file) => {
|
|
let mut list = Vec::new();
|
|
let file = BufReader::new(file);
|
|
let mut lines = file.lines();
|
|
match lines.next() {
|
|
Some(Ok(id)) => {
|
|
if id != cache_id { // cache is outdated - rewrite
|
|
return write_snapshot_cache(base_path, media_id, &cache_path, &cache_id);
|
|
}
|
|
}
|
|
_ => bail!("unable to read catalog cache firstline {:?}", cache_path),
|
|
}
|
|
|
|
for line in lines {
|
|
let mut line = line?;
|
|
|
|
let idx = line
|
|
.find(':')
|
|
.ok_or_else(|| format_err!("invalid line format (no store found)"))?;
|
|
|
|
let snapshot = line.split_off(idx + 1);
|
|
line.truncate(idx);
|
|
list.push((line, snapshot));
|
|
}
|
|
|
|
Ok(list)
|
|
}
|
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
|
write_snapshot_cache(base_path, media_id, &cache_path, &cache_id)
|
|
}
|
|
Err(err) => bail!("unable to open catalog cache - {}", err),
|
|
}
|
|
}
|
|
|
|
fn write_snapshot_cache(
|
|
base_path: &Path,
|
|
media_id: &MediaId,
|
|
cache_path: &Path,
|
|
cache_id: &str,
|
|
) -> Result<Vec<(String, String)>, Error> {
|
|
|
|
// open normal catalog and write cache
|
|
let catalog = MediaCatalog::open(base_path, media_id, false, false)?;
|
|
|
|
let mut data = String::new();
|
|
data.push_str(cache_id);
|
|
data.push('\n');
|
|
|
|
let mut list = Vec::new();
|
|
for (store, content) in catalog.content() {
|
|
for snapshot in content.snapshot_index.keys() {
|
|
list.push((store.to_string(), snapshot.to_string()));
|
|
data.push_str(store);
|
|
data.push(':');
|
|
data.push_str(snapshot);
|
|
data.push('\n');
|
|
}
|
|
}
|
|
|
|
let backup_user = pbs_config::backup_user()?;
|
|
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
|
|
let options = CreateOptions::new()
|
|
.perm(mode)
|
|
.owner(backup_user.uid)
|
|
.group(backup_user.gid);
|
|
|
|
proxmox_sys::fs::replace_file(
|
|
cache_path,
|
|
data.as_bytes(),
|
|
options,
|
|
false,
|
|
)?;
|
|
|
|
Ok(list)
|
|
}
|