Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
0d5ab04a90 | |||
4059285649 | |||
2e079b8bf2 | |||
4ff2c9b832 | |||
a8e2940ff3 |
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "proxmox-backup"
|
name = "proxmox-backup"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
authors = ["Dietmar Maurer <dietmar@proxmox.com>"]
|
authors = ["Dietmar Maurer <dietmar@proxmox.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3"
|
license = "AGPL-3"
|
||||||
|
10
debian/changelog
vendored
10
debian/changelog
vendored
@ -1,3 +1,13 @@
|
|||||||
|
rust-proxmox-backup (0.5.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* partially revert commit 1f82f9b7b5d231da22a541432d5617cb303c0000
|
||||||
|
|
||||||
|
* ui: allow to Forget (delete) backup snapshots
|
||||||
|
|
||||||
|
* pxar: deal with files changing size during archiving
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Mon, 29 Jun 2020 13:00:54 +0200
|
||||||
|
|
||||||
rust-proxmox-backup (0.4.0-1) unstable; urgency=medium
|
rust-proxmox-backup (0.4.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* change api for incremental backups mode
|
* change api for incremental backups mode
|
||||||
|
@ -189,6 +189,19 @@ impl IndexFile for DynamicIndexReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_csum(&self) -> ([u8; 32], u64) {
|
||||||
|
let mut csum = openssl::sha::Sha256::new();
|
||||||
|
let mut chunk_end = 0;
|
||||||
|
for pos in 0..self.index_count() {
|
||||||
|
let info = self.chunk_info(pos).unwrap();
|
||||||
|
chunk_end = info.range.end;
|
||||||
|
csum.update(&chunk_end.to_le_bytes());
|
||||||
|
csum.update(&info.digest);
|
||||||
|
}
|
||||||
|
let csum = csum.finish();
|
||||||
|
(csum, chunk_end)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
fn chunk_info(&self, pos: usize) -> Option<ChunkReadInfo> {
|
fn chunk_info(&self, pos: usize) -> Option<ChunkReadInfo> {
|
||||||
if pos >= self.index.len() {
|
if pos >= self.index.len() {
|
||||||
|
@ -206,6 +206,19 @@ impl IndexFile for FixedIndexReader {
|
|||||||
digest: *digest,
|
digest: *digest,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_csum(&self) -> ([u8; 32], u64) {
|
||||||
|
let mut csum = openssl::sha::Sha256::new();
|
||||||
|
let mut chunk_end = 0;
|
||||||
|
for pos in 0..self.index_count() {
|
||||||
|
let info = self.chunk_info(pos).unwrap();
|
||||||
|
chunk_end = info.range.end;
|
||||||
|
csum.update(&info.digest);
|
||||||
|
}
|
||||||
|
let csum = csum.finish();
|
||||||
|
|
||||||
|
(csum, chunk_end)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FixedIndexWriter {
|
pub struct FixedIndexWriter {
|
||||||
|
@ -23,19 +23,7 @@ pub trait IndexFile {
|
|||||||
fn chunk_info(&self, pos: usize) -> Option<ChunkReadInfo>;
|
fn chunk_info(&self, pos: usize) -> Option<ChunkReadInfo>;
|
||||||
|
|
||||||
/// Compute index checksum and size
|
/// Compute index checksum and size
|
||||||
fn compute_csum(&self) -> ([u8; 32], u64) {
|
fn compute_csum(&self) -> ([u8; 32], u64);
|
||||||
let mut csum = openssl::sha::Sha256::new();
|
|
||||||
let mut chunk_end = 0;
|
|
||||||
for pos in 0..self.index_count() {
|
|
||||||
let info = self.chunk_info(pos).unwrap();
|
|
||||||
chunk_end = info.range.end;
|
|
||||||
csum.update(&chunk_end.to_le_bytes());
|
|
||||||
csum.update(&info.digest);
|
|
||||||
}
|
|
||||||
let csum = csum.finish();
|
|
||||||
|
|
||||||
(csum, chunk_end)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns most often used chunks
|
/// Returns most often used chunks
|
||||||
fn find_most_used_chunks(&self, max: usize) -> HashMap<[u8; 32], usize> {
|
fn find_most_used_chunks(&self, max: usize) -> HashMap<[u8; 32], usize> {
|
||||||
|
@ -434,7 +434,7 @@ impl BackupWriter {
|
|||||||
self.h2.download("previous", Some(param), &mut tmpfile).await?;
|
self.h2.download("previous", Some(param), &mut tmpfile).await?;
|
||||||
|
|
||||||
let index = DynamicIndexReader::new(tmpfile)
|
let index = DynamicIndexReader::new(tmpfile)
|
||||||
.map_err(|err| format_err!("unable to read fixed index '{}' - {}", archive_name, err))?;
|
.map_err(|err| format_err!("unable to read dynmamic index '{}' - {}", archive_name, err))?;
|
||||||
// Note: do not use values stored in index (not trusted) - instead, computed them again
|
// Note: do not use values stored in index (not trusted) - instead, computed them again
|
||||||
let (csum, size) = index.compute_csum();
|
let (csum, size) = index.compute_csum();
|
||||||
manifest.verify_file(archive_name, &csum, size)?;
|
manifest.verify_file(archive_name, &csum, size)?;
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::{HashSet, HashMap};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
use std::ffi::{CStr, CString, OsStr};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -20,6 +20,7 @@ use pxar::encoder::LinkOffset;
|
|||||||
use proxmox::c_str;
|
use proxmox::c_str;
|
||||||
use proxmox::sys::error::SysError;
|
use proxmox::sys::error::SysError;
|
||||||
use proxmox::tools::fd::RawFdNum;
|
use proxmox::tools::fd::RawFdNum;
|
||||||
|
use proxmox::tools::vec;
|
||||||
|
|
||||||
use crate::pxar::catalog::BackupCatalogWriter;
|
use crate::pxar::catalog::BackupCatalogWriter;
|
||||||
use crate::pxar::Flags;
|
use crate::pxar::Flags;
|
||||||
@ -35,6 +36,7 @@ fn detect_fs_type(fd: RawFd) -> Result<i64, Error> {
|
|||||||
Ok(fs_stat.f_type)
|
Ok(fs_stat.f_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
pub fn is_virtual_file_system(magic: i64) -> bool {
|
pub fn is_virtual_file_system(magic: i64) -> bool {
|
||||||
use proxmox::sys::linux::magic::*;
|
use proxmox::sys::linux::magic::*;
|
||||||
|
|
||||||
@ -114,6 +116,7 @@ struct Archiver<'a, 'b> {
|
|||||||
device_set: Option<HashSet<u64>>,
|
device_set: Option<HashSet<u64>>,
|
||||||
hardlinks: HashMap<HardLinkInfo, (PathBuf, LinkOffset)>,
|
hardlinks: HashMap<HardLinkInfo, (PathBuf, LinkOffset)>,
|
||||||
errors: ErrorReporter,
|
errors: ErrorReporter,
|
||||||
|
file_copy_buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Encoder<'a, 'b> = pxar::encoder::Encoder<'a, &'b mut dyn pxar::encoder::SeqWrite>;
|
type Encoder<'a, 'b> = pxar::encoder::Encoder<'a, &'b mut dyn pxar::encoder::SeqWrite>;
|
||||||
@ -178,6 +181,7 @@ where
|
|||||||
device_set,
|
device_set,
|
||||||
hardlinks: HashMap::new(),
|
hardlinks: HashMap::new(),
|
||||||
errors: ErrorReporter,
|
errors: ErrorReporter,
|
||||||
|
file_copy_buffer: vec::undefined(4 * 1024 * 1024),
|
||||||
};
|
};
|
||||||
|
|
||||||
archiver.archive_dir_contents(&mut encoder, source_dir, true)?;
|
archiver.archive_dir_contents(&mut encoder, source_dir, true)?;
|
||||||
@ -410,6 +414,24 @@ impl<'a, 'b> Archiver<'a, 'b> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn report_file_shrunk_while_reading(&mut self) -> Result<(), Error> {
|
||||||
|
write!(
|
||||||
|
self.errors,
|
||||||
|
"warning: file size shrunk while reading: {:?}, file will be padded with zeros!",
|
||||||
|
self.path,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report_file_grew_while_reading(&mut self) -> Result<(), Error> {
|
||||||
|
write!(
|
||||||
|
self.errors,
|
||||||
|
"warning: file size increased while reading: {:?}, file will be truncated!",
|
||||||
|
self.path,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn add_entry(
|
fn add_entry(
|
||||||
&mut self,
|
&mut self,
|
||||||
encoder: &mut Encoder,
|
encoder: &mut Encoder,
|
||||||
@ -591,8 +613,29 @@ impl<'a, 'b> Archiver<'a, 'b> {
|
|||||||
file_size: u64,
|
file_size: u64,
|
||||||
) -> Result<LinkOffset, Error> {
|
) -> Result<LinkOffset, Error> {
|
||||||
let mut file = unsafe { std::fs::File::from_raw_fd(fd.into_raw_fd()) };
|
let mut file = unsafe { std::fs::File::from_raw_fd(fd.into_raw_fd()) };
|
||||||
let offset = encoder.add_file(metadata, file_name, file_size, &mut file)?;
|
let mut remaining = file_size;
|
||||||
Ok(offset)
|
let mut out = encoder.create_file(metadata, file_name, file_size)?;
|
||||||
|
while remaining != 0 {
|
||||||
|
let mut got = file.read(&mut self.file_copy_buffer[..])?;
|
||||||
|
if got as u64 > remaining {
|
||||||
|
self.report_file_grew_while_reading()?;
|
||||||
|
got = remaining as usize;
|
||||||
|
}
|
||||||
|
out.write_all(&self.file_copy_buffer[..got])?;
|
||||||
|
remaining -= got as u64;
|
||||||
|
}
|
||||||
|
if remaining > 0 {
|
||||||
|
self.report_file_shrunk_while_reading()?;
|
||||||
|
let to_zero = remaining.min(self.file_copy_buffer.len() as u64) as usize;
|
||||||
|
vec::clear(&mut self.file_copy_buffer[..to_zero]);
|
||||||
|
while remaining != 0 {
|
||||||
|
let fill = remaining.min(self.file_copy_buffer.len() as u64) as usize;
|
||||||
|
out.write_all(&self.file_copy_buffer[..fill])?;
|
||||||
|
remaining -= fill as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out.file_offset())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_symlink(
|
fn add_symlink(
|
||||||
|
@ -199,6 +199,34 @@ Ext.define('PBS.DataStoreContent', {
|
|||||||
win.show();
|
win.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onForget: function() {
|
||||||
|
var view = this.getView();
|
||||||
|
|
||||||
|
let rec = view.selModel.getSelection()[0];
|
||||||
|
if (!(rec && rec.data)) return;
|
||||||
|
let data = rec.data;
|
||||||
|
if (!data.leaf) return;
|
||||||
|
|
||||||
|
if (!view.datastore) return;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
Proxmox.Utils.API2Request({
|
||||||
|
params: {
|
||||||
|
"backup-type": data["backup-type"],
|
||||||
|
"backup-id": data["backup-id"],
|
||||||
|
"backup-time": (data['backup-time'].getTime()/1000).toFixed(0),
|
||||||
|
},
|
||||||
|
url: `/admin/datastore/${view.datastore}/snapshots`,
|
||||||
|
method: 'DELETE',
|
||||||
|
waitMsgTarget: view,
|
||||||
|
failure: function(response, opts) {
|
||||||
|
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
|
||||||
|
},
|
||||||
|
callback: this.reload.bind(this),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
openBackupFileDownloader: function() {
|
openBackupFileDownloader: function() {
|
||||||
let me = this;
|
let me = this;
|
||||||
let view = me.getView();
|
let view = me.getView();
|
||||||
@ -336,6 +364,21 @@ Ext.define('PBS.DataStoreContent', {
|
|||||||
enableFn: function(record) { return !record.data.leaf; },
|
enableFn: function(record) { return !record.data.leaf; },
|
||||||
handler: 'onPrune',
|
handler: 'onPrune',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Forget'),
|
||||||
|
disabled: true,
|
||||||
|
parentXType: 'pbsDataStoreContent',
|
||||||
|
handler: 'onForget',
|
||||||
|
confirmMsg: function(record) {
|
||||||
|
console.log(record);
|
||||||
|
let name = record.data.text;
|
||||||
|
return Ext.String.format(gettext('Are you sure you want to remove snapshot {0}'), `'${name}'`);
|
||||||
|
},
|
||||||
|
enableFn: function(record) {
|
||||||
|
return !!record.data.leaf;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
xtype: 'proxmoxButton',
|
xtype: 'proxmoxButton',
|
||||||
text: gettext('Download Files'),
|
text: gettext('Download Files'),
|
||||||
|
Reference in New Issue
Block a user