Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct Builder<W: Write> {
mode: HeaderMode,
follow: bool,
finished: bool,
skip_unsupported: bool,
obj: Option<W>,
}

Expand All @@ -27,6 +28,7 @@ impl<W: Write> Builder<W> {
mode: HeaderMode::Complete,
follow: true,
finished: false,
skip_unsupported: false,
obj: Some(obj),
}
}
Expand All @@ -44,6 +46,12 @@ impl<W: Write> Builder<W> {
self.follow = follow;
}

/// Skip unsupported file types (e.g. UNIX sockets) rather than returning
/// with an error.
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 +245,15 @@ 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)
let skip_unsupported = self.skip_unsupported;
append_path_with_name(
self.get_mut(),
path.as_ref(),
None,
mode,
follow,
skip_unsupported,
)
}

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

Expand Down Expand Up @@ -381,12 +399,14 @@ impl<W: Write> Builder<W> {
{
let mode = self.mode.clone();
let follow = self.follow;
let skip_unsupported = self.skip_unsupported;
append_dir_all(
self.get_mut(),
path.as_ref(),
src_path.as_ref(),
mode,
follow,
skip_unsupported,
)
}

Expand Down Expand Up @@ -426,6 +446,7 @@ fn append_path_with_name(
name: Option<&Path>,
mode: HeaderMode,
follow: bool,
_skip_unsupported: bool,
) -> io::Result<()> {
let stat = if follow {
fs::metadata(path).map_err(|err| {
Expand Down Expand Up @@ -460,7 +481,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 +496,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 +648,7 @@ fn append_dir_all(
src_path: &Path,
mode: HeaderMode,
follow: bool,
_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 +672,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