Skip to content

Commit

Permalink
feat: allow to skip unsupported files (e.g. UNIX named socket)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean Belicot committed Nov 2, 2023
1 parent 3474445 commit fa458aa
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 7 deletions.
49 changes: 42 additions & 7 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub struct Builder<W: Write> {
mode: HeaderMode,
follow: bool,
finished: bool,
#[cfg(unix)]
skip_unsupported: bool,
obj: Option<W>,
}

Expand All @@ -27,6 +29,8 @@ impl<W: Write> Builder<W> {
mode: HeaderMode::Complete,
follow: true,
finished: false,
#[cfg(unix)]
skip_unsupported: false,
obj: Some(obj),
}
}
Expand All @@ -44,6 +48,13 @@ impl<W: Write> Builder<W> {
self.follow = follow;
}

/// Skip unsupported file types (e.g. UNIX sockets) rather than returning
/// with an error.
#[cfg(unix)]
pub fn skip_unsupported_file_types(&mut self, skip: bool) {
self.skip_unsupported = skip
}

/// Gets shared reference to the underlying object.
pub fn get_ref(&self) -> &W {
self.obj.as_ref().unwrap()
Expand Down Expand Up @@ -237,7 +248,17 @@ impl<W: Write> Builder<W> {
pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
let mode = self.mode.clone();
let follow = self.follow;
append_path_with_name(self.get_mut(), path.as_ref(), None, mode, follow)
#[cfg(unix)]
let skip_unsupported = self.skip_unsupported;
append_path_with_name(
self.get_mut(),
path.as_ref(),
None,
mode,
follow,
#[cfg(unix)]
skip_unsupported,
)
}

/// Adds a file on the local filesystem to this archive under another name.
Expand Down Expand Up @@ -275,12 +296,16 @@ impl<W: Write> Builder<W> {
) -> io::Result<()> {
let mode = self.mode.clone();
let follow = self.follow;
#[cfg(unix)]
let skip_unsupported = self.skip_unsupported;
append_path_with_name(
self.get_mut(),
path.as_ref(),
Some(name.as_ref()),
mode,
follow,
#[cfg(unix)]
skip_unsupported,
)
}

Expand Down Expand Up @@ -381,12 +406,16 @@ impl<W: Write> Builder<W> {
{
let mode = self.mode.clone();
let follow = self.follow;
#[cfg(unix)]
let skip_unsupported = self.skip_unsupported;
append_dir_all(
self.get_mut(),
path.as_ref(),
src_path.as_ref(),
mode,
follow,
#[cfg(unix)]
skip_unsupported,
)
}

Expand Down Expand Up @@ -426,6 +455,7 @@ fn append_path_with_name(
name: Option<&Path>,
mode: HeaderMode,
follow: bool,
#[cfg(unix)] skip_unsupported: bool,
) -> io::Result<()> {
let stat = if follow {
fs::metadata(path).map_err(|err| {
Expand Down Expand Up @@ -460,7 +490,7 @@ fn append_path_with_name(
} else {
#[cfg(unix)]
{
append_special(dst, path, &stat, mode)
append_special(dst, path, &stat, mode, skip_unsupported)
}
#[cfg(not(unix))]
{
Expand All @@ -475,17 +505,21 @@ fn append_special(
path: &Path,
stat: &fs::Metadata,
mode: HeaderMode,
skip_unsupported: bool,
) -> io::Result<()> {
use ::std::os::unix::fs::{FileTypeExt, MetadataExt};

let file_type = stat.file_type();
let entry_type;
if file_type.is_socket() {
// sockets can't be archived
return Err(other(&format!(
"{}: socket can not be archived",
path.display()
)));
return match skip_unsupported {
true => Ok(()),
false => Err(other(&format!(
"{}: socket can not be archived",
path.display()
))),
};
} else if file_type.is_fifo() {
entry_type = EntryType::Fifo;
} else if file_type.is_char_device() {
Expand Down Expand Up @@ -623,6 +657,7 @@ fn append_dir_all(
src_path: &Path,
mode: HeaderMode,
follow: bool,
#[cfg(unix)] skip_unsupported: bool,
) -> io::Result<()> {
let mut stack = vec![(src_path.to_path_buf(), true, false)];
while let Some((src, is_dir, is_symlink)) = stack.pop() {
Expand All @@ -646,7 +681,7 @@ fn append_dir_all(
{
let stat = fs::metadata(&src)?;
if !stat.is_file() {
append_special(dst, &dest, &stat, mode)?;
append_special(dst, &dest, &stat, mode, skip_unsupported)?;
continue;
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ macro_rules! t {
};
}

macro_rules! te {
($e:expr) => {
match $e {
Err(e) => e,
Ok(_) => panic!("{} was expected to return with error", stringify!($e)),
}
};
}

macro_rules! tar {
($e:expr) => {
&include_bytes!(concat!("archives/", $e))[..]
Expand Down Expand Up @@ -1368,6 +1377,7 @@ fn read_only_directory_containing_files() {
fn tar_directory_containing_special_files() {
use std::env;
use std::ffi::CString;
use std::os::unix::net::UnixListener;

let td = t!(TempBuilder::new().prefix("tar-rs").tempdir());
let fifo = td.path().join("fifo");
Expand All @@ -1381,10 +1391,21 @@ fn tar_directory_containing_special_files() {
}
}

let socket = td.path().join("socket");
match UnixListener::bind(socket) {
Ok(_) => {}
Err(e) => panic!("Failed to create and bind to a UNIX named socket: {}", e),
}

t!(env::set_current_dir(td.path()));
let mut ar = Builder::new(Vec::new());
// Default behavior is expected to reject UNIX named sockets, so we need to test it first.
// append_path has a different logic for processing files, so we need to test it as well
te!(ar.append_path("socket"));
te!(ar.append_dir_all("special", td.path()));
ar.skip_unsupported_file_types(true);
t!(ar.append_path("fifo"));
t!(ar.append_path("socket"));
t!(ar.append_dir_all("special", td.path()));
// unfortunately, block device file cannot be created by non-root users
// as a substitute, just test the file that exists on most Unix systems
Expand Down

0 comments on commit fa458aa

Please sign in to comment.