Skip to content

Commit fa458aa

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

2 files changed

Lines changed: 63 additions & 7 deletions

File tree

src/builder.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub struct Builder<W: Write> {
1515
mode: HeaderMode,
1616
follow: bool,
1717
finished: bool,
18+
#[cfg(unix)]
19+
skip_unsupported: bool,
1820
obj: Option<W>,
1921
}
2022

@@ -27,6 +29,8 @@ impl<W: Write> Builder<W> {
2729
mode: HeaderMode::Complete,
2830
follow: true,
2931
finished: false,
32+
#[cfg(unix)]
33+
skip_unsupported: false,
3034
obj: Some(obj),
3135
}
3236
}
@@ -44,6 +48,13 @@ impl<W: Write> Builder<W> {
4448
self.follow = follow;
4549
}
4650

51+
/// Skip unsupported file types (e.g. UNIX sockets) rather than returning
52+
/// with an error.
53+
#[cfg(unix)]
54+
pub fn skip_unsupported_file_types(&mut self, skip: bool) {
55+
self.skip_unsupported = skip
56+
}
57+
4758
/// Gets shared reference to the underlying object.
4859
pub fn get_ref(&self) -> &W {
4960
self.obj.as_ref().unwrap()
@@ -237,7 +248,17 @@ impl<W: Write> Builder<W> {
237248
pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
238249
let mode = self.mode.clone();
239250
let follow = self.follow;
240-
append_path_with_name(self.get_mut(), path.as_ref(), None, mode, follow)
251+
#[cfg(unix)]
252+
let skip_unsupported = self.skip_unsupported;
253+
append_path_with_name(
254+
self.get_mut(),
255+
path.as_ref(),
256+
None,
257+
mode,
258+
follow,
259+
#[cfg(unix)]
260+
skip_unsupported,
261+
)
241262
}
242263

243264
/// Adds a file on the local filesystem to this archive under another name.
@@ -275,12 +296,16 @@ impl<W: Write> Builder<W> {
275296
) -> io::Result<()> {
276297
let mode = self.mode.clone();
277298
let follow = self.follow;
299+
#[cfg(unix)]
300+
let skip_unsupported = self.skip_unsupported;
278301
append_path_with_name(
279302
self.get_mut(),
280303
path.as_ref(),
281304
Some(name.as_ref()),
282305
mode,
283306
follow,
307+
#[cfg(unix)]
308+
skip_unsupported,
284309
)
285310
}
286311

@@ -381,12 +406,16 @@ impl<W: Write> Builder<W> {
381406
{
382407
let mode = self.mode.clone();
383408
let follow = self.follow;
409+
#[cfg(unix)]
410+
let skip_unsupported = self.skip_unsupported;
384411
append_dir_all(
385412
self.get_mut(),
386413
path.as_ref(),
387414
src_path.as_ref(),
388415
mode,
389416
follow,
417+
#[cfg(unix)]
418+
skip_unsupported,
390419
)
391420
}
392421

@@ -426,6 +455,7 @@ fn append_path_with_name(
426455
name: Option<&Path>,
427456
mode: HeaderMode,
428457
follow: bool,
458+
#[cfg(unix)] skip_unsupported: bool,
429459
) -> io::Result<()> {
430460
let stat = if follow {
431461
fs::metadata(path).map_err(|err| {
@@ -460,7 +490,7 @@ fn append_path_with_name(
460490
} else {
461491
#[cfg(unix)]
462492
{
463-
append_special(dst, path, &stat, mode)
493+
append_special(dst, path, &stat, mode, skip_unsupported)
464494
}
465495
#[cfg(not(unix))]
466496
{
@@ -475,17 +505,21 @@ fn append_special(
475505
path: &Path,
476506
stat: &fs::Metadata,
477507
mode: HeaderMode,
508+
skip_unsupported: bool,
478509
) -> io::Result<()> {
479510
use ::std::os::unix::fs::{FileTypeExt, MetadataExt};
480511

481512
let file_type = stat.file_type();
482513
let entry_type;
483514
if file_type.is_socket() {
484515
// sockets can't be archived
485-
return Err(other(&format!(
486-
"{}: socket can not be archived",
487-
path.display()
488-
)));
516+
return match skip_unsupported {
517+
true => Ok(()),
518+
false => Err(other(&format!(
519+
"{}: socket can not be archived",
520+
path.display()
521+
))),
522+
};
489523
} else if file_type.is_fifo() {
490524
entry_type = EntryType::Fifo;
491525
} else if file_type.is_char_device() {
@@ -623,6 +657,7 @@ fn append_dir_all(
623657
src_path: &Path,
624658
mode: HeaderMode,
625659
follow: bool,
660+
#[cfg(unix)] skip_unsupported: bool,
626661
) -> io::Result<()> {
627662
let mut stack = vec![(src_path.to_path_buf(), true, false)];
628663
while let Some((src, is_dir, is_symlink)) = stack.pop() {
@@ -646,7 +681,7 @@ fn append_dir_all(
646681
{
647682
let stat = fs::metadata(&src)?;
648683
if !stat.is_file() {
649-
append_special(dst, &dest, &stat, mode)?;
684+
append_special(dst, &dest, &stat, mode, skip_unsupported)?;
650685
continue;
651686
}
652687
}

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)