Skip to content

Commit 2bcd07e

Browse files
committed
feat: allow to skip unsupported files (e.g. UNIX named socket)
1 parent 3474445 commit 2bcd07e

2 files changed

Lines changed: 54 additions & 7 deletions

File tree

src/builder.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Builder<W: Write> {
1515
mode: HeaderMode,
1616
follow: bool,
1717
finished: bool,
18+
skip_unsupported: bool,
1819
obj: Option<W>,
1920
}
2021

@@ -27,6 +28,7 @@ impl<W: Write> Builder<W> {
2728
mode: HeaderMode::Complete,
2829
follow: true,
2930
finished: false,
31+
skip_unsupported: false,
3032
obj: Some(obj),
3133
}
3234
}
@@ -44,6 +46,12 @@ impl<W: Write> Builder<W> {
4446
self.follow = follow;
4547
}
4648

49+
/// Skip unsupported file types (e.g. UNIX sockets) rather than returning
50+
/// with an error.
51+
pub fn skip_unsupported_file_types(&mut self, skip: bool) {
52+
self.skip_unsupported = skip
53+
}
54+
4755
/// Gets shared reference to the underlying object.
4856
pub fn get_ref(&self) -> &W {
4957
self.obj.as_ref().unwrap()
@@ -237,7 +245,15 @@ impl<W: Write> Builder<W> {
237245
pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
238246
let mode = self.mode.clone();
239247
let follow = self.follow;
240-
append_path_with_name(self.get_mut(), path.as_ref(), None, mode, follow)
248+
let skip_unsupported = self.skip_unsupported;
249+
append_path_with_name(
250+
self.get_mut(),
251+
path.as_ref(),
252+
None,
253+
mode,
254+
follow,
255+
skip_unsupported,
256+
)
241257
}
242258

243259
/// Adds a file on the local filesystem to this archive under another name.
@@ -275,12 +291,14 @@ impl<W: Write> Builder<W> {
275291
) -> io::Result<()> {
276292
let mode = self.mode.clone();
277293
let follow = self.follow;
294+
let skip_unsupported = self.skip_unsupported;
278295
append_path_with_name(
279296
self.get_mut(),
280297
path.as_ref(),
281298
Some(name.as_ref()),
282299
mode,
283300
follow,
301+
skip_unsupported,
284302
)
285303
}
286304

@@ -381,12 +399,14 @@ impl<W: Write> Builder<W> {
381399
{
382400
let mode = self.mode.clone();
383401
let follow = self.follow;
402+
let skip_unsupported = self.skip_unsupported;
384403
append_dir_all(
385404
self.get_mut(),
386405
path.as_ref(),
387406
src_path.as_ref(),
388407
mode,
389408
follow,
409+
skip_unsupported,
390410
)
391411
}
392412

@@ -426,6 +446,7 @@ fn append_path_with_name(
426446
name: Option<&Path>,
427447
mode: HeaderMode,
428448
follow: bool,
449+
skip_unsupported: bool,
429450
) -> io::Result<()> {
430451
let stat = if follow {
431452
fs::metadata(path).map_err(|err| {
@@ -460,7 +481,7 @@ fn append_path_with_name(
460481
} else {
461482
#[cfg(unix)]
462483
{
463-
append_special(dst, path, &stat, mode)
484+
append_special(dst, path, &stat, mode, skip_unsupported)
464485
}
465486
#[cfg(not(unix))]
466487
{
@@ -475,17 +496,21 @@ fn append_special(
475496
path: &Path,
476497
stat: &fs::Metadata,
477498
mode: HeaderMode,
499+
skip_unsupported: bool,
478500
) -> io::Result<()> {
479501
use ::std::os::unix::fs::{FileTypeExt, MetadataExt};
480502

481503
let file_type = stat.file_type();
482504
let entry_type;
483505
if file_type.is_socket() {
484506
// sockets can't be archived
485-
return Err(other(&format!(
486-
"{}: socket can not be archived",
487-
path.display()
488-
)));
507+
return match skip_unsupported {
508+
true => Ok(()),
509+
false => Err(other(&format!(
510+
"{}: socket can not be archived",
511+
path.display()
512+
))),
513+
};
489514
} else if file_type.is_fifo() {
490515
entry_type = EntryType::Fifo;
491516
} else if file_type.is_char_device() {
@@ -623,6 +648,7 @@ fn append_dir_all(
623648
src_path: &Path,
624649
mode: HeaderMode,
625650
follow: bool,
651+
skip_unsupported: bool,
626652
) -> io::Result<()> {
627653
let mut stack = vec![(src_path.to_path_buf(), true, false)];
628654
while let Some((src, is_dir, is_symlink)) = stack.pop() {
@@ -646,7 +672,7 @@ fn append_dir_all(
646672
{
647673
let stat = fs::metadata(&src)?;
648674
if !stat.is_file() {
649-
append_special(dst, &dest, &stat, mode)?;
675+
append_special(dst, &dest, &stat, mode, skip_unsupported)?;
650676
continue;
651677
}
652678
}

tests/all.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ macro_rules! t {
2323
};
2424
}
2525

26+
macro_rules! te {
27+
($e:expr) => {
28+
match $e {
29+
Err(e) => e,
30+
Ok(_) => panic!("{} was expected to return with error", stringify!($e)),
31+
}
32+
};
33+
}
34+
2635
macro_rules! tar {
2736
($e:expr) => {
2837
&include_bytes!(concat!("archives/", $e))[..]
@@ -1368,6 +1377,7 @@ fn read_only_directory_containing_files() {
13681377
fn tar_directory_containing_special_files() {
13691378
use std::env;
13701379
use std::ffi::CString;
1380+
use std::os::unix::net::UnixListener;
13711381

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

1394+
let socket = td.path().join("socket");
1395+
match UnixListener::bind(socket) {
1396+
Ok(_) => {}
1397+
Err(e) => panic!("Failed to create and bind to a UNIX named socket: {}", e),
1398+
}
1399+
13841400
t!(env::set_current_dir(td.path()));
13851401
let mut ar = Builder::new(Vec::new());
1402+
// Default behavior is expected to reject UNIX named sockets, so we need to test it first.
13861403
// append_path has a different logic for processing files, so we need to test it as well
1404+
te!(ar.append_path("socket"));
1405+
te!(ar.append_dir_all("special", td.path()));
1406+
ar.skip_unsupported_file_types(true);
13871407
t!(ar.append_path("fifo"));
1408+
t!(ar.append_path("socket"));
13881409
t!(ar.append_dir_all("special", td.path()));
13891410
// unfortunately, block device file cannot be created by non-root users
13901411
// as a substitute, just test the file that exists on most Unix systems

0 commit comments

Comments
 (0)