pxar: encoder: warn on lacking read permissions instead of fail.
If during creation of the archive, files/dirs with lacking read permissions are encountered, the user is displayed a warning and the archive is created without including the file/dir. Previously this resulted in an error and the archive creation failed. In order to implement this also for the .pxarexclude files, the Error type of MatchPattern::from_file() and MatchPattern::from_line() was adopted accordingly. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
88cee60bad
commit
ecbc62261c
2
TODO.rst
2
TODO.rst
|
@ -1,8 +1,6 @@
|
||||||
TODO list for Proxmox Backup
|
TODO list for Proxmox Backup
|
||||||
============================
|
============================
|
||||||
|
|
||||||
* do not abort host backups on file permission errors
|
|
||||||
|
|
||||||
* proxmox-backup-client: verify server cert (save/compare last saved cert fingerprint)
|
* proxmox-backup-client: verify server cert (save/compare last saved cert fingerprint)
|
||||||
|
|
||||||
Suggestions
|
Suggestions
|
||||||
|
|
|
@ -697,6 +697,14 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
(Some((buffer, stat)), excludes)
|
(Some((buffer, stat)), excludes)
|
||||||
}
|
}
|
||||||
Ok(None) => (None, Vec::new()),
|
Ok(None) => (None, Vec::new()),
|
||||||
|
Err(nix::Error::Sys(Errno::EACCES)) => {
|
||||||
|
// No permission to read .pxarexclude, ignore its contents.
|
||||||
|
eprintln!(
|
||||||
|
"ignoring match patterns in {:?}: open file failed - EACCES",
|
||||||
|
self.full_path().join(".pxarexclude"),
|
||||||
|
);
|
||||||
|
(None, Vec::new())
|
||||||
|
}
|
||||||
Err(err) => bail!("error while reading exclude file - {}", err),
|
Err(err) => bail!("error while reading exclude file - {}", err),
|
||||||
};
|
};
|
||||||
for excl in &excludes {
|
for excl in &excludes {
|
||||||
|
@ -777,6 +785,7 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
let start_pos = self.writer_pos;
|
let start_pos = self.writer_pos;
|
||||||
|
|
||||||
if filename.as_bytes() == b".pxarexclude" {
|
if filename.as_bytes() == b".pxarexclude" {
|
||||||
|
// pxar_exclude is none in case of error EACCES.
|
||||||
if let Some((ref content, ref stat)) = pxar_exclude {
|
if let Some((ref content, ref stat)) = pxar_exclude {
|
||||||
let filefd = match nix::fcntl::openat(
|
let filefd = match nix::fcntl::openat(
|
||||||
rawfd,
|
rawfd,
|
||||||
|
@ -789,6 +798,14 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
self.report_vanished_file(&self.full_path())?;
|
self.report_vanished_file(&self.full_path())?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(nix::Error::Sys(Errno::EACCES)) => {
|
||||||
|
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
|
||||||
|
eprintln!(
|
||||||
|
"skipping {:?}: open file failed - EACCES",
|
||||||
|
self.full_path().join(filename_osstr),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
|
let filename_osstr = std::ffi::OsStr::from_bytes(filename.to_bytes());
|
||||||
bail!(
|
bail!(
|
||||||
|
@ -810,8 +827,8 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
catalog.add_file(&filename, stat.st_size as u64, stat.st_mtime as u64)?;
|
catalog.add_file(&filename, stat.st_size as u64, stat.st_mtime as u64)?;
|
||||||
}
|
}
|
||||||
self.encode_pxar_exclude(filefd, stat, child_magic, content)?;
|
self.encode_pxar_exclude(filefd, stat, child_magic, content)?;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_root && filename.as_bytes() == b".pxarexclude-cli" {
|
if is_root && filename.as_bytes() == b".pxarexclude-cli" {
|
||||||
|
@ -846,6 +863,14 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
self.relative_path.pop();
|
self.relative_path.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(nix::Error::Sys(Errno::EACCES)) => {
|
||||||
|
eprintln!(
|
||||||
|
"skipping {:?}: open dir failed - EACCES",
|
||||||
|
self.full_path(),
|
||||||
|
);
|
||||||
|
self.relative_path.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Err(err) => bail!("open dir {:?} failed - {}", self.full_path(), err),
|
Err(err) => bail!("open dir {:?} failed - {}", self.full_path(), err),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -901,6 +926,14 @@ impl<'a, W: Write, C: BackupCatalogWriter> Encoder<'a, W, C> {
|
||||||
self.relative_path.pop();
|
self.relative_path.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Err(nix::Error::Sys(Errno::EACCES)) => {
|
||||||
|
eprintln!(
|
||||||
|
"skipping {:?}: open file failed - EACCES",
|
||||||
|
self.full_path(),
|
||||||
|
);
|
||||||
|
self.relative_path.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Err(err) => bail!("open file {:?} failed - {}", self.full_path(), err),
|
Err(err) => bail!("open file {:?} failed - {}", self.full_path(), err),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -97,18 +97,19 @@ impl MatchPattern {
|
||||||
pub fn from_file<P: ?Sized + NixPath>(
|
pub fn from_file<P: ?Sized + NixPath>(
|
||||||
parent_fd: RawFd,
|
parent_fd: RawFd,
|
||||||
filename: &P,
|
filename: &P,
|
||||||
) -> Result<Option<(Vec<MatchPattern>, Vec<u8>, FileStat)>, Error> {
|
) -> Result<Option<(Vec<MatchPattern>, Vec<u8>, FileStat)>, nix::Error> {
|
||||||
let stat = match stat::fstatat(parent_fd, filename, AtFlags::AT_SYMLINK_NOFOLLOW) {
|
let stat = match stat::fstatat(parent_fd, filename, AtFlags::AT_SYMLINK_NOFOLLOW) {
|
||||||
Ok(stat) => stat,
|
Ok(stat) => stat,
|
||||||
Err(nix::Error::Sys(Errno::ENOENT)) => return Ok(None),
|
Err(nix::Error::Sys(Errno::ENOENT)) => return Ok(None),
|
||||||
Err(err) => bail!("stat failed - {}", err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let filefd = fcntl::openat(parent_fd, filename, OFlag::O_NOFOLLOW, Mode::empty())?;
|
let filefd = fcntl::openat(parent_fd, filename, OFlag::O_NOFOLLOW, Mode::empty())?;
|
||||||
let mut file = unsafe { File::from_raw_fd(filefd) };
|
let mut file = unsafe { File::from_raw_fd(filefd) };
|
||||||
|
|
||||||
let mut content_buffer = Vec::new();
|
let mut content_buffer = Vec::new();
|
||||||
let _bytes = file.read_to_end(&mut content_buffer)?;
|
let _bytes = file.read_to_end(&mut content_buffer)
|
||||||
|
.map_err(|_| Errno::EIO)?;
|
||||||
|
|
||||||
let mut match_pattern = Vec::new();
|
let mut match_pattern = Vec::new();
|
||||||
for line in content_buffer.split(|&c| c == b'\n') {
|
for line in content_buffer.split(|&c| c == b'\n') {
|
||||||
|
@ -129,8 +130,8 @@ impl MatchPattern {
|
||||||
/// Pattern starting with '!' are interpreted as negative match pattern.
|
/// Pattern starting with '!' are interpreted as negative match pattern.
|
||||||
/// Pattern with trailing `/` match only against directories.
|
/// Pattern with trailing `/` match only against directories.
|
||||||
/// `.` as well as `..` and any pattern containing `\0` are invalid and will
|
/// `.` as well as `..` and any pattern containing `\0` are invalid and will
|
||||||
/// result in an error.
|
/// result in an error with Errno::EINVAL.
|
||||||
pub fn from_line(line: &[u8]) -> Result<Option<MatchPattern>, Error> {
|
pub fn from_line(line: &[u8]) -> Result<Option<MatchPattern>, nix::Error> {
|
||||||
let mut input = line;
|
let mut input = line;
|
||||||
|
|
||||||
if input.starts_with(b"#") {
|
if input.starts_with(b"#") {
|
||||||
|
@ -160,7 +161,7 @@ impl MatchPattern {
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.is_empty() || input == b"." || input == b".." || input.contains(&b'\0') {
|
if input.is_empty() || input == b"." || input == b".." || input.contains(&b'\0') {
|
||||||
bail!("invalid path component encountered");
|
return Err(nix::Error::Sys(Errno::EINVAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(MatchPattern {
|
Ok(Some(MatchPattern {
|
||||||
|
|
Loading…
Reference in New Issue