1 #[cfg(unix)]
2 use std::os::unix::prelude::*;
3 #[cfg(windows)]
4 use std::os::windows::prelude::*;
5 
6 use std::borrow::Cow;
7 use std::fmt;
8 use std::fs;
9 use std::io;
10 use std::iter;
11 use std::iter::repeat;
12 use std::mem;
13 use std::path::{Component, Path, PathBuf};
14 use std::str;
15 
16 use crate::other;
17 use crate::EntryType;
18 
19 /// Representation of the header of an entry in an archive
20 #[repr(C)]
21 #[allow(missing_docs)]
22 pub struct Header {
23     bytes: [u8; 512],
24 }
25 
26 /// Declares the information that should be included when filling a Header
27 /// from filesystem metadata.
28 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
29 #[non_exhaustive]
30 pub enum HeaderMode {
31     /// All supported metadata, including mod/access times and ownership will
32     /// be included.
33     Complete,
34 
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.
37     Deterministic,
38 }
39 
40 /// Representation of the header of an entry in an archive
41 #[repr(C)]
42 #[allow(missing_docs)]
43 pub struct OldHeader {
44     pub name: [u8; 100],
45     pub mode: [u8; 8],
46     pub uid: [u8; 8],
47     pub gid: [u8; 8],
48     pub size: [u8; 12],
49     pub mtime: [u8; 12],
50     pub cksum: [u8; 8],
51     pub linkflag: [u8; 1],
52     pub linkname: [u8; 100],
53     pub pad: [u8; 255],
54 }
55 
56 /// Representation of the header of an entry in an archive
57 #[repr(C)]
58 #[allow(missing_docs)]
59 pub struct UstarHeader {
60     pub name: [u8; 100],
61     pub mode: [u8; 8],
62     pub uid: [u8; 8],
63     pub gid: [u8; 8],
64     pub size: [u8; 12],
65     pub mtime: [u8; 12],
66     pub cksum: [u8; 8],
67     pub typeflag: [u8; 1],
68     pub linkname: [u8; 100],
69 
70     // UStar format
71     pub magic: [u8; 6],
72     pub version: [u8; 2],
73     pub uname: [u8; 32],
74     pub gname: [u8; 32],
75     pub dev_major: [u8; 8],
76     pub dev_minor: [u8; 8],
77     pub prefix: [u8; 155],
78     pub pad: [u8; 12],
79 }
80 
81 /// Representation of the header of an entry in an archive
82 #[repr(C)]
83 #[allow(missing_docs)]
84 pub struct GnuHeader {
85     pub name: [u8; 100],
86     pub mode: [u8; 8],
87     pub uid: [u8; 8],
88     pub gid: [u8; 8],
89     pub size: [u8; 12],
90     pub mtime: [u8; 12],
91     pub cksum: [u8; 8],
92     pub typeflag: [u8; 1],
93     pub linkname: [u8; 100],
94 
95     // GNU format
96     pub magic: [u8; 6],
97     pub version: [u8; 2],
98     pub uname: [u8; 32],
99     pub gname: [u8; 32],
100     pub dev_major: [u8; 8],
101     pub dev_minor: [u8; 8],
102     pub atime: [u8; 12],
103     pub ctime: [u8; 12],
104     pub offset: [u8; 12],
105     pub longnames: [u8; 4],
106     pub unused: [u8; 1],
107     pub sparse: [GnuSparseHeader; 4],
108     pub isextended: [u8; 1],
109     pub realsize: [u8; 12],
110     pub pad: [u8; 17],
111 }
112 
113 /// Description of the header of a spare entry.
114 ///
115 /// Specifies the offset/number of bytes of a chunk of data in octal.
116 #[repr(C)]
117 #[allow(missing_docs)]
118 pub struct GnuSparseHeader {
119     pub offset: [u8; 12],
120     pub numbytes: [u8; 12],
121 }
122 
123 /// Representation of the entry found to represent extended GNU sparse files.
124 ///
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.
127 #[repr(C)]
128 #[allow(missing_docs)]
129 pub struct GnuExtSparseHeader {
130     pub sparse: [GnuSparseHeader; 21],
131     pub isextended: [u8; 1],
132     pub padding: [u8; 7],
133 }
134 
135 impl Header {
136     /// Creates a new blank GNU header.
137     ///
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.
new_gnu() -> Header141     pub fn new_gnu() -> Header {
142         let mut header = Header { bytes: [0; 512] };
143         unsafe {
144             let gnu = cast_mut::<_, GnuHeader>(&mut header);
145             gnu.magic = *b"ustar ";
146             gnu.version = *b" \0";
147         }
148         header.set_mtime(0);
149         header
150     }
151 
152     /// Creates a new blank UStar header.
153     ///
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.
157     ///
158     /// UStar is also the basis used for pax archives.
new_ustar() -> Header159     pub fn new_ustar() -> Header {
160         let mut header = Header { bytes: [0; 512] };
161         unsafe {
162             let gnu = cast_mut::<_, UstarHeader>(&mut header);
163             gnu.magic = *b"ustar\0";
164             gnu.version = *b"00";
165         }
166         header.set_mtime(0);
167         header
168     }
169 
170     /// Creates a new blank old header.
171     ///
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.
new_old() -> Header176     pub fn new_old() -> Header {
177         let mut header = Header { bytes: [0; 512] };
178         header.set_mtime(0);
179         header
180     }
181 
is_ustar(&self) -> bool182     fn is_ustar(&self) -> bool {
183         let ustar = unsafe { cast::<_, UstarHeader>(self) };
184         ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
185     }
186 
is_gnu(&self) -> bool187     fn is_gnu(&self) -> bool {
188         let ustar = unsafe { cast::<_, UstarHeader>(self) };
189         ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
190     }
191 
192     /// View this archive header as a raw "old" archive header.
193     ///
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.
as_old(&self) -> &OldHeader196     pub fn as_old(&self) -> &OldHeader {
197         unsafe { cast(self) }
198     }
199 
200     /// Same as `as_old`, but the mutable version.
as_old_mut(&mut self) -> &mut OldHeader201     pub fn as_old_mut(&mut self) -> &mut OldHeader {
202         unsafe { cast_mut(self) }
203     }
204 
205     /// View this archive header as a raw UStar archive header.
206     ///
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
209     /// name.
210     ///
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.
as_ustar(&self) -> Option<&UstarHeader>214     pub fn as_ustar(&self) -> Option<&UstarHeader> {
215         if self.is_ustar() {
216             Some(unsafe { cast(self) })
217         } else {
218             None
219         }
220     }
221 
222     /// Same as `as_ustar_mut`, but the mutable version.
as_ustar_mut(&mut self) -> Option<&mut UstarHeader>223     pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
224         if self.is_ustar() {
225             Some(unsafe { cast_mut(self) })
226         } else {
227             None
228         }
229     }
230 
231     /// View this archive header as a raw GNU archive header.
232     ///
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
235     /// name.
236     ///
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.
as_gnu(&self) -> Option<&GnuHeader>240     pub fn as_gnu(&self) -> Option<&GnuHeader> {
241         if self.is_gnu() {
242             Some(unsafe { cast(self) })
243         } else {
244             None
245         }
246     }
247 
248     /// Same as `as_gnu`, but the mutable version.
as_gnu_mut(&mut self) -> Option<&mut GnuHeader>249     pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
250         if self.is_gnu() {
251             Some(unsafe { cast_mut(self) })
252         } else {
253             None
254         }
255     }
256 
257     /// Treats the given byte slice as a header.
258     ///
259     /// Panics if the length of the passed slice is not equal to 512.
from_byte_slice(bytes: &[u8]) -> &Header260     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) }
264     }
265 
266     /// Returns a view into this header as a byte array.
as_bytes(&self) -> &[u8; 512]267     pub fn as_bytes(&self) -> &[u8; 512] {
268         &self.bytes
269     }
270 
271     /// Returns a view into this header as a byte array.
as_mut_bytes(&mut self) -> &mut [u8; 512]272     pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
273         &mut self.bytes
274     }
275 
276     /// Blanket sets the metadata in this header from the metadata argument
277     /// provided.
278     ///
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
281     /// metadata.
set_metadata(&mut self, meta: &fs::Metadata)282     pub fn set_metadata(&mut self, meta: &fs::Metadata) {
283         self.fill_from(meta, HeaderMode::Complete);
284     }
285 
286     /// Sets only the metadata relevant to the given HeaderMode in this header
287     /// from the metadata argument provided.
set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode)288     pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
289         self.fill_from(meta, mode);
290     }
291 
292     /// Returns the size of entry's data this header represents.
293     ///
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
297     /// describes.
298     ///
299     /// May return an error if the field is corrupted.
entry_size(&self) -> io::Result<u64>300     pub fn entry_size(&self) -> io::Result<u64> {
301         num_field_wrapper_from(&self.as_old().size).map_err(|err| {
302             io::Error::new(
303                 err.kind(),
304                 format!("{} when getting size for {}", err, self.path_lossy()),
305             )
306         })
307     }
308 
309     /// Returns the file size this header represents.
310     ///
311     /// May return an error if the field is corrupted.
size(&self) -> io::Result<u64>312     pub fn size(&self) -> io::Result<u64> {
313         if self.entry_type().is_gnu_sparse() {
314             self.as_gnu()
315                 .ok_or_else(|| other("sparse header was not a gnu header"))
316                 .and_then(|h| h.real_size())
317         } else {
318             self.entry_size()
319         }
320     }
321 
322     /// Encodes the `size` argument into the size field of this header.
set_size(&mut self, size: u64)323     pub fn set_size(&mut self, size: u64) {
324         num_field_wrapper_into(&mut self.as_old_mut().size, size);
325     }
326 
327     /// Returns the raw path name stored in this header.
328     ///
329     /// This method may fail if the pathname is not valid Unicode and this is
330     /// called on a Windows platform.
331     ///
332     /// Note that this function will convert any `\` characters to directory
333     /// separators.
path(&self) -> io::Result<Cow<Path>>334     pub fn path(&self) -> io::Result<Cow<Path>> {
335         bytes2path(self.path_bytes())
336     }
337 
338     /// Returns the pathname stored in this header as a byte array.
339     ///
340     /// This function is guaranteed to succeed, but you may wish to call the
341     /// `path` method to convert to a `Path`.
342     ///
343     /// Note that this function will convert any `\` characters to directory
344     /// separators.
path_bytes(&self) -> Cow<[u8]>345     pub fn path_bytes(&self) -> Cow<[u8]> {
346         if let Some(ustar) = self.as_ustar() {
347             ustar.path_bytes()
348         } else {
349             let name = truncate(&self.as_old().name);
350             Cow::Borrowed(name)
351         }
352     }
353 
354     /// Gets the path in a "lossy" way, used for error reporting ONLY.
path_lossy(&self) -> String355     fn path_lossy(&self) -> String {
356         String::from_utf8_lossy(&self.path_bytes()).to_string()
357     }
358 
359     /// Sets the path name for this header.
360     ///
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.
365     ///
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.
set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>370     pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
371         self._set_path(p.as_ref())
372     }
373 
_set_path(&mut self, path: &Path) -> io::Result<()>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);
377         }
378         copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
379             io::Error::new(
380                 err.kind(),
381                 format!("{} when setting path for {}", err, self.path_lossy()),
382             )
383         })
384     }
385 
386     /// Returns the link name stored in this header, if any is found.
387     ///
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.
391     ///
392     /// Note that this function will convert any `\` characters to directory
393     /// separators.
link_name(&self) -> io::Result<Option<Cow<Path>>>394     pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
395         match self.link_name_bytes() {
396             Some(bytes) => bytes2path(bytes).map(Some),
397             None => Ok(None),
398         }
399     }
400 
401     /// Returns the link name stored in this header as a byte array, if any.
402     ///
403     /// This function is guaranteed to succeed, but you may wish to call the
404     /// `link_name` method to convert to a `Path`.
405     ///
406     /// Note that this function will convert any `\` characters to directory
407     /// separators.
link_name_bytes(&self) -> Option<Cow<[u8]>>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)))
412         } else {
413             None
414         }
415     }
416 
417     /// Sets the link name for this header.
418     ///
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.
set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>423     pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
424         self._set_link_name(p.as_ref())
425     }
426 
_set_link_name(&mut self, path: &Path) -> io::Result<()>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| {
429             io::Error::new(
430                 err.kind(),
431                 format!("{} when setting link name for {}", err, self.path_lossy()),
432             )
433         })
434     }
435 
436     /// Returns the mode bits for this file
437     ///
438     /// May return an error if the field is corrupted.
mode(&self) -> io::Result<u32>439     pub fn mode(&self) -> io::Result<u32> {
440         octal_from(&self.as_old().mode)
441             .map(|u| u as u32)
442             .map_err(|err| {
443                 io::Error::new(
444                     err.kind(),
445                     format!("{} when getting mode for {}", err, self.path_lossy()),
446                 )
447             })
448     }
449 
450     /// Encodes the `mode` provided into this header.
set_mode(&mut self, mode: u32)451     pub fn set_mode(&mut self, mode: u32) {
452         octal_into(&mut self.as_old_mut().mode, mode);
453     }
454 
455     /// Returns the value of the owner's user ID field
456     ///
457     /// May return an error if the field is corrupted.
uid(&self) -> io::Result<u64>458     pub fn uid(&self) -> io::Result<u64> {
459         num_field_wrapper_from(&self.as_old().uid)
460             .map(|u| u as u64)
461             .map_err(|err| {
462                 io::Error::new(
463                     err.kind(),
464                     format!("{} when getting uid for {}", err, self.path_lossy()),
465                 )
466             })
467     }
468 
469     /// Encodes the `uid` provided into this header.
set_uid(&mut self, uid: u64)470     pub fn set_uid(&mut self, uid: u64) {
471         num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
472     }
473 
474     /// Returns the value of the group's user ID field
gid(&self) -> io::Result<u64>475     pub fn gid(&self) -> io::Result<u64> {
476         num_field_wrapper_from(&self.as_old().gid)
477             .map(|u| u as u64)
478             .map_err(|err| {
479                 io::Error::new(
480                     err.kind(),
481                     format!("{} when getting gid for {}", err, self.path_lossy()),
482                 )
483             })
484     }
485 
486     /// Encodes the `gid` provided into this header.
set_gid(&mut self, gid: u64)487     pub fn set_gid(&mut self, gid: u64) {
488         num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
489     }
490 
491     /// Returns the last modification time in Unix time format
mtime(&self) -> io::Result<u64>492     pub fn mtime(&self) -> io::Result<u64> {
493         num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
494             io::Error::new(
495                 err.kind(),
496                 format!("{} when getting mtime for {}", err, self.path_lossy()),
497             )
498         })
499     }
500 
501     /// Encodes the `mtime` provided into this header.
502     ///
503     /// Note that this time is typically a number of seconds passed since
504     /// January 1, 1970.
set_mtime(&mut self, mtime: u64)505     pub fn set_mtime(&mut self, mtime: u64) {
506         num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
507     }
508 
509     /// Return the user name of the owner of this file.
510     ///
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.
username(&self) -> Result<Option<&str>, str::Utf8Error>515     pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
516         match self.username_bytes() {
517             Some(bytes) => str::from_utf8(bytes).map(Some),
518             None => Ok(None),
519         }
520     }
521 
522     /// Returns the user name of the owner of this file, if present.
523     ///
524     /// A return value of `None` indicates that the user name is not present in
525     /// this header format.
username_bytes(&self) -> Option<&[u8]>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())
531         } else {
532             None
533         }
534     }
535 
536     /// Sets the username inside this header.
537     ///
538     /// This function will return an error if this header format cannot encode a
539     /// user name or the name is too long.
set_username(&mut self, name: &str) -> io::Result<()>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);
543         }
544         if let Some(gnu) = self.as_gnu_mut() {
545             gnu.set_username(name)
546         } else {
547             Err(other("not a ustar or gnu archive, cannot set username"))
548         }
549     }
550 
551     /// Return the group name of the owner of this file.
552     ///
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.
groupname(&self) -> Result<Option<&str>, str::Utf8Error>557     pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
558         match self.groupname_bytes() {
559             Some(bytes) => str::from_utf8(bytes).map(Some),
560             None => Ok(None),
561         }
562     }
563 
564     /// Returns the group name of the owner of this file, if present.
565     ///
566     /// A return value of `None` indicates that the group name is not present in
567     /// this header format.
groupname_bytes(&self) -> Option<&[u8]>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())
573         } else {
574             None
575         }
576     }
577 
578     /// Sets the group name inside this header.
579     ///
580     /// This function will return an error if this header format cannot encode a
581     /// group name or the name is too long.
set_groupname(&mut self, name: &str) -> io::Result<()>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);
585         }
586         if let Some(gnu) = self.as_gnu_mut() {
587             gnu.set_groupname(name)
588         } else {
589             Err(other("not a ustar or gnu archive, cannot set groupname"))
590         }
591     }
592 
593     /// Returns the device major number, if present.
594     ///
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.
device_major(&self) -> io::Result<Option<u32>>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)
605         } else {
606             Ok(None)
607         }
608     }
609 
610     /// Encodes the value `major` into the dev_major field of this header.
611     ///
612     /// This function will return an error if this header format cannot encode a
613     /// major device number.
set_device_major(&mut self, major: u32) -> io::Result<()>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);
617             Ok(())
618         } else if let Some(gnu) = self.as_gnu_mut() {
619             gnu.set_device_major(major);
620             Ok(())
621         } else {
622             Err(other("not a ustar or gnu archive, cannot set dev_major"))
623         }
624     }
625 
626     /// Returns the device minor number, if present.
627     ///
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.
device_minor(&self) -> io::Result<Option<u32>>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)
638         } else {
639             Ok(None)
640         }
641     }
642 
643     /// Encodes the value `minor` into the dev_minor field of this header.
644     ///
645     /// This function will return an error if this header format cannot encode a
646     /// minor device number.
set_device_minor(&mut self, minor: u32) -> io::Result<()>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);
650             Ok(())
651         } else if let Some(gnu) = self.as_gnu_mut() {
652             gnu.set_device_minor(minor);
653             Ok(())
654         } else {
655             Err(other("not a ustar or gnu archive, cannot set dev_minor"))
656         }
657     }
658 
659     /// Returns the type of file described by this header.
entry_type(&self) -> EntryType660     pub fn entry_type(&self) -> EntryType {
661         EntryType::new(self.as_old().linkflag[0])
662     }
663 
664     /// Sets the type of file that will be described by this header.
set_entry_type(&mut self, ty: EntryType)665     pub fn set_entry_type(&mut self, ty: EntryType) {
666         self.as_old_mut().linkflag = [ty.as_byte()];
667     }
668 
669     /// Returns the checksum field of this header.
670     ///
671     /// May return an error if the field is corrupted.
cksum(&self) -> io::Result<u32>672     pub fn cksum(&self) -> io::Result<u32> {
673         octal_from(&self.as_old().cksum)
674             .map(|u| u as u32)
675             .map_err(|err| {
676                 io::Error::new(
677                     err.kind(),
678                     format!("{} when getting cksum for {}", err, self.path_lossy()),
679                 )
680             })
681     }
682 
683     /// Sets the checksum field of this header based on the current fields in
684     /// this header.
set_cksum(&mut self)685     pub fn set_cksum(&mut self) {
686         let cksum = self.calculate_cksum();
687         octal_into(&mut self.as_old_mut().cksum, cksum);
688     }
689 
calculate_cksum(&self) -> u32690     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]
697             .iter()
698             .chain(iter::repeat(&b' ').take(len))
699             .chain(&self.bytes[offset + len..])
700             .fold(0, |a, b| a + (*b as u32))
701     }
702 
fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)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() {
707             0
708         } else {
709             meta.len()
710         });
711         if let Some(ustar) = self.as_ustar_mut() {
712             ustar.set_device_major(0);
713             ustar.set_device_minor(0);
714         }
715         if let Some(gnu) = self.as_gnu_mut() {
716             gnu.set_device_major(0);
717             gnu.set_device_minor(0);
718         }
719     }
720 
721     #[cfg(target_arch = "wasm32")]
722     #[allow(unused_variables)]
fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)723     fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
724         unimplemented!();
725     }
726 
727     #[cfg(unix)]
fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)728     fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
729         match mode {
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);
735             }
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.
741                 //
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);
746 
747                 self.set_uid(0);
748                 self.set_gid(0);
749 
750                 // Use a default umask value, but propagate the (user) execute bit.
751                 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
752                     0o755
753                 } else {
754                     0o644
755                 };
756                 self.set_mode(fs_mode);
757             }
758         }
759 
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
762         // with 7-zip [1].
763         //
764         // It's always possible to fill them out manually, so we just don't fill
765         // it out automatically here.
766         //
767         // [1]: https://github.com/alexcrichton/tar-rs/issues/70
768 
769         // TODO: need to bind more file types
770         self.set_entry_type(entry_type(meta.mode()));
771 
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' '),
781             }
782         }
783     }
784 
785     #[cfg(windows)]
fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)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.
788         match mode {
789             HeaderMode::Complete => {
790                 self.set_uid(0);
791                 self.set_gid(0);
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);
798                 let fs_mode = {
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,
806                     }
807                 };
808                 self.set_mode(fs_mode);
809             }
810             HeaderMode::Deterministic => {
811                 self.set_uid(0);
812                 self.set_gid(0);
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);
816             }
817         }
818 
819         let ft = meta.file_type();
820         self.set_entry_type(if ft.is_dir() {
821             EntryType::dir()
822         } else if ft.is_file() {
823             EntryType::file()
824         } else if ft.is_symlink() {
825             EntryType::symlink()
826         } else {
827             EntryType::new(b' ')
828         });
829     }
830 
debug_fields(&self, b: &mut fmt::DebugStruct)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);
834         }
835         if let Ok(size) = self.size() {
836             b.field("size", &size);
837         }
838         if let Ok(path) = self.path() {
839             b.field("path", &path);
840         }
841         if let Ok(link_name) = self.link_name() {
842             b.field("link_name", &link_name);
843         }
844         if let Ok(mode) = self.mode() {
845             b.field("mode", &DebugAsOctal(mode));
846         }
847         if let Ok(uid) = self.uid() {
848             b.field("uid", &uid);
849         }
850         if let Ok(gid) = self.gid() {
851             b.field("gid", &gid);
852         }
853         if let Ok(mtime) = self.mtime() {
854             b.field("mtime", &mtime);
855         }
856         if let Ok(username) = self.username() {
857             b.field("username", &username);
858         }
859         if let Ok(groupname) = self.groupname() {
860             b.field("groupname", &groupname);
861         }
862         if let Ok(device_major) = self.device_major() {
863             b.field("device_major", &device_major);
864         }
865         if let Ok(device_minor) = self.device_minor() {
866             b.field("device_minor", &device_minor);
867         }
868         if let Ok(cksum) = self.cksum() {
869             b.field("cksum", &cksum);
870             b.field("cksum_valid", &(cksum == self.calculate_cksum()));
871         }
872     }
873 }
874 
875 struct DebugAsOctal<T>(T);
876 
877 impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result878     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
879         fmt::Octal::fmt(&self.0, f)
880     }
881 }
882 
cast<T, U>(a: &T) -> &U883 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)
887 }
888 
cast_mut<T, U>(a: &mut T) -> &mut U889 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)
893 }
894 
895 impl Clone for Header {
clone(&self) -> Header896     fn clone(&self) -> Header {
897         Header { bytes: self.bytes }
898     }
899 }
900 
901 impl fmt::Debug for Header {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result902     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
903         if let Some(me) = self.as_ustar() {
904             me.fmt(f)
905         } else if let Some(me) = self.as_gnu() {
906             me.fmt(f)
907         } else {
908             self.as_old().fmt(f)
909         }
910     }
911 }
912 
913 impl OldHeader {
914     /// Views this as a normal `Header`
as_header(&self) -> &Header915     pub fn as_header(&self) -> &Header {
916         unsafe { cast(self) }
917     }
918 
919     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header920     pub fn as_header_mut(&mut self) -> &mut Header {
921         unsafe { cast_mut(self) }
922     }
923 }
924 
925 impl fmt::Debug for OldHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result926     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);
929         f.finish()
930     }
931 }
932 
933 impl UstarHeader {
934     /// See `Header::path_bytes`
path_bytes(&self) -> Cow<[u8]>935     pub fn path_bytes(&self) -> Cow<[u8]> {
936         if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
937             Cow::Borrowed(truncate(&self.name))
938         } else {
939             let mut bytes = Vec::new();
940             let prefix = truncate(&self.prefix);
941             if !prefix.is_empty() {
942                 bytes.extend_from_slice(prefix);
943                 bytes.push(b'/');
944             }
945             bytes.extend_from_slice(truncate(&self.name));
946             Cow::Owned(bytes)
947         }
948     }
949 
950     /// Gets the path in a "lossy" way, used for error reporting ONLY.
path_lossy(&self) -> String951     fn path_lossy(&self) -> String {
952         String::from_utf8_lossy(&self.path_bytes()).to_string()
953     }
954 
955     /// See `Header::set_path`
set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>956     pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
957         self._set_path(p.as_ref())
958     }
959 
_set_path(&mut self, path: &Path) -> io::Result<()>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.
963         //
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| {
973                 io::Error::new(
974                     err.kind(),
975                     format!("{} when setting path for {}", err, self.path_lossy()),
976                 )
977             })?;
978         } else {
979             let mut prefix = path;
980             let mut prefixlen;
981             loop {
982                 match prefix.parent() {
983                     Some(parent) => prefix = parent,
984                     None => {
985                         return Err(other(&format!(
986                             "path cannot be split to be inserted into archive: {}",
987                             path.display()
988                         )));
989                     }
990                 }
991                 prefixlen = path2bytes(prefix)?.len();
992                 if prefixlen <= maxprefixlen {
993                     break;
994                 }
995             }
996             copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
997                 io::Error::new(
998                     err.kind(),
999                     format!("{} when setting path for {}", err, self.path_lossy()),
1000                 )
1001             })?;
1002             let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1003             copy_path_into(&mut self.name, &path, false).map_err(|err| {
1004                 io::Error::new(
1005                     err.kind(),
1006                     format!("{} when setting path for {}", err, self.path_lossy()),
1007                 )
1008             })?;
1009         }
1010         Ok(())
1011     }
1012 
1013     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1014     pub fn username_bytes(&self) -> &[u8] {
1015         truncate(&self.uname)
1016     }
1017 
1018     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1019     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1020         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1021             io::Error::new(
1022                 err.kind(),
1023                 format!("{} when setting username for {}", err, self.path_lossy()),
1024             )
1025         })
1026     }
1027 
1028     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1029     pub fn groupname_bytes(&self) -> &[u8] {
1030         truncate(&self.gname)
1031     }
1032 
1033     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1034     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1035         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1036             io::Error::new(
1037                 err.kind(),
1038                 format!("{} when setting groupname for {}", err, self.path_lossy()),
1039             )
1040         })
1041     }
1042 
1043     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1044     pub fn device_major(&self) -> io::Result<u32> {
1045         octal_from(&self.dev_major)
1046             .map(|u| u as u32)
1047             .map_err(|err| {
1048                 io::Error::new(
1049                     err.kind(),
1050                     format!(
1051                         "{} when getting device_major for {}",
1052                         err,
1053                         self.path_lossy()
1054                     ),
1055                 )
1056             })
1057     }
1058 
1059     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1060     pub fn set_device_major(&mut self, major: u32) {
1061         octal_into(&mut self.dev_major, major);
1062     }
1063 
1064     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1065     pub fn device_minor(&self) -> io::Result<u32> {
1066         octal_from(&self.dev_minor)
1067             .map(|u| u as u32)
1068             .map_err(|err| {
1069                 io::Error::new(
1070                     err.kind(),
1071                     format!(
1072                         "{} when getting device_minor for {}",
1073                         err,
1074                         self.path_lossy()
1075                     ),
1076                 )
1077             })
1078     }
1079 
1080     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1081     pub fn set_device_minor(&mut self, minor: u32) {
1082         octal_into(&mut self.dev_minor, minor);
1083     }
1084 
1085     /// Views this as a normal `Header`
as_header(&self) -> &Header1086     pub fn as_header(&self) -> &Header {
1087         unsafe { cast(self) }
1088     }
1089 
1090     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1091     pub fn as_header_mut(&mut self) -> &mut Header {
1092         unsafe { cast_mut(self) }
1093     }
1094 }
1095 
1096 impl fmt::Debug for UstarHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1097     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);
1100         f.finish()
1101     }
1102 }
1103 
1104 impl GnuHeader {
1105     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1106     pub fn username_bytes(&self) -> &[u8] {
1107         truncate(&self.uname)
1108     }
1109 
1110     /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
fullname_lossy(&self) -> String1111     fn fullname_lossy(&self) -> String {
1112         format!(
1113             "{}:{}",
1114             String::from_utf8_lossy(self.groupname_bytes()),
1115             String::from_utf8_lossy(self.username_bytes()),
1116         )
1117     }
1118 
1119     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1120     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1121         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1122             io::Error::new(
1123                 err.kind(),
1124                 format!(
1125                     "{} when setting username for {}",
1126                     err,
1127                     self.fullname_lossy()
1128                 ),
1129             )
1130         })
1131     }
1132 
1133     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1134     pub fn groupname_bytes(&self) -> &[u8] {
1135         truncate(&self.gname)
1136     }
1137 
1138     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1139     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1140         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1141             io::Error::new(
1142                 err.kind(),
1143                 format!(
1144                     "{} when setting groupname for {}",
1145                     err,
1146                     self.fullname_lossy()
1147                 ),
1148             )
1149         })
1150     }
1151 
1152     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1153     pub fn device_major(&self) -> io::Result<u32> {
1154         octal_from(&self.dev_major)
1155             .map(|u| u as u32)
1156             .map_err(|err| {
1157                 io::Error::new(
1158                     err.kind(),
1159                     format!(
1160                         "{} when getting device_major for {}",
1161                         err,
1162                         self.fullname_lossy()
1163                     ),
1164                 )
1165             })
1166     }
1167 
1168     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1169     pub fn set_device_major(&mut self, major: u32) {
1170         octal_into(&mut self.dev_major, major);
1171     }
1172 
1173     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1174     pub fn device_minor(&self) -> io::Result<u32> {
1175         octal_from(&self.dev_minor)
1176             .map(|u| u as u32)
1177             .map_err(|err| {
1178                 io::Error::new(
1179                     err.kind(),
1180                     format!(
1181                         "{} when getting device_minor for {}",
1182                         err,
1183                         self.fullname_lossy()
1184                     ),
1185                 )
1186             })
1187     }
1188 
1189     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1190     pub fn set_device_minor(&mut self, minor: u32) {
1191         octal_into(&mut self.dev_minor, minor);
1192     }
1193 
1194     /// Returns the last modification time in Unix time format
atime(&self) -> io::Result<u64>1195     pub fn atime(&self) -> io::Result<u64> {
1196         num_field_wrapper_from(&self.atime).map_err(|err| {
1197             io::Error::new(
1198                 err.kind(),
1199                 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1200             )
1201         })
1202     }
1203 
1204     /// Encodes the `atime` provided into this header.
1205     ///
1206     /// Note that this time is typically a number of seconds passed since
1207     /// January 1, 1970.
set_atime(&mut self, atime: u64)1208     pub fn set_atime(&mut self, atime: u64) {
1209         num_field_wrapper_into(&mut self.atime, atime);
1210     }
1211 
1212     /// Returns the last modification time in Unix time format
ctime(&self) -> io::Result<u64>1213     pub fn ctime(&self) -> io::Result<u64> {
1214         num_field_wrapper_from(&self.ctime).map_err(|err| {
1215             io::Error::new(
1216                 err.kind(),
1217                 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1218             )
1219         })
1220     }
1221 
1222     /// Encodes the `ctime` provided into this header.
1223     ///
1224     /// Note that this time is typically a number of seconds passed since
1225     /// January 1, 1970.
set_ctime(&mut self, ctime: u64)1226     pub fn set_ctime(&mut self, ctime: u64) {
1227         num_field_wrapper_into(&mut self.ctime, ctime);
1228     }
1229 
1230     /// Returns the "real size" of the file this header represents.
1231     ///
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.
real_size(&self) -> io::Result<u64>1234     pub fn real_size(&self) -> io::Result<u64> {
1235         octal_from(&self.realsize).map_err(|err| {
1236             io::Error::new(
1237                 err.kind(),
1238                 format!(
1239                     "{} when getting real_size for {}",
1240                     err,
1241                     self.fullname_lossy()
1242                 ),
1243             )
1244         })
1245     }
1246 
1247     /// Indicates whether this header will be followed by additional
1248     /// sparse-header records.
1249     ///
1250     /// Note that this is handled internally by this library, and is likely only
1251     /// interesting if a `raw` iterator is being used.
is_extended(&self) -> bool1252     pub fn is_extended(&self) -> bool {
1253         self.isextended[0] == 1
1254     }
1255 
1256     /// Views this as a normal `Header`
as_header(&self) -> &Header1257     pub fn as_header(&self) -> &Header {
1258         unsafe { cast(self) }
1259     }
1260 
1261     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1262     pub fn as_header_mut(&mut self) -> &mut Header {
1263         unsafe { cast_mut(self) }
1264     }
1265 }
1266 
1267 impl fmt::Debug for GnuHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1268     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);
1273         }
1274         if let Ok(ctime) = self.ctime() {
1275             f.field("ctime", &ctime);
1276         }
1277         f.field("is_extended", &self.is_extended())
1278             .field("sparse", &DebugSparseHeaders(&self.sparse))
1279             .finish()
1280     }
1281 }
1282 
1283 struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1284 
1285 impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1286     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() {
1290                 f.entry(header);
1291             }
1292         }
1293         f.finish()
1294     }
1295 }
1296 
1297 impl GnuSparseHeader {
1298     /// Returns true if block is empty
is_empty(&self) -> bool1299     pub fn is_empty(&self) -> bool {
1300         self.offset[0] == 0 || self.numbytes[0] == 0
1301     }
1302 
1303     /// Offset of the block from the start of the file
1304     ///
1305     /// Returns `Err` for a malformed `offset` field.
offset(&self) -> io::Result<u64>1306     pub fn offset(&self) -> io::Result<u64> {
1307         octal_from(&self.offset).map_err(|err| {
1308             io::Error::new(
1309                 err.kind(),
1310                 format!("{} when getting offset from sparse header", err),
1311             )
1312         })
1313     }
1314 
1315     /// Length of the block
1316     ///
1317     /// Returns `Err` for a malformed `numbytes` field.
length(&self) -> io::Result<u64>1318     pub fn length(&self) -> io::Result<u64> {
1319         octal_from(&self.numbytes).map_err(|err| {
1320             io::Error::new(
1321                 err.kind(),
1322                 format!("{} when getting length from sparse header", err),
1323             )
1324         })
1325     }
1326 }
1327 
1328 impl fmt::Debug for GnuSparseHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1329     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);
1333         }
1334         if let Ok(length) = self.length() {
1335             f.field("length", &length);
1336         }
1337         f.finish()
1338     }
1339 }
1340 
1341 impl GnuExtSparseHeader {
1342     /// Crates a new zero'd out sparse header entry.
new() -> GnuExtSparseHeader1343     pub fn new() -> GnuExtSparseHeader {
1344         unsafe { mem::zeroed() }
1345     }
1346 
1347     /// Returns a view into this header as a byte array.
as_bytes(&self) -> &[u8; 512]1348     pub fn as_bytes(&self) -> &[u8; 512] {
1349         debug_assert_eq!(mem::size_of_val(self), 512);
1350         unsafe { mem::transmute(self) }
1351     }
1352 
1353     /// Returns a view into this header as a byte array.
as_mut_bytes(&mut self) -> &mut [u8; 512]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) }
1357     }
1358 
1359     /// Returns a slice of the underlying sparse headers.
1360     ///
1361     /// Some headers may represent empty chunks of both the offset and numbytes
1362     /// fields are 0.
sparse(&self) -> &[GnuSparseHeader; 21]1363     pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1364         &self.sparse
1365     }
1366 
1367     /// Indicates if another sparse header should be following this one.
is_extended(&self) -> bool1368     pub fn is_extended(&self) -> bool {
1369         self.isextended[0] == 1
1370     }
1371 }
1372 
1373 impl Default for GnuExtSparseHeader {
default() -> Self1374     fn default() -> Self {
1375         Self::new()
1376     }
1377 }
1378 
octal_from(slice: &[u8]) -> io::Result<u64>1379 fn octal_from(slice: &[u8]) -> io::Result<u64> {
1380     let trun = truncate(slice);
1381     let num = match str::from_utf8(trun) {
1382         Ok(n) => n,
1383         Err(_) => {
1384             return Err(other(&format!(
1385                 "numeric field did not have utf-8 text: {}",
1386                 String::from_utf8_lossy(trun)
1387             )));
1388         }
1389     };
1390     match u64::from_str_radix(num.trim(), 8) {
1391         Ok(n) => Ok(n),
1392         Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1393     }
1394 }
1395 
octal_into<T: fmt::Octal>(dst: &mut [u8], val: T)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) {
1400         *slot = value;
1401     }
1402 }
1403 
1404 // Wrapper to figure out if we should fill the header field using tar's numeric
1405 // extension (binary) or not (octal).
num_field_wrapper_into(dst: &mut [u8], src: u64)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);
1409     } else {
1410         octal_into(dst, src);
1411     }
1412 }
1413 
1414 // Wrapper to figure out if we should read the header field in binary (numeric
1415 // extension) or octal (standard encoding).
num_field_wrapper_from(src: &[u8]) -> io::Result<u64>1416 fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1417     if src[0] & 0x80 != 0 {
1418         Ok(numeric_extended_from(src))
1419     } else {
1420         octal_from(src)
1421     }
1422 }
1423 
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.
numeric_extended_into(dst: &mut [u8], src: u64)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(
1431         repeat(0)
1432             .take(len - 8) // to zero init extra bytes
1433             .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1434     ) {
1435         *slot = val;
1436     }
1437     dst[0] |= 0x80;
1438 }
1439 
numeric_extended_from(src: &[u8]) -> u641440 fn numeric_extended_from(src: &[u8]) -> u64 {
1441     let mut dst: u64 = 0;
1442     let mut b_to_skip = 1;
1443     if src.len() == 8 {
1444         // read first byte without extension flag bit
1445         dst = (src[0] ^ 0x80) as u64;
1446     } else {
1447         // only read last 8 bytes
1448         b_to_skip = src.len() - 8;
1449     }
1450     for byte in src.iter().skip(b_to_skip) {
1451         dst <<= 8;
1452         dst |= *byte as u64;
1453     }
1454     dst
1455 }
1456 
truncate(slice: &[u8]) -> &[u8]1457 fn truncate(slice: &[u8]) -> &[u8] {
1458     match slice.iter().position(|i| *i == 0) {
1459         Some(i) => &slice[..i],
1460         None => slice,
1461     }
1462 }
1463 
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.
copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()>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"))
1471     } else {
1472         for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1473             *slot = *val;
1474         }
1475         Ok(())
1476     }
1477 }
1478 
1479 /// Copies `path` into the `slot` provided
1480 ///
1481 /// Returns an error if:
1482 ///
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
copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()>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"));
1495             }
1496             (Component::ParentDir, false) => {
1497                 return Err(other("paths in archives must not have `..`"));
1498             }
1499             // Allow "./" as the path
1500             (Component::CurDir, false) if path.components().count() == 1 => {}
1501             (Component::CurDir, false) => continue,
1502             (Component::Normal(_), _) | (_, true) => {}
1503         };
1504         if needs_slash {
1505             copy(&mut slot, b"/")?;
1506         }
1507         if bytes.contains(&b'/') {
1508             if let Component::Normal(..) = component {
1509                 return Err(other("path component in archive cannot contain `/`"));
1510             }
1511         }
1512         copy(&mut slot, &*bytes)?;
1513         if &*bytes != b"/" {
1514             needs_slash = true;
1515         }
1516         emitted = true;
1517     }
1518     if !emitted {
1519         return Err(other("paths in archives must have at least one component"));
1520     }
1521     if ends_with_slash(path) {
1522         copy(&mut slot, &[b'/'])?;
1523     }
1524     return Ok(());
1525 
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()..];
1530         Ok(())
1531     }
1532 }
1533 
1534 #[cfg(target_arch = "wasm32")]
ends_with_slash(p: &Path) -> bool1535 fn ends_with_slash(p: &Path) -> bool {
1536     p.to_string_lossy().ends_with('/')
1537 }
1538 
1539 #[cfg(windows)]
ends_with_slash(p: &Path) -> bool1540 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)
1543 }
1544 
1545 #[cfg(unix)]
ends_with_slash(p: &Path) -> bool1546 fn ends_with_slash(p: &Path) -> bool {
1547     p.as_os_str().as_bytes().ends_with(&[b'/'])
1548 }
1549 
1550 #[cfg(any(windows, target_arch = "wasm32"))]
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1551 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1552     p.as_os_str()
1553         .to_str()
1554         .map(|s| s.as_bytes())
1555         .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1556         .map(|bytes| {
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 {
1561                     if *b == b'\\' {
1562                         *b = b'/';
1563                     }
1564                 }
1565                 Cow::Owned(bytes)
1566             } else {
1567                 Cow::Borrowed(bytes)
1568             }
1569         })
1570 }
1571 
1572 #[cfg(unix)]
1573 /// On unix this will never fail
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1574 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1575     Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1576 }
1577 
1578 #[cfg(windows)]
1579 /// On windows we cannot accept non-Unicode bytes because it
1580 /// is impossible to convert it to UTF-16.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>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)))
1586         }
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)))
1590         }
1591     };
1592 
1593     fn not_unicode(v: &[u8]) -> io::Error {
1594         other(&format!(
1595             "only Unicode paths are supported on Windows: {}",
1596             String::from_utf8_lossy(v)
1597         ))
1598     }
1599 }
1600 
1601 #[cfg(unix)]
1602 /// On unix this operation can never fail.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1603 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1604     use std::ffi::{OsStr, OsString};
1605 
1606     Ok(match bytes {
1607         Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1608         Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1609     })
1610 }
1611 
1612 #[cfg(target_arch = "wasm32")]
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1613 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1614     Ok(match bytes {
1615         Cow::Borrowed(bytes) => {
1616             Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1617         }
1618         Cow::Owned(bytes) => {
1619             Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1620         }
1621     })
1622 }
1623 
1624 #[cfg(target_arch = "wasm32")]
invalid_utf8<T>(_: T) -> io::Error1625 fn invalid_utf8<T>(_: T) -> io::Error {
1626     io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1627 }
1628