Skip to content

Commit d07bc3e

Browse files
Don't follow symlinks in append_dir_all() if follow_symlinks(false)
Before (append_dir_all("file")): called `Result::unwrap()` on an `Err` value: Os { code: 20, kind: NotADirectory, message: "Not a directory" } called `Result::unwrap()` on an `Err` value: Os { code: 267, kind: NotADirectory, message: "The directory name is invalid." } after (append_dir_all("file") or append_dir_all("symlink"): called `Result::unwrap()` on an `Err` value: Custom { kind: NotADirectory, error: "append_dir_all() argument not a directory" } Repro with fn main() { let mut tar = tar::Builder::new(std::io::stdout().lock()); tar.follow_symlinks(false); tar.append_dir_all("", std::env::args_os().nth(1).unwrap()) .unwrap(); tar.finish().unwrap(); } before: $ ls -lnd src symlink Cargo.toml -rw-r--r-- 1 1000 100 118 10-08 18:31 Cargo.toml drwxr-xr-x 2 1000 100 60 10-08 17:57 src lrwxrwxrwx 1 1000 100 3 10-08 18:30 symlink -> src $ cargo run -- Cargo.toml | tar -tv called `Result::unwrap()` on an `Err` value: Os { code: 20, kind: NotADirectory, message: "Not a directory" } $ cargo run -- src | tar -tv -rw-r--r-- 1000/100 211 2025-10-08 18:29 main.rs $ cargo run -- symlink | tar -tv -rw-r--r-- 1000/100 211 2025-10-08 18:29 main.rs after: $ cargo run -- Cargo.toml | tar -tv called `Result::unwrap()` on an `Err` value: Custom { kind: NotADirectory, error: "append_dir_all() argument not a directory" } $ cargo run -- src | tar -tv -rw-r--r-- 1000/100 211 2025-10-08 18:29 main.rs $ cargo run -- symlink | tar -tv called `Result::unwrap()` on an `Err` value: Custom { kind: NotADirectory, error: "append_dir_all() argument not a directory" } This also achieves parity with tar(1)
1 parent 2f9cfc2 commit d07bc3e

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

‎src/builder.rs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,18 @@ fn append_dir_all(
870870
src_path: &Path,
871871
options: BuilderOptions,
872872
) -> io::Result<()> {
873-
let mut stack = vec![(src_path.to_path_buf(), true, false)];
873+
let src_metadata = if options.follow {
874+
fs::metadata(src_path)
875+
} else {
876+
fs::symlink_metadata(src_path)
877+
}?;
878+
if !src_metadata.is_dir() {
879+
return Err(io::Error::new(
880+
io::ErrorKind::NotADirectory,
881+
"append_dir_all() argument not a directory",
882+
));
883+
}
884+
let mut stack = vec![(src_path.to_path_buf(), true, src_metadata.is_symlink())];
874885
while let Some((src, is_dir, is_symlink)) = stack.pop() {
875886
let dest = path.join(src.strip_prefix(src_path).unwrap());
876887
// In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true

0 commit comments

Comments
 (0)