Struct Builder
pub struct Builder<W: Write> { /* private fields */ }
A structure for building archives
This structure has methods for building up an archive from scratch into any arbitrary writer.
Implementations
impl<T: Write> Builder<T>
fn append_pax_extensions<'key, 'value>(&mut self, headers: impl IntoIterator<Item = (&'key str, &'value [u8])>) -> Result<(), Error>Append PAX extended headers to the archive.
Takes in an iterator over the list of headers to add to convert it into a header set formatted.
Returns io::Error if an error occurs, else it returns ()
impl<W: Write> Builder<W>
fn new(obj: W) -> Builder<W>Create a new archive builder with the underlying object as the destination of all data written. The builder will use
HeaderMode::Completeby default.fn mode(&mut self, mode: HeaderMode)Changes the HeaderMode that will be used when reading fs Metadata for methods that implicitly read metadata for an input Path. Notably, this does not apply to
append(Header).fn preserve_absolute(&mut self, preserve: bool)Peserve absolute path while creating an archive
fn follow_symlinks(&mut self, follow: bool)Control whether symlinks are followed when reading from the filesystem. Defaults to
true(but see the note below — you almost certainly want to callfollow_symlinks(false)).When
true, symlinks are dereferenced: the archive entry contains the contents of the symlink target rather than the symlink itself, equivalent to GNUtar --dereference(-h). Whenfalse(the default for all mainstream tar implementations), symlinks are stored as symlink entries in the archive.Why you should almost always use
follow_symlinks(false)Every mainstream tar implementation preserves symlinks by default. GNU
tarrequires the explicit--dereference(-h) flag to follow them. Go'sarchive/tarstores whatever the underlyingfs.FSreports and never dereferences on its own. BSDtarbehaves the same way. This crate's default oftrueis a historical quirk kept for compatibility but is wrong for most use-cases:- Symlinks in the source tree are part of its structure and should normally be preserved, not silently replaced by their targets.
- When
true,append_dir_allfollows symlinks that point outsidesrc_pathjust as readily as those inside it. If the archiving process has broader filesystem read access than whoever controls the source tree (e.g. a privileged backup service, a CI runner archiving user-submitted workspaces), an attacker can plant a symlink insidesrc_pathto silently include arbitrary files from the host.
Call
follow_symlinks(false)unless you have a specific reason to flatten symlinks into their targets. For the strongest guarantee, opensrc_pathwithcap-stdand walk the tree with capability-safe I/O, which blocks symlink escapes at the OS level regardless of this setting.fn sparse(&mut self, sparse: bool)Handle sparse files efficiently, if supported by the underlying filesystem. When true, sparse file information is read from disk and empty segments are omitted from the archive. Defaults to true.
fn get_ref(&self) -> &WGets shared reference to the underlying object.
fn get_mut(&mut self) -> &mut WGets mutable reference to the underlying object.
Note that care must be taken while writing to the underlying object. But, e.g.
get_mut().flush()is claimed to be safe and useful in the situations when one needs to be ensured that tar entry was flushed to the disk.fn into_inner(self) -> Result<W>Unwrap this archive, returning the underlying object.
This function will finish writing the archive if the
finishfunction hasn't yet been called, returning any I/O error which happens during that operation.fn append<R: Read>(&mut self, header: &Header, data: R) -> Result<()>Adds a new entry to this archive.
This function will append the header specified, followed by contents of the stream specified by
data. To produce a valid archive thesizefield ofheadermust be the same as the length of the stream that's being written. Additionally the checksum for the header should have been set via theset_cksummethod.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Also note that after all entries have been written to an archive the
finishfunction needs to be called to finish writing the archive.Errors
This function will return an error for any intermittent I/O error which occurs when either reading or writing.
Examples
use ; let mut header = new_gnu; header.set_path.unwrap; header.set_size; header.set_cksum; let mut data: & = &; let mut ar = new; ar.append.unwrap; let data = ar.into_inner.unwrap;fn append_data<P: AsRef<Path>, R: Read>(&mut self, header: &mut Header, path: P, data: R) -> Result<()>Adds a new entry to this archive with the specified path.
This function will set the specified path in the given header, which may require appending a GNU long-name extension entry to the archive first. The checksum for the header will be automatically updated via the
set_cksummethod after setting the path. No other metadata in the header will be modified.Then it will append the header, followed by contents of the stream specified by
data. To produce a valid archive thesizefield ofheadermust be the same as the length of the stream that's being written.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Also note that after all entries have been written to an archive the
finishfunction needs to be called to finish writing the archive.Errors
This function will return an error for any intermittent I/O error which occurs when either reading or writing.
Examples
use ; let mut header = new_gnu; header.set_size; header.set_cksum; let mut data: & = &; let mut ar = new; ar.append_data.unwrap; let data = ar.into_inner.unwrap;fn append_writer<'a, P: AsRef<Path>>(&'a mut self, header: &'a mut Header, path: P) -> Result<EntryWriter<'a>> where W: Seek,Adds a new entry to this archive and returns an
EntryWriterfor adding its contents.This function is similar to
Self::append_databut returns aio::Writeimplementation instead of taking data as a parameter.Similar constraints around the position of the archive and completion apply as with
Self::append_data. It requires the underlying writer to implementSeekto update the header after writing the data.Errors
This function will return an error for any intermittent I/O error which occurs when either reading or writing.
Examples
use Cursor; use Write as _; use ; let mut header = new_gnu; let mut ar = new; let mut entry = ar.append_writer.unwrap; entry.write_all.unwrap; entry.write_all.unwrap; entry.finish.unwrap;fn append_link<P: AsRef<Path>, T: AsRef<Path>>(&mut self, header: &mut Header, path: P, target: T) -> Result<()>Adds a new link (symbolic or hard) entry to this archive with the specified path and target.
This function is similar to
Self::append_datawhich supports long filenames, but also supports long link targets using GNU extensions if necessary. You must set the entry type to eitherEntryType::LinkorEntryType::Symlink. Theset_cksummethod will be invoked after setting the path. No other metadata in the header will be modified.If you are intending to use GNU extensions, you must use this method over calling
Header::set_link_namebecause that function will fail on long links.Similar constraints around the position of the archive and completion apply as with
Self::append_data.Errors
This function will return an error for any intermittent I/O error which occurs when either reading or writing.
Examples
use ; let mut ar = new; let mut header = new_gnu; header.set_username; header.set_entry_type; header.set_size; ar.append_link.unwrap; let data = ar.into_inner.unwrap;fn append_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()>Adds a file on the local filesystem to this archive.
This function will open the file specified by
pathand insert the file into the archive with the appropriate metadata set, returning any I/O error which occurs while writing. The path name for the file inside of this archive will be the same aspath, and it is required that the path is a relative path.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Also note that after all files have been written to an archive the
finishfunction needs to be called to finish writing the archive.Examples
use tar::Builder; let mut ar = Builder::new(Vec::new()); ar.append_path("foo/bar.txt").unwrap();fn append_path_with_name<P: AsRef<Path>, N: AsRef<Path>>(&mut self, path: P, name: N) -> Result<()>Adds a file on the local filesystem to this archive under another name.
This function will open the file specified by
pathand insert the file into the archive asnamewith appropriate metadata set, returning any I/O error which occurs while writing. The path name for the file inside of this archive will benameis required to be a relative path.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Note if the
pathis a directory. This will just add an entry to the archive, rather than contents of the directory.Also note that after all files have been written to an archive the
finishfunction needs to be called to finish writing the archive.Examples
use tar::Builder; let mut ar = Builder::new(Vec::new()); // Insert the local file "foo/bar.txt" in the archive but with the name // "bar/foo.txt". ar.append_path_with_name("foo/bar.txt", "bar/foo.txt").unwrap();fn append_file<P: AsRef<Path>>(&mut self, path: P, file: &mut File) -> Result<()>Adds a file to this archive with the given path as the name of the file in the archive.
This will use the metadata of
fileto populate aHeader, and it will then append the file to the archive with the namepath.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Also note that after all files have been written to an archive the
finishfunction needs to be called to finish writing the archive.Examples
use std::fs::File; use tar::Builder; let mut ar = Builder::new(Vec::new()); // Open the file at one location, but insert it into the archive with a // different name. let mut f = File::open("foo/bar/baz.txt").unwrap(); ar.append_file("bar/baz.txt", &mut f).unwrap();fn append_dir<P, Q>(&mut self, path: P, src_path: Q) -> Result<()> where P: AsRef<Path>, Q: AsRef<Path>,Adds a directory to this archive with the given path as the name of the directory in the archive.
This will use
statto populate aHeader, and it will then append the directory to the archive with the namepath.Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Note this will not add the contents of the directory to the archive. See
append_dir_allfor recursively adding the contents of the directory.Also note that after all files have been written to an archive the
finishfunction needs to be called to finish writing the archive.Examples
use fs; use Builder; let mut ar = new; // Use the directory at one location, but insert it into the archive // with a different name. ar.append_dir.unwrap;fn append_dir_all<P, Q>(&mut self, path: P, src_path: Q) -> Result<()> where P: AsRef<Path>, Q: AsRef<Path>,Adds a directory and all of its contents (recursively) to this archive with the given path as the name of the directory in the archive.
Note that this will not attempt to seek the archive to a valid position, so if the archive is in the middle of a read or some other similar operation then this may corrupt the archive.
Also note that after all files have been written to an archive the
finishorinto_innerfunction needs to be called to finish writing the archive.Security
Call
follow_symlinks(false)before this method unless you have an explicit reason to dereference symlinks. All mainstream tar implementations (GNU tar, BSD tar, Go'sarchive/tar) preserve symlinks by default; this crate's default oftrueis a historical quirk.When
follow_symlinksistrue(the current default), this method dereferences every symlink it encounters, including ones whose targets lie outsidesrc_path. When the archiver runs with broader filesystem access than whoever controls the source tree (e.g. a privileged backup or export service), an attacker can plant a symlink insidesrc_pathto silently include arbitrary files the archiver can read, with no indication in the archive that they came from outside the source root.use tar::Builder; # let src_path = std::path::Path::new("."); # let writer = std::io::sink(); // Recommended: preserve symlinks as-is, matching GNU tar's default. let mut ar = Builder::new(writer); ar.follow_symlinks(false); ar.append_dir_all("", src_path).unwrap(); ar.finish().unwrap();With
follow_symlinks(false), symlinks inside the source tree are stored as symlink entries in the archive rather than being read through. Note that the resulting archive may then contain symlinks with absolute or..-relative targets; validate or strip those on extraction if the archive consumer is also untrusted.For the strongest available guarantee, open
src_pathusingcap-stdand walk the directory tree with capability-safe I/O. This prevents symlink escapes at the OS level and protects against TOCTOU races that a purely path-based check cannot close.Examples
use fs; use Builder; let mut ar = new; // Use the directory at one location ("."), but insert it into the archive // with a different name ("bardir"). ar.append_dir_all.unwrap; ar.finish.unwrap;Use
append_dir_allwith an empty string as the first path argument to create an archive from all files in a directory without renaming.use fs; use PathBuf; use ; let tmpdir = tempdir.unwrap; let path = tmpdir.path; write.unwrap; write.unwrap; // Create a tarball from the files in the directory let mut ar = new; ar.append_dir_all.unwrap; // List files in the archive let archive = ar.into_inner.unwrap; let archived_files = new .entries .unwrap .map .; assert!; assert!;fn finish(&mut self) -> Result<()>Finish writing this archive, emitting the termination sections.
This function should only be called when the archive has been written entirely and if an I/O error happens the underlying object still needs to be acquired.
In most situations the
into_innermethod should be preferred.
Trait Implementations
impl<W: Write> Drop for Builder<W>
fn drop(&mut self)
Auto Trait Implementations
impl<W> Freeze for Builder<W>
where
Option<W>: Freeze,
impl<W> RefUnwindSafe for Builder<W>
where
Option<W>: RefUnwindSafe,
impl<W> Send for Builder<W>
where
Option<W>: Send,
impl<W> Sync for Builder<W>
where
Option<W>: Sync,
impl<W> Unpin for Builder<W>
where
Option<W>: Unpin,
impl<W> UnsafeUnpin for Builder<W>
where
Option<W>: UnsafeUnpin,
impl<W> UnwindSafe for Builder<W>
where
Option<W>: UnwindSafe,
Blanket Implementations
impl<T> Any for Builder<W>
where
T: 'static + ?Sized,
fn type_id(&self) -> TypeId
impl<T> Borrow<T> for Builder<W>
where
T: ?Sized,
fn borrow(&self) -> &T
impl<T> BorrowMut<T> for Builder<W>
where
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
impl<T> From<T> for Builder<W>
fn from(t: T) -> TReturns the argument unchanged.
impl<T, U> Into<U> for Builder<W>
where
U: From<T>,
fn into(self) -> UCalls
U::from(self).That is, this conversion is whatever the implementation of
[From]<T> for Uchooses to do.
impl<T, U> TryFrom<U> for Builder<W>
where
U: Into<T>,
type Error = never;fn try_from(value: U) -> Result<T, never>
impl<T, U> TryInto<U> for Builder<W>
where
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error;fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>