2 use std
::os
::unix
::prelude
::*;
4 use std
::os
::windows
::prelude
::*;
11 use std
::iter
::repeat
;
13 use std
::path
::{Component, Path, PathBuf}
;
19 /// Representation of the header of an entry in an archive
21 #[allow(missing_docs)]
26 /// Declares the information that should be included when filling a Header
27 /// from filesystem metadata.
28 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
31 /// All supported metadata, including mod/access times and ownership will
35 /// Only metadata that is directly relevant to the identity of a file will
36 /// be included. In particular, ownership and mod/access times are excluded.
40 /// Representation of the header of an entry in an archive
42 #[allow(missing_docs)]
43 pub struct OldHeader
{
51 pub linkflag
: [u8; 1],
52 pub linkname
: [u8; 100],
56 /// Representation of the header of an entry in an archive
58 #[allow(missing_docs)]
59 pub struct UstarHeader
{
67 pub typeflag
: [u8; 1],
68 pub linkname
: [u8; 100],
75 pub dev_major
: [u8; 8],
76 pub dev_minor
: [u8; 8],
77 pub prefix
: [u8; 155],
81 /// Representation of the header of an entry in an archive
83 #[allow(missing_docs)]
84 pub struct GnuHeader
{
92 pub typeflag
: [u8; 1],
93 pub linkname
: [u8; 100],
100 pub dev_major
: [u8; 8],
101 pub dev_minor
: [u8; 8],
104 pub offset
: [u8; 12],
105 pub longnames
: [u8; 4],
107 pub sparse
: [GnuSparseHeader
; 4],
108 pub isextended
: [u8; 1],
109 pub realsize
: [u8; 12],
113 /// Description of the header of a spare entry.
115 /// Specifies the offset/number of bytes of a chunk of data in octal.
117 #[allow(missing_docs)]
118 pub struct GnuSparseHeader
{
119 pub offset
: [u8; 12],
120 pub numbytes
: [u8; 12],
123 /// Representation of the entry found to represent extended GNU sparse files.
125 /// When a `GnuHeader` has the `isextended` flag set to `1` then the contents of
126 /// the next entry will be one of these headers.
128 #[allow(missing_docs)]
129 pub struct GnuExtSparseHeader
{
130 pub sparse
: [GnuSparseHeader
; 21],
131 pub isextended
: [u8; 1],
132 pub padding
: [u8; 7],
136 /// Creates a new blank GNU header.
138 /// The GNU style header is the default for this library and allows various
139 /// extensions such as long path names, long link names, and setting the
140 /// atime/ctime metadata attributes of files.
141 pub fn new_gnu() -> Header
{
142 let mut header
= Header { bytes: [0; 512] }
;
144 let gnu
= cast_mut
::<_
, GnuHeader
>(&mut header
);
145 gnu
.magic
= *b
"ustar ";
146 gnu
.version
= *b
" \0";
152 /// Creates a new blank UStar header.
154 /// The UStar style header is an extension of the original archive header
155 /// which enables some extra metadata along with storing a longer (but not
156 /// too long) path name.
158 /// UStar is also the basis used for pax archives.
159 pub fn new_ustar() -> Header
{
160 let mut header
= Header { bytes: [0; 512] }
;
162 let gnu
= cast_mut
::<_
, UstarHeader
>(&mut header
);
163 gnu
.magic
= *b
"ustar\0";
164 gnu
.version
= *b
"00";
170 /// Creates a new blank old header.
172 /// This header format is the original archive header format which all other
173 /// versions are compatible with (e.g. they are a superset). This header
174 /// format limits the path name limit and isn't able to contain extra
175 /// metadata like atime/ctime.
176 pub fn new_old() -> Header
{
177 let mut header
= Header { bytes: [0; 512] }
;
182 fn is_ustar(&self) -> bool
{
183 let ustar
= unsafe { cast::<_, UstarHeader>(self) }
;
184 ustar
.magic
[..] == b
"ustar\0"[..] && ustar
.version
[..] == b
"00"[..]
187 fn is_gnu(&self) -> bool
{
188 let ustar
= unsafe { cast::<_, UstarHeader>(self) }
;
189 ustar
.magic
[..] == b
"ustar "[..] && ustar
.version
[..] == b
" \0"[..]
192 /// View this archive header as a raw "old" archive header.
194 /// This view will always succeed as all archive header formats will fill
195 /// out at least the fields specified in the old header format.
196 pub fn as_old(&self) -> &OldHeader
{
197 unsafe { cast(self) }
200 /// Same as `as_old`, but the mutable version.
201 pub fn as_old_mut(&mut self) -> &mut OldHeader
{
202 unsafe { cast_mut(self) }
205 /// View this archive header as a raw UStar archive header.
207 /// The UStar format is an extension to the tar archive format which enables
208 /// longer pathnames and a few extra attributes such as the group and user
211 /// This cast may not succeed as this function will test whether the
212 /// magic/version fields of the UStar format have the appropriate values,
213 /// returning `None` if they aren't correct.
214 pub fn as_ustar(&self) -> Option
<&UstarHeader
> {
216 Some(unsafe { cast(self) }
)
222 /// Same as `as_ustar_mut`, but the mutable version.
223 pub fn as_ustar_mut(&mut self) -> Option
<&mut UstarHeader
> {
225 Some(unsafe { cast_mut(self) }
)
231 /// View this archive header as a raw GNU archive header.
233 /// The GNU format is an extension to the tar archive format which enables
234 /// longer pathnames and a few extra attributes such as the group and user
237 /// This cast may not succeed as this function will test whether the
238 /// magic/version fields of the GNU format have the appropriate values,
239 /// returning `None` if they aren't correct.
240 pub fn as_gnu(&self) -> Option
<&GnuHeader
> {
242 Some(unsafe { cast(self) }
)
248 /// Same as `as_gnu`, but the mutable version.
249 pub fn as_gnu_mut(&mut self) -> Option
<&mut GnuHeader
> {
251 Some(unsafe { cast_mut(self) }
)
257 /// Treats the given byte slice as a header.
259 /// Panics if the length of the passed slice is not equal to 512.
260 pub fn from_byte_slice(bytes
: &[u8]) -> &Header
{
261 assert_eq
!(bytes
.len(), mem
::size_of
::<Header
>());
262 assert_eq
!(mem
::align_of_val(bytes
), mem
::align_of
::<Header
>());
263 unsafe { &*(bytes.as_ptr() as *const Header) }
266 /// Returns a view into this header as a byte array.
267 pub fn as_bytes(&self) -> &[u8; 512] {
271 /// Returns a view into this header as a byte array.
272 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
276 /// Blanket sets the metadata in this header from the metadata argument
279 /// This is useful for initializing a `Header` from the OS's metadata from a
280 /// file. By default, this will use `HeaderMode::Complete` to include all
282 pub fn set_metadata(&mut self, meta
: &fs
::Metadata
) {
283 self.fill_from(meta
, HeaderMode
::Complete
);
286 /// Sets only the metadata relevant to the given HeaderMode in this header
287 /// from the metadata argument provided.
288 pub fn set_metadata_in_mode(&mut self, meta
: &fs
::Metadata
, mode
: HeaderMode
) {
289 self.fill_from(meta
, mode
);
292 /// Returns the size of entry's data this header represents.
294 /// This is different from `Header::size` for sparse files, which have
295 /// some longer `size()` but shorter `entry_size()`. The `entry_size()`
296 /// listed here should be the number of bytes in the archive this header
299 /// May return an error if the field is corrupted.
300 pub fn entry_size(&self) -> io
::Result
<u64> {
301 num_field_wrapper_from(&self.as_old().size
).map_err(|err
| {
304 format
!("{} when getting size for {}", err
, self.path_lossy()),
309 /// Returns the file size this header represents.
311 /// May return an error if the field is corrupted.
312 pub fn size(&self) -> io
::Result
<u64> {
313 if self.entry_type().is_gnu_sparse() {
315 .ok_or_else(|| other("sparse header was not a gnu header"))
316 .and_then(|h
| h
.real_size())
322 /// Encodes the `size` argument into the size field of this header.
323 pub fn set_size(&mut self, size
: u64) {
324 num_field_wrapper_into(&mut self.as_old_mut().size
, size
);
327 /// Returns the raw path name stored in this header.
329 /// This method may fail if the pathname is not valid Unicode and this is
330 /// called on a Windows platform.
332 /// Note that this function will convert any `\` characters to directory
334 pub fn path(&self) -> io
::Result
<Cow
<Path
>> {
335 bytes2path(self.path_bytes())
338 /// Returns the pathname stored in this header as a byte array.
340 /// This function is guaranteed to succeed, but you may wish to call the
341 /// `path` method to convert to a `Path`.
343 /// Note that this function will convert any `\` characters to directory
345 pub fn path_bytes(&self) -> Cow
<[u8]> {
346 if let Some(ustar
) = self.as_ustar() {
349 let name
= truncate(&self.as_old().name
);
354 /// Gets the path in a "lossy" way, used for error reporting ONLY.
355 fn path_lossy(&self) -> String
{
356 String
::from_utf8_lossy(&self.path_bytes()).to_string()
359 /// Sets the path name for this header.
361 /// This function will set the pathname listed in this header, encoding it
362 /// in the appropriate format. May fail if the path is too long or if the
363 /// path specified is not Unicode and this is a Windows platform. Will
364 /// strip out any "." path component, which signifies the current directory.
366 /// Note: This function does not support names over 100 bytes, or paths
367 /// over 255 bytes, even for formats that support longer names. Instead,
368 /// use `Builder` methods to insert a long-name extension at the same time
369 /// as the file content.
370 pub fn set_path
<P
: AsRef
<Path
>>(&mut self, p
: P
) -> io
::Result
<()> {
371 self._set_path(p
.as_ref())
374 fn _set_path(&mut self, path
: &Path
) -> io
::Result
<()> {
375 if let Some(ustar
) = self.as_ustar_mut() {
376 return ustar
.set_path(path
);
378 copy_path_into(&mut self.as_old_mut().name
, path
, false).map_err(|err
| {
381 format
!("{} when setting path for {}", err
, self.path_lossy()),
386 /// Returns the link name stored in this header, if any is found.
388 /// This method may fail if the pathname is not valid Unicode and this is
389 /// called on a Windows platform. `Ok(None)` being returned, however,
390 /// indicates that the link name was not present.
392 /// Note that this function will convert any `\` characters to directory
394 pub fn link_name(&self) -> io
::Result
<Option
<Cow
<Path
>>> {
395 match self.link_name_bytes() {
396 Some(bytes
) => bytes2path(bytes
).map(Some
),
401 /// Returns the link name stored in this header as a byte array, if any.
403 /// This function is guaranteed to succeed, but you may wish to call the
404 /// `link_name` method to convert to a `Path`.
406 /// Note that this function will convert any `\` characters to directory
408 pub fn link_name_bytes(&self) -> Option
<Cow
<[u8]>> {
409 let old
= self.as_old();
410 if old
.linkname
[0] != 0 {
411 Some(Cow
::Borrowed(truncate(&old
.linkname
)))
417 /// Sets the link name for this header.
419 /// This function will set the linkname listed in this header, encoding it
420 /// in the appropriate format. May fail if the link name is too long or if
421 /// the path specified is not Unicode and this is a Windows platform. Will
422 /// strip out any "." path component, which signifies the current directory.
423 pub fn set_link_name
<P
: AsRef
<Path
>>(&mut self, p
: P
) -> io
::Result
<()> {
424 self._set_link_name(p
.as_ref())
427 fn _set_link_name(&mut self, path
: &Path
) -> io
::Result
<()> {
428 copy_path_into(&mut self.as_old_mut().linkname
, path
, true).map_err(|err
| {
431 format
!("{} when setting link name for {}", err
, self.path_lossy()),
436 /// Returns the mode bits for this file
438 /// May return an error if the field is corrupted.
439 pub fn mode(&self) -> io
::Result
<u32> {
440 octal_from(&self.as_old().mode
)
445 format
!("{} when getting mode for {}", err
, self.path_lossy()),
450 /// Encodes the `mode` provided into this header.
451 pub fn set_mode(&mut self, mode
: u32) {
452 octal_into(&mut self.as_old_mut().mode
, mode
);
455 /// Returns the value of the owner's user ID field
457 /// May return an error if the field is corrupted.
458 pub fn uid(&self) -> io
::Result
<u64> {
459 num_field_wrapper_from(&self.as_old().uid
)
464 format
!("{} when getting uid for {}", err
, self.path_lossy()),
469 /// Encodes the `uid` provided into this header.
470 pub fn set_uid(&mut self, uid
: u64) {
471 num_field_wrapper_into(&mut self.as_old_mut().uid
, uid
);
474 /// Returns the value of the group's user ID field
475 pub fn gid(&self) -> io
::Result
<u64> {
476 num_field_wrapper_from(&self.as_old().gid
)
481 format
!("{} when getting gid for {}", err
, self.path_lossy()),
486 /// Encodes the `gid` provided into this header.
487 pub fn set_gid(&mut self, gid
: u64) {
488 num_field_wrapper_into(&mut self.as_old_mut().gid
, gid
);
491 /// Returns the last modification time in Unix time format
492 pub fn mtime(&self) -> io
::Result
<u64> {
493 num_field_wrapper_from(&self.as_old().mtime
).map_err(|err
| {
496 format
!("{} when getting mtime for {}", err
, self.path_lossy()),
501 /// Encodes the `mtime` provided into this header.
503 /// Note that this time is typically a number of seconds passed since
505 pub fn set_mtime(&mut self, mtime
: u64) {
506 num_field_wrapper_into(&mut self.as_old_mut().mtime
, mtime
);
509 /// Return the user name of the owner of this file.
511 /// A return value of `Ok(Some(..))` indicates that the user name was
512 /// present and was valid utf-8, `Ok(None)` indicates that the user name is
513 /// not present in this archive format, and `Err` indicates that the user
514 /// name was present but was not valid utf-8.
515 pub fn username(&self) -> Result
<Option
<&str>, str::Utf8Error
> {
516 match self.username_bytes() {
517 Some(bytes
) => str::from_utf8(bytes
).map(Some
),
522 /// Returns the user name of the owner of this file, if present.
524 /// A return value of `None` indicates that the user name is not present in
525 /// this header format.
526 pub fn username_bytes(&self) -> Option
<&[u8]> {
527 if let Some(ustar
) = self.as_ustar() {
528 Some(ustar
.username_bytes())
529 } else if let Some(gnu
) = self.as_gnu() {
530 Some(gnu
.username_bytes())
536 /// Sets the username inside this header.
538 /// This function will return an error if this header format cannot encode a
539 /// user name or the name is too long.
540 pub fn set_username(&mut self, name
: &str) -> io
::Result
<()> {
541 if let Some(ustar
) = self.as_ustar_mut() {
542 return ustar
.set_username(name
);
544 if let Some(gnu
) = self.as_gnu_mut() {
545 gnu
.set_username(name
)
547 Err(other("not a ustar or gnu archive, cannot set username"))
551 /// Return the group name of the owner of this file.
553 /// A return value of `Ok(Some(..))` indicates that the group name was
554 /// present and was valid utf-8, `Ok(None)` indicates that the group name is
555 /// not present in this archive format, and `Err` indicates that the group
556 /// name was present but was not valid utf-8.
557 pub fn groupname(&self) -> Result
<Option
<&str>, str::Utf8Error
> {
558 match self.groupname_bytes() {
559 Some(bytes
) => str::from_utf8(bytes
).map(Some
),
564 /// Returns the group name of the owner of this file, if present.
566 /// A return value of `None` indicates that the group name is not present in
567 /// this header format.
568 pub fn groupname_bytes(&self) -> Option
<&[u8]> {
569 if let Some(ustar
) = self.as_ustar() {
570 Some(ustar
.groupname_bytes())
571 } else if let Some(gnu
) = self.as_gnu() {
572 Some(gnu
.groupname_bytes())
578 /// Sets the group name inside this header.
580 /// This function will return an error if this header format cannot encode a
581 /// group name or the name is too long.
582 pub fn set_groupname(&mut self, name
: &str) -> io
::Result
<()> {
583 if let Some(ustar
) = self.as_ustar_mut() {
584 return ustar
.set_groupname(name
);
586 if let Some(gnu
) = self.as_gnu_mut() {
587 gnu
.set_groupname(name
)
589 Err(other("not a ustar or gnu archive, cannot set groupname"))
593 /// Returns the device major number, if present.
595 /// This field may not be present in all archives, and it may not be
596 /// correctly formed in all archives. `Ok(Some(..))` means it was present
597 /// and correctly decoded, `Ok(None)` indicates that this header format does
598 /// not include the device major number, and `Err` indicates that it was
599 /// present and failed to decode.
600 pub fn device_major(&self) -> io
::Result
<Option
<u32>> {
601 if let Some(ustar
) = self.as_ustar() {
602 ustar
.device_major().map(Some
)
603 } else if let Some(gnu
) = self.as_gnu() {
604 gnu
.device_major().map(Some
)
610 /// Encodes the value `major` into the dev_major field of this header.
612 /// This function will return an error if this header format cannot encode a
613 /// major device number.
614 pub fn set_device_major(&mut self, major
: u32) -> io
::Result
<()> {
615 if let Some(ustar
) = self.as_ustar_mut() {
616 ustar
.set_device_major(major
);
618 } else if let Some(gnu
) = self.as_gnu_mut() {
619 gnu
.set_device_major(major
);
622 Err(other("not a ustar or gnu archive, cannot set dev_major"))
626 /// Returns the device minor number, if present.
628 /// This field may not be present in all archives, and it may not be
629 /// correctly formed in all archives. `Ok(Some(..))` means it was present
630 /// and correctly decoded, `Ok(None)` indicates that this header format does
631 /// not include the device minor number, and `Err` indicates that it was
632 /// present and failed to decode.
633 pub fn device_minor(&self) -> io
::Result
<Option
<u32>> {
634 if let Some(ustar
) = self.as_ustar() {
635 ustar
.device_minor().map(Some
)
636 } else if let Some(gnu
) = self.as_gnu() {
637 gnu
.device_minor().map(Some
)
643 /// Encodes the value `minor` into the dev_minor field of this header.
645 /// This function will return an error if this header format cannot encode a
646 /// minor device number.
647 pub fn set_device_minor(&mut self, minor
: u32) -> io
::Result
<()> {
648 if let Some(ustar
) = self.as_ustar_mut() {
649 ustar
.set_device_minor(minor
);
651 } else if let Some(gnu
) = self.as_gnu_mut() {
652 gnu
.set_device_minor(minor
);
655 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
659 /// Returns the type of file described by this header.
660 pub fn entry_type(&self) -> EntryType
{
661 EntryType
::new(self.as_old().linkflag
[0])
664 /// Sets the type of file that will be described by this header.
665 pub fn set_entry_type(&mut self, ty
: EntryType
) {
666 self.as_old_mut().linkflag
= [ty
.as_byte()];
669 /// Returns the checksum field of this header.
671 /// May return an error if the field is corrupted.
672 pub fn cksum(&self) -> io
::Result
<u32> {
673 octal_from(&self.as_old().cksum
)
678 format
!("{} when getting cksum for {}", err
, self.path_lossy()),
683 /// Sets the checksum field of this header based on the current fields in
685 pub fn set_cksum(&mut self) {
686 let cksum
= self.calculate_cksum();
687 octal_into(&mut self.as_old_mut().cksum
, cksum
);
690 fn calculate_cksum(&self) -> u32 {
691 let old
= self.as_old();
692 let start
= old
as *const _
as usize;
693 let cksum_start
= old
.cksum
.as_ptr() as *const _
as usize;
694 let offset
= cksum_start
- start
;
695 let len
= old
.cksum
.len();
696 self.bytes
[0..offset
]
698 .chain(iter
::repeat(&b' '
).take(len
))
699 .chain(&self.bytes
[offset
+ len
..])
700 .fold(0, |a
, b
| a
+ (*b
as u32))
703 fn fill_from(&mut self, meta
: &fs
::Metadata
, mode
: HeaderMode
) {
704 self.fill_platform_from(meta
, mode
);
705 // Set size of directories to zero
706 self.set_size(if meta
.is_dir() || meta
.file_type().is_symlink() {
711 if let Some(ustar
) = self.as_ustar_mut() {
712 ustar
.set_device_major(0);
713 ustar
.set_device_minor(0);
715 if let Some(gnu
) = self.as_gnu_mut() {
716 gnu
.set_device_major(0);
717 gnu
.set_device_minor(0);
721 #[cfg(target_arch = "wasm32")]
722 #[allow(unused_variables)]
723 fn fill_platform_from(&mut self, meta
: &fs
::Metadata
, mode
: HeaderMode
) {
728 fn fill_platform_from(&mut self, meta
: &fs
::Metadata
, mode
: HeaderMode
) {
730 HeaderMode
::Complete
=> {
731 self.set_mtime(meta
.mtime() as u64);
732 self.set_uid(meta
.uid() as u64);
733 self.set_gid(meta
.gid() as u64);
734 self.set_mode(meta
.mode() as u32);
736 HeaderMode
::Deterministic
=> {
737 // We could in theory set the mtime to zero here, but not all
738 // tools seem to behave well when ingesting files with a 0
739 // timestamp. For example rust-lang/cargo#9512 shows that lldb
740 // doesn't ingest files with a zero timestamp correctly.
742 // We just need things to be deterministic here so just pick
743 // something that isn't zero. This time, chosen after careful
744 // deliberation, corresponds to Nov 29, 1973.
745 self.set_mtime(123456789);
750 // Use a default umask value, but propagate the (user) execute bit.
751 let fs_mode
= if meta
.is_dir() || (0o100 & meta
.mode() == 0o100) {
756 self.set_mode(fs_mode
);
760 // Note that if we are a GNU header we *could* set atime/ctime, except
761 // the `tar` utility doesn't do that by default and it causes problems
764 // It's always possible to fill them out manually, so we just don't fill
765 // it out automatically here.
767 // [1]: https://github.com/alexcrichton/tar-rs/issues/70
769 // TODO: need to bind more file types
770 self.set_entry_type(entry_type(meta
.mode()));
772 fn entry_type(mode
: u32) -> EntryType
{
773 match mode
as libc
::mode_t
& libc
::S_IFMT
{
774 libc
::S_IFREG
=> EntryType
::file(),
775 libc
::S_IFLNK
=> EntryType
::symlink(),
776 libc
::S_IFCHR
=> EntryType
::character_special(),
777 libc
::S_IFBLK
=> EntryType
::block_special(),
778 libc
::S_IFDIR
=> EntryType
::dir(),
779 libc
::S_IFIFO
=> EntryType
::fifo(),
780 _
=> EntryType
::new(b' '
),
786 fn fill_platform_from(&mut self, meta
: &fs
::Metadata
, mode
: HeaderMode
) {
787 // There's no concept of a file mode on Windows, so do a best approximation here.
789 HeaderMode
::Complete
=> {
792 // The dates listed in tarballs are always seconds relative to
793 // January 1, 1970. On Windows, however, the timestamps are returned as
794 // dates relative to January 1, 1601 (in 100ns intervals), so we need to
795 // add in some offset for those dates.
796 let mtime
= (meta
.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
797 self.set_mtime(mtime
);
799 const FILE_ATTRIBUTE_READONLY
: u32 = 0x00000001;
800 let readonly
= meta
.file_attributes() & FILE_ATTRIBUTE_READONLY
;
801 match (meta
.is_dir(), readonly
!= 0) {
802 (true, false) => 0o755,
803 (true, true) => 0o555,
804 (false, false) => 0o644,
805 (false, true) => 0o444,
808 self.set_mode(fs_mode
);
810 HeaderMode
::Deterministic
=> {
813 self.set_mtime(123456789); // see above in unix
814 let fs_mode
= if meta
.is_dir() { 0o755 }
else { 0o644 }
;
815 self.set_mode(fs_mode
);
819 let ft
= meta
.file_type();
820 self.set_entry_type(if ft
.is_dir() {
822 } else if ft
.is_file() {
824 } else if ft
.is_symlink() {
831 fn debug_fields(&self, b
: &mut fmt
::DebugStruct
) {
832 if let Ok(entry_size
) = self.entry_size() {
833 b
.field("entry_size", &entry_size
);
835 if let Ok(size
) = self.size() {
836 b
.field("size", &size
);
838 if let Ok(path
) = self.path() {
839 b
.field("path", &path
);
841 if let Ok(link_name
) = self.link_name() {
842 b
.field("link_name", &link_name
);
844 if let Ok(mode
) = self.mode() {
845 b
.field("mode", &DebugAsOctal(mode
));
847 if let Ok(uid
) = self.uid() {
848 b
.field("uid", &uid
);
850 if let Ok(gid
) = self.gid() {
851 b
.field("gid", &gid
);
853 if let Ok(mtime
) = self.mtime() {
854 b
.field("mtime", &mtime
);
856 if let Ok(username
) = self.username() {
857 b
.field("username", &username
);
859 if let Ok(groupname
) = self.groupname() {
860 b
.field("groupname", &groupname
);
862 if let Ok(device_major
) = self.device_major() {
863 b
.field("device_major", &device_major
);
865 if let Ok(device_minor
) = self.device_minor() {
866 b
.field("device_minor", &device_minor
);
868 if let Ok(cksum
) = self.cksum() {
869 b
.field("cksum", &cksum
);
870 b
.field("cksum_valid", &(cksum
== self.calculate_cksum()));
875 struct DebugAsOctal
<T
>(T
);
877 impl<T
: fmt
::Octal
> fmt
::Debug
for DebugAsOctal
<T
> {
878 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
879 fmt
::Octal
::fmt(&self.0, f
)
883 unsafe fn cast
<T
, U
>(a
: &T
) -> &U
{
884 assert_eq
!(mem
::size_of_val(a
), mem
::size_of
::<U
>());
885 assert_eq
!(mem
::align_of_val(a
), mem
::align_of
::<U
>());
886 &*(a
as *const T
as *const U
)
889 unsafe fn cast_mut
<T
, U
>(a
: &mut T
) -> &mut U
{
890 assert_eq
!(mem
::size_of_val(a
), mem
::size_of
::<U
>());
891 assert_eq
!(mem
::align_of_val(a
), mem
::align_of
::<U
>());
892 &mut *(a
as *mut T
as *mut U
)
895 impl Clone
for Header
{
896 fn clone(&self) -> Header
{
897 Header { bytes: self.bytes }
901 impl fmt
::Debug
for Header
{
902 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
903 if let Some(me
) = self.as_ustar() {
905 } else if let Some(me
) = self.as_gnu() {
914 /// Views this as a normal `Header`
915 pub fn as_header(&self) -> &Header
{
916 unsafe { cast(self) }
919 /// Views this as a normal `Header`
920 pub fn as_header_mut(&mut self) -> &mut Header
{
921 unsafe { cast_mut(self) }
925 impl fmt
::Debug
for OldHeader
{
926 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
927 let mut f
= f
.debug_struct("OldHeader");
928 self.as_header().debug_fields(&mut f
);
934 /// See `Header::path_bytes`
935 pub fn path_bytes(&self) -> Cow
<[u8]> {
936 if self.prefix
[0] == 0 && !self.name
.contains(&b'
\\'
) {
937 Cow
::Borrowed(truncate(&self.name
))
939 let mut bytes
= Vec
::new();
940 let prefix
= truncate(&self.prefix
);
941 if !prefix
.is_empty() {
942 bytes
.extend_from_slice(prefix
);
945 bytes
.extend_from_slice(truncate(&self.name
));
950 /// Gets the path in a "lossy" way, used for error reporting ONLY.
951 fn path_lossy(&self) -> String
{
952 String
::from_utf8_lossy(&self.path_bytes()).to_string()
955 /// See `Header::set_path`
956 pub fn set_path
<P
: AsRef
<Path
>>(&mut self, p
: P
) -> io
::Result
<()> {
957 self._set_path(p
.as_ref())
960 fn _set_path(&mut self, path
: &Path
) -> io
::Result
<()> {
961 // This can probably be optimized quite a bit more, but for now just do
962 // something that's relatively easy and readable.
964 // First up, if the path fits within `self.name` then we just shove it
965 // in there. If not then we try to split it between some existing path
966 // components where it can fit in name/prefix. To do that we peel off
967 // enough until the path fits in `prefix`, then we try to put both
968 // halves into their destination.
969 let bytes
= path2bytes(path
)?
;
970 let (maxnamelen
, maxprefixlen
) = (self.name
.len(), self.prefix
.len());
971 if bytes
.len() <= maxnamelen
{
972 copy_path_into(&mut self.name
, path
, false).map_err(|err
| {
975 format
!("{} when setting path for {}", err
, self.path_lossy()),
979 let mut prefix
= path
;
982 match prefix
.parent() {
983 Some(parent
) => prefix
= parent
,
985 return Err(other(&format
!(
986 "path cannot be split to be inserted into archive: {}",
991 prefixlen
= path2bytes(prefix
)?
.len();
992 if prefixlen
<= maxprefixlen
{
996 copy_path_into(&mut self.prefix
, prefix
, false).map_err(|err
| {
999 format
!("{} when setting path for {}", err
, self.path_lossy()),
1002 let path
= bytes2path(Cow
::Borrowed(&bytes
[prefixlen
+ 1..]))?
;
1003 copy_path_into(&mut self.name
, &path
, false).map_err(|err
| {
1006 format
!("{} when setting path for {}", err
, self.path_lossy()),
1013 /// See `Header::username_bytes`
1014 pub fn username_bytes(&self) -> &[u8] {
1015 truncate(&self.uname
)
1018 /// See `Header::set_username`
1019 pub fn set_username(&mut self, name
: &str) -> io
::Result
<()> {
1020 copy_into(&mut self.uname
, name
.as_bytes()).map_err(|err
| {
1023 format
!("{} when setting username for {}", err
, self.path_lossy()),
1028 /// See `Header::groupname_bytes`
1029 pub fn groupname_bytes(&self) -> &[u8] {
1030 truncate(&self.gname
)
1033 /// See `Header::set_groupname`
1034 pub fn set_groupname(&mut self, name
: &str) -> io
::Result
<()> {
1035 copy_into(&mut self.gname
, name
.as_bytes()).map_err(|err
| {
1038 format
!("{} when setting groupname for {}", err
, self.path_lossy()),
1043 /// See `Header::device_major`
1044 pub fn device_major(&self) -> io
::Result
<u32> {
1045 octal_from(&self.dev_major
)
1051 "{} when getting device_major for {}",
1059 /// See `Header::set_device_major`
1060 pub fn set_device_major(&mut self, major
: u32) {
1061 octal_into(&mut self.dev_major
, major
);
1064 /// See `Header::device_minor`
1065 pub fn device_minor(&self) -> io
::Result
<u32> {
1066 octal_from(&self.dev_minor
)
1072 "{} when getting device_minor for {}",
1080 /// See `Header::set_device_minor`
1081 pub fn set_device_minor(&mut self, minor
: u32) {
1082 octal_into(&mut self.dev_minor
, minor
);
1085 /// Views this as a normal `Header`
1086 pub fn as_header(&self) -> &Header
{
1087 unsafe { cast(self) }
1090 /// Views this as a normal `Header`
1091 pub fn as_header_mut(&mut self) -> &mut Header
{
1092 unsafe { cast_mut(self) }
1096 impl fmt
::Debug
for UstarHeader
{
1097 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1098 let mut f
= f
.debug_struct("UstarHeader");
1099 self.as_header().debug_fields(&mut f
);
1105 /// See `Header::username_bytes`
1106 pub fn username_bytes(&self) -> &[u8] {
1107 truncate(&self.uname
)
1110 /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
1111 fn fullname_lossy(&self) -> String
{
1114 String
::from_utf8_lossy(self.groupname_bytes()),
1115 String
::from_utf8_lossy(self.username_bytes()),
1119 /// See `Header::set_username`
1120 pub fn set_username(&mut self, name
: &str) -> io
::Result
<()> {
1121 copy_into(&mut self.uname
, name
.as_bytes()).map_err(|err
| {
1125 "{} when setting username for {}",
1127 self.fullname_lossy()
1133 /// See `Header::groupname_bytes`
1134 pub fn groupname_bytes(&self) -> &[u8] {
1135 truncate(&self.gname
)
1138 /// See `Header::set_groupname`
1139 pub fn set_groupname(&mut self, name
: &str) -> io
::Result
<()> {
1140 copy_into(&mut self.gname
, name
.as_bytes()).map_err(|err
| {
1144 "{} when setting groupname for {}",
1146 self.fullname_lossy()
1152 /// See `Header::device_major`
1153 pub fn device_major(&self) -> io
::Result
<u32> {
1154 octal_from(&self.dev_major
)
1160 "{} when getting device_major for {}",
1162 self.fullname_lossy()
1168 /// See `Header::set_device_major`
1169 pub fn set_device_major(&mut self, major
: u32) {
1170 octal_into(&mut self.dev_major
, major
);
1173 /// See `Header::device_minor`
1174 pub fn device_minor(&self) -> io
::Result
<u32> {
1175 octal_from(&self.dev_minor
)
1181 "{} when getting device_minor for {}",
1183 self.fullname_lossy()
1189 /// See `Header::set_device_minor`
1190 pub fn set_device_minor(&mut self, minor
: u32) {
1191 octal_into(&mut self.dev_minor
, minor
);
1194 /// Returns the last modification time in Unix time format
1195 pub fn atime(&self) -> io
::Result
<u64> {
1196 num_field_wrapper_from(&self.atime
).map_err(|err
| {
1199 format
!("{} when getting atime for {}", err
, self.fullname_lossy()),
1204 /// Encodes the `atime` provided into this header.
1206 /// Note that this time is typically a number of seconds passed since
1207 /// January 1, 1970.
1208 pub fn set_atime(&mut self, atime
: u64) {
1209 num_field_wrapper_into(&mut self.atime
, atime
);
1212 /// Returns the last modification time in Unix time format
1213 pub fn ctime(&self) -> io
::Result
<u64> {
1214 num_field_wrapper_from(&self.ctime
).map_err(|err
| {
1217 format
!("{} when getting ctime for {}", err
, self.fullname_lossy()),
1222 /// Encodes the `ctime` provided into this header.
1224 /// Note that this time is typically a number of seconds passed since
1225 /// January 1, 1970.
1226 pub fn set_ctime(&mut self, ctime
: u64) {
1227 num_field_wrapper_into(&mut self.ctime
, ctime
);
1230 /// Returns the "real size" of the file this header represents.
1232 /// This is applicable for sparse files where the returned size here is the
1233 /// size of the entire file after the sparse regions have been filled in.
1234 pub fn real_size(&self) -> io
::Result
<u64> {
1235 octal_from(&self.realsize
).map_err(|err
| {
1239 "{} when getting real_size for {}",
1241 self.fullname_lossy()
1247 /// Indicates whether this header will be followed by additional
1248 /// sparse-header records.
1250 /// Note that this is handled internally by this library, and is likely only
1251 /// interesting if a `raw` iterator is being used.
1252 pub fn is_extended(&self) -> bool
{
1253 self.isextended
[0] == 1
1256 /// Views this as a normal `Header`
1257 pub fn as_header(&self) -> &Header
{
1258 unsafe { cast(self) }
1261 /// Views this as a normal `Header`
1262 pub fn as_header_mut(&mut self) -> &mut Header
{
1263 unsafe { cast_mut(self) }
1267 impl fmt
::Debug
for GnuHeader
{
1268 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1269 let mut f
= f
.debug_struct("GnuHeader");
1270 self.as_header().debug_fields(&mut f
);
1271 if let Ok(atime
) = self.atime() {
1272 f
.field("atime", &atime
);
1274 if let Ok(ctime
) = self.ctime() {
1275 f
.field("ctime", &ctime
);
1277 f
.field("is_extended", &self.is_extended())
1278 .field("sparse", &DebugSparseHeaders(&self.sparse
))
1283 struct DebugSparseHeaders
<'a
>(&'a
[GnuSparseHeader
]);
1285 impl<'a
> fmt
::Debug
for DebugSparseHeaders
<'a
> {
1286 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1287 let mut f
= f
.debug_list();
1288 for header
in self.0 {
1289 if !header
.is_empty() {
1297 impl GnuSparseHeader
{
1298 /// Returns true if block is empty
1299 pub fn is_empty(&self) -> bool
{
1300 self.offset
[0] == 0 || self.numbytes
[0] == 0
1303 /// Offset of the block from the start of the file
1305 /// Returns `Err` for a malformed `offset` field.
1306 pub fn offset(&self) -> io
::Result
<u64> {
1307 octal_from(&self.offset
).map_err(|err
| {
1310 format
!("{} when getting offset from sparse header", err
),
1315 /// Length of the block
1317 /// Returns `Err` for a malformed `numbytes` field.
1318 pub fn length(&self) -> io
::Result
<u64> {
1319 octal_from(&self.numbytes
).map_err(|err
| {
1322 format
!("{} when getting length from sparse header", err
),
1328 impl fmt
::Debug
for GnuSparseHeader
{
1329 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1330 let mut f
= f
.debug_struct("GnuSparseHeader");
1331 if let Ok(offset
) = self.offset() {
1332 f
.field("offset", &offset
);
1334 if let Ok(length
) = self.length() {
1335 f
.field("length", &length
);
1341 impl GnuExtSparseHeader
{
1342 /// Crates a new zero'd out sparse header entry.
1343 pub fn new() -> GnuExtSparseHeader
{
1344 unsafe { mem::zeroed() }
1347 /// Returns a view into this header as a byte array.
1348 pub fn as_bytes(&self) -> &[u8; 512] {
1349 debug_assert_eq
!(mem
::size_of_val(self), 512);
1350 unsafe { mem::transmute(self) }
1353 /// Returns a view into this header as a byte array.
1354 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1355 debug_assert_eq
!(mem
::size_of_val(self), 512);
1356 unsafe { mem::transmute(self) }
1359 /// Returns a slice of the underlying sparse headers.
1361 /// Some headers may represent empty chunks of both the offset and numbytes
1363 pub fn sparse(&self) -> &[GnuSparseHeader
; 21] {
1367 /// Indicates if another sparse header should be following this one.
1368 pub fn is_extended(&self) -> bool
{
1369 self.isextended
[0] == 1
1373 impl Default
for GnuExtSparseHeader
{
1374 fn default() -> Self {
1379 fn octal_from(slice
: &[u8]) -> io
::Result
<u64> {
1380 let trun
= truncate(slice
);
1381 let num
= match str::from_utf8(trun
) {
1384 return Err(other(&format
!(
1385 "numeric field did not have utf-8 text: {}",
1386 String
::from_utf8_lossy(trun
)
1390 match u64::from_str_radix(num
.trim(), 8) {
1392 Err(_
) => Err(other(&format
!("numeric field was not a number: {}", num
))),
1396 fn octal_into
<T
: fmt
::Octal
>(dst
: &mut [u8], val
: T
) {
1397 let o
= format
!("{:o}", val
);
1398 let value
= o
.bytes().rev().chain(repeat(b'
0'
));
1399 for (slot
, value
) in dst
.iter_mut().rev().skip(1).zip(value
) {
1404 // Wrapper to figure out if we should fill the header field using tar's numeric
1405 // extension (binary) or not (octal).
1406 fn num_field_wrapper_into(dst
: &mut [u8], src
: u64) {
1407 if src
>= 8589934592 || (src
>= 2097152 && dst
.len() == 8) {
1408 numeric_extended_into(dst
, src
);
1410 octal_into(dst
, src
);
1414 // Wrapper to figure out if we should read the header field in binary (numeric
1415 // extension) or octal (standard encoding).
1416 fn num_field_wrapper_from(src
: &[u8]) -> io
::Result
<u64> {
1417 if src
[0] & 0x80 != 0 {
1418 Ok(numeric_extended_from(src
))
1424 // When writing numeric fields with is the extended form, the high bit of the
1425 // first byte is set to 1 and the remainder of the field is treated as binary
1426 // instead of octal ascii.
1427 // This handles writing u64 to 8 (uid, gid) or 12 (size, *time) bytes array.
1428 fn numeric_extended_into(dst
: &mut [u8], src
: u64) {
1429 let len
: usize = dst
.len();
1430 for (slot
, val
) in dst
.iter_mut().zip(
1432 .take(len
- 8) // to zero init extra bytes
1433 .chain((0..8).rev().map(|x
| ((src
>> (8 * x
)) & 0xff) as u8)),
1440 fn numeric_extended_from(src
: &[u8]) -> u64 {
1441 let mut dst
: u64 = 0;
1442 let mut b_to_skip
= 1;
1444 // read first byte without extension flag bit
1445 dst
= (src
[0] ^
0x80) as u64;
1447 // only read last 8 bytes
1448 b_to_skip
= src
.len() - 8;
1450 for byte
in src
.iter().skip(b_to_skip
) {
1452 dst
|= *byte
as u64;
1457 fn truncate(slice
: &[u8]) -> &[u8] {
1458 match slice
.iter().position(|i
| *i
== 0) {
1459 Some(i
) => &slice
[..i
],
1464 /// Copies `bytes` into the `slot` provided, returning an error if the `bytes`
1465 /// array is too long or if it contains any nul bytes.
1466 fn copy_into(slot
: &mut [u8], bytes
: &[u8]) -> io
::Result
<()> {
1467 if bytes
.len() > slot
.len() {
1468 Err(other("provided value is too long"))
1469 } else if bytes
.iter().any(|b
| *b
== 0) {
1470 Err(other("provided value contains a nul byte"))
1472 for (slot
, val
) in slot
.iter_mut().zip(bytes
.iter().chain(Some(&0))) {
1479 /// Copies `path` into the `slot` provided
1481 /// Returns an error if:
1483 /// * the path is too long to fit
1484 /// * a nul byte was found
1485 /// * an invalid path component is encountered (e.g. a root path or parent dir)
1486 /// * the path itself is empty
1487 fn copy_path_into(mut slot
: &mut [u8], path
: &Path
, is_link_name
: bool
) -> io
::Result
<()> {
1488 let mut emitted
= false;
1489 let mut needs_slash
= false;
1490 for component
in path
.components() {
1491 let bytes
= path2bytes(Path
::new(component
.as_os_str()))?
;
1492 match (component
, is_link_name
) {
1493 (Component
::Prefix(..), false) | (Component
::RootDir
, false) => {
1494 return Err(other("paths in archives must be relative"));
1496 (Component
::ParentDir
, false) => {
1497 return Err(other("paths in archives must not have `..`"));
1499 // Allow "./" as the path
1500 (Component
::CurDir
, false) if path
.components().count() == 1 => {}
1501 (Component
::CurDir
, false) => continue,
1502 (Component
::Normal(_
), _
) | (_
, true) => {}
1505 copy(&mut slot
, b
"/")?
;
1507 if bytes
.contains(&b'
/'
) {
1508 if let Component
::Normal(..) = component
{
1509 return Err(other("path component in archive cannot contain `/`"));
1512 copy(&mut slot
, &*bytes
)?
;
1513 if &*bytes
!= b
"/" {
1519 return Err(other("paths in archives must have at least one component"));
1521 if ends_with_slash(path
) {
1522 copy(&mut slot
, &[b'
/'
])?
;
1526 fn copy(slot
: &mut &mut [u8], bytes
: &[u8]) -> io
::Result
<()> {
1527 copy_into(*slot
, bytes
)?
;
1528 let tmp
= mem
::replace(slot
, &mut []);
1529 *slot
= &mut tmp
[bytes
.len()..];
1534 #[cfg(target_arch = "wasm32")]
1535 fn ends_with_slash(p
: &Path
) -> bool
{
1536 p
.to_string_lossy().ends_with('
/'
)
1540 fn ends_with_slash(p
: &Path
) -> bool
{
1541 let last
= p
.as_os_str().encode_wide().last();
1542 last
== Some(b'
/'
as u16) || last
== Some(b'
\\'
as u16)
1546 fn ends_with_slash(p
: &Path
) -> bool
{
1547 p
.as_os_str().as_bytes().ends_with(&[b'
/'
])
1550 #[cfg(any(windows, target_arch = "wasm32"))]
1551 pub fn path2bytes(p
: &Path
) -> io
::Result
<Cow
<[u8]>> {
1554 .map(|s
| s
.as_bytes())
1555 .ok_or_else(|| other(&format
!("path {} was not valid Unicode", p
.display())))
1557 if bytes
.contains(&b'
\\'
) {
1558 // Normalize to Unix-style path separators
1559 let mut bytes
= bytes
.to_owned();
1560 for b
in &mut bytes
{
1567 Cow
::Borrowed(bytes
)
1573 /// On unix this will never fail
1574 pub fn path2bytes(p
: &Path
) -> io
::Result
<Cow
<[u8]>> {
1575 Ok(p
.as_os_str().as_bytes()).map(Cow
::Borrowed
)
1579 /// On windows we cannot accept non-Unicode bytes because it
1580 /// is impossible to convert it to UTF-16.
1581 pub fn bytes2path(bytes
: Cow
<[u8]>) -> io
::Result
<Cow
<Path
>> {
1582 return match bytes
{
1583 Cow
::Borrowed(bytes
) => {
1584 let s
= str::from_utf8(bytes
).map_err(|_
| not_unicode(bytes
))?
;
1585 Ok(Cow
::Borrowed(Path
::new(s
)))
1587 Cow
::Owned(bytes
) => {
1588 let s
= String
::from_utf8(bytes
).map_err(|uerr
| not_unicode(&uerr
.into_bytes()))?
;
1589 Ok(Cow
::Owned(PathBuf
::from(s
)))
1593 fn not_unicode(v
: &[u8]) -> io
::Error
{
1595 "only Unicode paths are supported on Windows: {}",
1596 String
::from_utf8_lossy(v
)
1602 /// On unix this operation can never fail.
1603 pub fn bytes2path(bytes
: Cow
<[u8]>) -> io
::Result
<Cow
<Path
>> {
1604 use std
::ffi
::{OsStr, OsString}
;
1607 Cow
::Borrowed(bytes
) => Cow
::Borrowed(Path
::new(OsStr
::from_bytes(bytes
))),
1608 Cow
::Owned(bytes
) => Cow
::Owned(PathBuf
::from(OsString
::from_vec(bytes
))),
1612 #[cfg(target_arch = "wasm32")]
1613 pub fn bytes2path(bytes
: Cow
<[u8]>) -> io
::Result
<Cow
<Path
>> {
1615 Cow
::Borrowed(bytes
) => {
1616 Cow
::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) }
)
1618 Cow
::Owned(bytes
) => {
1619 Cow
::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) }
)
1624 #[cfg(target_arch = "wasm32")]
1625 fn invalid_utf8
<T
>(_
: T
) -> io
::Error
{
1626 io
::Error
::new(io
::ErrorKind
::InvalidData
, "Invalid utf-8")