]> git.proxmox.com Git - proxmox-backup.git/blame - src/tape/media_catalog_cache.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / src / tape / media_catalog_cache.rs
CommitLineData
5ad40a3d
DM
1use std::path::Path;
2use std::io::{BufRead, BufReader};
3
4use anyhow::{format_err, bail, Error};
5
25877d05 6use proxmox_sys::fs::CreateOptions;
5ad40a3d
DM
7
8use crate::tape::{MediaCatalog, MediaId};
9
10/// Returns a list of (store, snapshot) for a given MediaId
11///
12/// To speedup things for large catalogs, we cache the list of
13/// snapshots into a separate file.
14pub fn media_catalog_snapshot_list(
15 base_path: &Path,
16 media_id: &MediaId,
17) -> Result<Vec<(String, String)>, Error> {
18
19 let uuid = &media_id.label.uuid;
20
21 let mut cache_path = base_path.to_owned();
22 cache_path.push(uuid.to_string());
23 let mut catalog_path = cache_path.clone();
24 cache_path.set_extension("index");
25 catalog_path.set_extension("log");
26
27 let stat = match nix::sys::stat::stat(&catalog_path) {
28 Ok(stat) => stat,
29 Err(err) => bail!("unable to stat media catalog {:?} - {}", catalog_path, err),
30 };
31
32 let cache_id = format!("{:016X}-{:016X}-{:016X}", stat.st_ino, stat.st_size as u64, stat.st_mtime as u64);
33
34 match std::fs::OpenOptions::new().read(true).open(&cache_path) {
35 Ok(file) => {
36 let mut list = Vec::new();
37 let file = BufReader::new(file);
38 let mut lines = file.lines();
39 match lines.next() {
40 Some(Ok(id)) => {
41 if id != cache_id { // cache is outdated - rewrite
42 return write_snapshot_cache(base_path, media_id, &cache_path, &cache_id);
43 }
44 }
45 _ => bail!("unable to read catalog cache firstline {:?}", cache_path),
46 }
47
48 for line in lines {
49 let mut line = line?;
50
51 let idx = line
52 .find(':')
53 .ok_or_else(|| format_err!("invalid line format (no store found)"))?;
54
55 let snapshot = line.split_off(idx + 1);
56 line.truncate(idx);
57 list.push((line, snapshot));
58 }
59
60 Ok(list)
61 }
62 Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
63 write_snapshot_cache(base_path, media_id, &cache_path, &cache_id)
64 }
65 Err(err) => bail!("unable to open catalog cache - {}", err),
66 }
67}
68
69fn write_snapshot_cache(
70 base_path: &Path,
71 media_id: &MediaId,
72 cache_path: &Path,
73 cache_id: &str,
74) -> Result<Vec<(String, String)>, Error> {
75
76 // open normal catalog and write cache
77 let catalog = MediaCatalog::open(base_path, media_id, false, false)?;
78
79 let mut data = String::new();
80 data.push_str(cache_id);
81 data.push('\n');
82
83 let mut list = Vec::new();
84 for (store, content) in catalog.content() {
85 for snapshot in content.snapshot_index.keys() {
86 list.push((store.to_string(), snapshot.to_string()));
87 data.push_str(store);
88 data.push(':');
89 data.push_str(snapshot);
90 data.push('\n');
91 }
92 }
93
21211748 94 let backup_user = pbs_config::backup_user()?;
5ad40a3d
DM
95 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
96 let options = CreateOptions::new()
97 .perm(mode)
98 .owner(backup_user.uid)
99 .group(backup_user.gid);
100
25877d05 101 proxmox_sys::fs::replace_file(
5ad40a3d
DM
102 cache_path,
103 data.as_bytes(),
104 options,
e0a19d33 105 false,
5ad40a3d
DM
106 )?;
107
108 Ok(list)
109}