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 pub enum HeaderMode {
30     /// All supported metadata, including mod/access times and ownership will
31     /// be included.
32     Complete,
33 
34     /// Only metadata that is directly relevant to the identity of a file will
35     /// be included. In particular, ownership and mod/access times are excluded.
36     Deterministic,
37 
38     #[doc(hidden)]
39     __Nonexhaustive,
40 }
41 
42 /// Representation of the header of an entry in an archive
43 #[repr(C)]
44 #[allow(missing_docs)]
45 pub struct OldHeader {
46     pub name: [u8; 100],
47     pub mode: [u8; 8],
48     pub uid: [u8; 8],
49     pub gid: [u8; 8],
50     pub size: [u8; 12],
51     pub mtime: [u8; 12],
52     pub cksum: [u8; 8],
53     pub linkflag: [u8; 1],
54     pub linkname: [u8; 100],
55     pub pad: [u8; 255],
56 }
57 
58 /// Representation of the header of an entry in an archive
59 #[repr(C)]
60 #[allow(missing_docs)]
61 pub struct UstarHeader {
62     pub name: [u8; 100],
63     pub mode: [u8; 8],
64     pub uid: [u8; 8],
65     pub gid: [u8; 8],
66     pub size: [u8; 12],
67     pub mtime: [u8; 12],
68     pub cksum: [u8; 8],
69     pub typeflag: [u8; 1],
70     pub linkname: [u8; 100],
71 
72     // UStar format
73     pub magic: [u8; 6],
74     pub version: [u8; 2],
75     pub uname: [u8; 32],
76     pub gname: [u8; 32],
77     pub dev_major: [u8; 8],
78     pub dev_minor: [u8; 8],
79     pub prefix: [u8; 155],
80     pub pad: [u8; 12],
81 }
82 
83 /// Representation of the header of an entry in an archive
84 #[repr(C)]
85 #[allow(missing_docs)]
86 pub struct GnuHeader {
87     pub name: [u8; 100],
88     pub mode: [u8; 8],
89     pub uid: [u8; 8],
90     pub gid: [u8; 8],
91     pub size: [u8; 12],
92     pub mtime: [u8; 12],
93     pub cksum: [u8; 8],
94     pub typeflag: [u8; 1],
95     pub linkname: [u8; 100],
96 
97     // GNU format
98     pub magic: [u8; 6],
99     pub version: [u8; 2],
100     pub uname: [u8; 32],
101     pub gname: [u8; 32],
102     pub dev_major: [u8; 8],
103     pub dev_minor: [u8; 8],
104     pub atime: [u8; 12],
105     pub ctime: [u8; 12],
106     pub offset: [u8; 12],
107     pub longnames: [u8; 4],
108     pub unused: [u8; 1],
109     pub sparse: [GnuSparseHeader; 4],
110     pub isextended: [u8; 1],
111     pub realsize: [u8; 12],
112     pub pad: [u8; 17],
113 }
114 
115 /// Description of the header of a spare entry.
116 ///
117 /// Specifies the offset/number of bytes of a chunk of data in octal.
118 #[repr(C)]
119 #[allow(missing_docs)]
120 pub struct GnuSparseHeader {
121     pub offset: [u8; 12],
122     pub numbytes: [u8; 12],
123 }
124 
125 /// Representation of the entry found to represent extended GNU sparse files.
126 ///
127 /// When a `GnuHeader` has the `isextended` flag set to `1` then the contents of
128 /// the next entry will be one of these headers.
129 #[repr(C)]
130 #[allow(missing_docs)]
131 pub struct GnuExtSparseHeader {
132     pub sparse: [GnuSparseHeader; 21],
133     pub isextended: [u8; 1],
134     pub padding: [u8; 7],
135 }
136 
137 impl Header {
138     /// Creates a new blank GNU header.
139     ///
140     /// The GNU style header is the default for this library and allows various
141     /// extensions such as long path names, long link names, and setting the
142     /// atime/ctime metadata attributes of files.
new_gnu() -> Header143     pub fn new_gnu() -> Header {
144         let mut header = Header { bytes: [0; 512] };
145         unsafe {
146             let gnu = cast_mut::<_, GnuHeader>(&mut header);
147             gnu.magic = *b"ustar ";
148             gnu.version = *b" \0";
149         }
150         header.set_mtime(0);
151         header
152     }
153 
154     /// Creates a new blank UStar header.
155     ///
156     /// The UStar style header is an extension of the original archive header
157     /// which enables some extra metadata along with storing a longer (but not
158     /// too long) path name.
159     ///
160     /// UStar is also the basis used for pax archives.
new_ustar() -> Header161     pub fn new_ustar() -> Header {
162         let mut header = Header { bytes: [0; 512] };
163         unsafe {
164             let gnu = cast_mut::<_, UstarHeader>(&mut header);
165             gnu.magic = *b"ustar\0";
166             gnu.version = *b"00";
167         }
168         header.set_mtime(0);
169         header
170     }
171 
172     /// Creates a new blank old header.
173     ///
174     /// This header format is the original archive header format which all other
175     /// versions are compatible with (e.g. they are a superset). This header
176     /// format limits the path name limit and isn't able to contain extra
177     /// metadata like atime/ctime.
new_old() -> Header178     pub fn new_old() -> Header {
179         let mut header = Header { bytes: [0; 512] };
180         header.set_mtime(0);
181         header
182     }
183 
is_ustar(&self) -> bool184     fn is_ustar(&self) -> bool {
185         let ustar = unsafe { cast::<_, UstarHeader>(self) };
186         ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
187     }
188 
is_gnu(&self) -> bool189     fn is_gnu(&self) -> bool {
190         let ustar = unsafe { cast::<_, UstarHeader>(self) };
191         ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
192     }
193 
194     /// View this archive header as a raw "old" archive header.
195     ///
196     /// This view will always succeed as all archive header formats will fill
197     /// out at least the fields specified in the old header format.
as_old(&self) -> &OldHeader198     pub fn as_old(&self) -> &OldHeader {
199         unsafe { cast(self) }
200     }
201 
202     /// Same as `as_old`, but the mutable version.
as_old_mut(&mut self) -> &mut OldHeader203     pub fn as_old_mut(&mut self) -> &mut OldHeader {
204         unsafe { cast_mut(self) }
205     }
206 
207     /// View this archive header as a raw UStar archive header.
208     ///
209     /// The UStar format is an extension to the tar archive format which enables
210     /// longer pathnames and a few extra attributes such as the group and user
211     /// name.
212     ///
213     /// This cast may not succeed as this function will test whether the
214     /// magic/version fields of the UStar format have the appropriate values,
215     /// returning `None` if they aren't correct.
as_ustar(&self) -> Option<&UstarHeader>216     pub fn as_ustar(&self) -> Option<&UstarHeader> {
217         if self.is_ustar() {
218             Some(unsafe { cast(self) })
219         } else {
220             None
221         }
222     }
223 
224     /// Same as `as_ustar_mut`, but the mutable version.
as_ustar_mut(&mut self) -> Option<&mut UstarHeader>225     pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
226         if self.is_ustar() {
227             Some(unsafe { cast_mut(self) })
228         } else {
229             None
230         }
231     }
232 
233     /// View this archive header as a raw GNU archive header.
234     ///
235     /// The GNU format is an extension to the tar archive format which enables
236     /// longer pathnames and a few extra attributes such as the group and user
237     /// name.
238     ///
239     /// This cast may not succeed as this function will test whether the
240     /// magic/version fields of the GNU format have the appropriate values,
241     /// returning `None` if they aren't correct.
as_gnu(&self) -> Option<&GnuHeader>242     pub fn as_gnu(&self) -> Option<&GnuHeader> {
243         if self.is_gnu() {
244             Some(unsafe { cast(self) })
245         } else {
246             None
247         }
248     }
249 
250     /// Same as `as_gnu`, but the mutable version.
as_gnu_mut(&mut self) -> Option<&mut GnuHeader>251     pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
252         if self.is_gnu() {
253             Some(unsafe { cast_mut(self) })
254         } else {
255             None
256         }
257     }
258 
259     /// Treats the given byte slice as a header.
260     ///
261     /// Panics if the length of the passed slice is not equal to 512.
from_byte_slice(bytes: &[u8]) -> &Header262     pub fn from_byte_slice(bytes: &[u8]) -> &Header {
263         assert_eq!(bytes.len(), mem::size_of::<Header>());
264         assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
265         unsafe { &*(bytes.as_ptr() as *const Header) }
266     }
267 
268     /// Returns a view into this header as a byte array.
as_bytes(&self) -> &[u8; 512]269     pub fn as_bytes(&self) -> &[u8; 512] {
270         &self.bytes
271     }
272 
273     /// Returns a view into this header as a byte array.
as_mut_bytes(&mut self) -> &mut [u8; 512]274     pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
275         &mut self.bytes
276     }
277 
278     /// Blanket sets the metadata in this header from the metadata argument
279     /// provided.
280     ///
281     /// This is useful for initializing a `Header` from the OS's metadata from a
282     /// file. By default, this will use `HeaderMode::Complete` to include all
283     /// metadata.
set_metadata(&mut self, meta: &fs::Metadata)284     pub fn set_metadata(&mut self, meta: &fs::Metadata) {
285         self.fill_from(meta, HeaderMode::Complete);
286     }
287 
288     /// Sets only the metadata relevant to the given HeaderMode in this header
289     /// from the metadata argument provided.
set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode)290     pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
291         self.fill_from(meta, mode);
292     }
293 
294     /// Returns the size of entry's data this header represents.
295     ///
296     /// This is different from `Header::size` for sparse files, which have
297     /// some longer `size()` but shorter `entry_size()`. The `entry_size()`
298     /// listed here should be the number of bytes in the archive this header
299     /// describes.
300     ///
301     /// May return an error if the field is corrupted.
entry_size(&self) -> io::Result<u64>302     pub fn entry_size(&self) -> io::Result<u64> {
303         num_field_wrapper_from(&self.as_old().size).map_err(|err| {
304             io::Error::new(
305                 err.kind(),
306                 format!("{} when getting size for {}", err, self.path_lossy()),
307             )
308         })
309     }
310 
311     /// Returns the file size this header represents.
312     ///
313     /// May return an error if the field is corrupted.
size(&self) -> io::Result<u64>314     pub fn size(&self) -> io::Result<u64> {
315         if self.entry_type().is_gnu_sparse() {
316             self.as_gnu()
317                 .ok_or_else(|| other("sparse header was not a gnu header"))
318                 .and_then(|h| h.real_size())
319         } else {
320             self.entry_size()
321         }
322     }
323 
324     /// Encodes the `size` argument into the size field of this header.
set_size(&mut self, size: u64)325     pub fn set_size(&mut self, size: u64) {
326         num_field_wrapper_into(&mut self.as_old_mut().size, size);
327     }
328 
329     /// Returns the raw path name stored in this header.
330     ///
331     /// This method may fail if the pathname is not valid Unicode and this is
332     /// called on a Windows platform.
333     ///
334     /// Note that this function will convert any `\` characters to directory
335     /// separators.
path(&self) -> io::Result<Cow<Path>>336     pub fn path(&self) -> io::Result<Cow<Path>> {
337         bytes2path(self.path_bytes())
338     }
339 
340     /// Returns the pathname stored in this header as a byte array.
341     ///
342     /// This function is guaranteed to succeed, but you may wish to call the
343     /// `path` method to convert to a `Path`.
344     ///
345     /// Note that this function will convert any `\` characters to directory
346     /// separators.
path_bytes(&self) -> Cow<[u8]>347     pub fn path_bytes(&self) -> Cow<[u8]> {
348         if let Some(ustar) = self.as_ustar() {
349             ustar.path_bytes()
350         } else {
351             let name = truncate(&self.as_old().name);
352             Cow::Borrowed(name)
353         }
354     }
355 
356     /// Gets the path in a "lossy" way, used for error reporting ONLY.
path_lossy(&self) -> String357     fn path_lossy(&self) -> String {
358         String::from_utf8_lossy(&self.path_bytes()).to_string()
359     }
360 
361     /// Sets the path name for this header.
362     ///
363     /// This function will set the pathname listed in this header, encoding it
364     /// in the appropriate format. May fail if the path is too long or if the
365     /// path specified is not Unicode and this is a Windows platform. Will
366     /// strip out any "." path component, which signifies the current directory.
367     ///
368     /// Note: This function does not support names over 100 bytes, or paths
369     /// over 255 bytes, even for formats that support longer names. Instead,
370     /// use `Builder` methods to insert a long-name extension at the same time
371     /// as the file content.
set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>372     pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
373         self._set_path(p.as_ref())
374     }
375 
_set_path(&mut self, path: &Path) -> io::Result<()>376     fn _set_path(&mut self, path: &Path) -> io::Result<()> {
377         if let Some(ustar) = self.as_ustar_mut() {
378             return ustar.set_path(path);
379         }
380         copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
381             io::Error::new(
382                 err.kind(),
383                 format!("{} when setting path for {}", err, self.path_lossy()),
384             )
385         })
386     }
387 
388     /// Returns the link name stored in this header, if any is found.
389     ///
390     /// This method may fail if the pathname is not valid Unicode and this is
391     /// called on a Windows platform. `Ok(None)` being returned, however,
392     /// indicates that the link name was not present.
393     ///
394     /// Note that this function will convert any `\` characters to directory
395     /// separators.
link_name(&self) -> io::Result<Option<Cow<Path>>>396     pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
397         match self.link_name_bytes() {
398             Some(bytes) => bytes2path(bytes).map(Some),
399             None => Ok(None),
400         }
401     }
402 
403     /// Returns the link name stored in this header as a byte array, if any.
404     ///
405     /// This function is guaranteed to succeed, but you may wish to call the
406     /// `link_name` method to convert to a `Path`.
407     ///
408     /// Note that this function will convert any `\` characters to directory
409     /// separators.
link_name_bytes(&self) -> Option<Cow<[u8]>>410     pub fn link_name_bytes(&self) -> Option<Cow<[u8]>> {
411         let old = self.as_old();
412         if old.linkname[0] != 0 {
413             Some(Cow::Borrowed(truncate(&old.linkname)))
414         } else {
415             None
416         }
417     }
418 
419     /// Sets the link name for this header.
420     ///
421     /// This function will set the linkname listed in this header, encoding it
422     /// in the appropriate format. May fail if the link name is too long or if
423     /// the path specified is not Unicode and this is a Windows platform. Will
424     /// strip out any "." path component, which signifies the current directory.
set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>425     pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
426         self._set_link_name(p.as_ref())
427     }
428 
_set_link_name(&mut self, path: &Path) -> io::Result<()>429     fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
430         copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
431             io::Error::new(
432                 err.kind(),
433                 format!("{} when setting link name for {}", err, self.path_lossy()),
434             )
435         })
436     }
437 
438     /// Returns the mode bits for this file
439     ///
440     /// May return an error if the field is corrupted.
mode(&self) -> io::Result<u32>441     pub fn mode(&self) -> io::Result<u32> {
442         octal_from(&self.as_old().mode)
443             .map(|u| u as u32)
444             .map_err(|err| {
445                 io::Error::new(
446                     err.kind(),
447                     format!("{} when getting mode for {}", err, self.path_lossy()),
448                 )
449             })
450     }
451 
452     /// Encodes the `mode` provided into this header.
set_mode(&mut self, mode: u32)453     pub fn set_mode(&mut self, mode: u32) {
454         octal_into(&mut self.as_old_mut().mode, mode);
455     }
456 
457     /// Returns the value of the owner's user ID field
458     ///
459     /// May return an error if the field is corrupted.
uid(&self) -> io::Result<u64>460     pub fn uid(&self) -> io::Result<u64> {
461         num_field_wrapper_from(&self.as_old().uid)
462             .map(|u| u as u64)
463             .map_err(|err| {
464                 io::Error::new(
465                     err.kind(),
466                     format!("{} when getting uid for {}", err, self.path_lossy()),
467                 )
468             })
469     }
470 
471     /// Encodes the `uid` provided into this header.
set_uid(&mut self, uid: u64)472     pub fn set_uid(&mut self, uid: u64) {
473         num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
474     }
475 
476     /// Returns the value of the group's user ID field
gid(&self) -> io::Result<u64>477     pub fn gid(&self) -> io::Result<u64> {
478         num_field_wrapper_from(&self.as_old().gid)
479             .map(|u| u as u64)
480             .map_err(|err| {
481                 io::Error::new(
482                     err.kind(),
483                     format!("{} when getting gid for {}", err, self.path_lossy()),
484                 )
485             })
486     }
487 
488     /// Encodes the `gid` provided into this header.
set_gid(&mut self, gid: u64)489     pub fn set_gid(&mut self, gid: u64) {
490         num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
491     }
492 
493     /// Returns the last modification time in Unix time format
mtime(&self) -> io::Result<u64>494     pub fn mtime(&self) -> io::Result<u64> {
495         num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
496             io::Error::new(
497                 err.kind(),
498                 format!("{} when getting mtime for {}", err, self.path_lossy()),
499             )
500         })
501     }
502 
503     /// Encodes the `mtime` provided into this header.
504     ///
505     /// Note that this time is typically a number of seconds passed since
506     /// January 1, 1970.
set_mtime(&mut self, mtime: u64)507     pub fn set_mtime(&mut self, mtime: u64) {
508         num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
509     }
510 
511     /// Return the user name of the owner of this file.
512     ///
513     /// A return value of `Ok(Some(..))` indicates that the user name was
514     /// present and was valid utf-8, `Ok(None)` indicates that the user name is
515     /// not present in this archive format, and `Err` indicates that the user
516     /// name was present but was not valid utf-8.
username(&self) -> Result<Option<&str>, str::Utf8Error>517     pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
518         match self.username_bytes() {
519             Some(bytes) => str::from_utf8(bytes).map(Some),
520             None => Ok(None),
521         }
522     }
523 
524     /// Returns the user name of the owner of this file, if present.
525     ///
526     /// A return value of `None` indicates that the user name is not present in
527     /// this header format.
username_bytes(&self) -> Option<&[u8]>528     pub fn username_bytes(&self) -> Option<&[u8]> {
529         if let Some(ustar) = self.as_ustar() {
530             Some(ustar.username_bytes())
531         } else if let Some(gnu) = self.as_gnu() {
532             Some(gnu.username_bytes())
533         } else {
534             None
535         }
536     }
537 
538     /// Sets the username inside this header.
539     ///
540     /// This function will return an error if this header format cannot encode a
541     /// user name or the name is too long.
set_username(&mut self, name: &str) -> io::Result<()>542     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
543         if let Some(ustar) = self.as_ustar_mut() {
544             return ustar.set_username(name);
545         }
546         if let Some(gnu) = self.as_gnu_mut() {
547             gnu.set_username(name)
548         } else {
549             Err(other("not a ustar or gnu archive, cannot set username"))
550         }
551     }
552 
553     /// Return the group name of the owner of this file.
554     ///
555     /// A return value of `Ok(Some(..))` indicates that the group name was
556     /// present and was valid utf-8, `Ok(None)` indicates that the group name is
557     /// not present in this archive format, and `Err` indicates that the group
558     /// name was present but was not valid utf-8.
groupname(&self) -> Result<Option<&str>, str::Utf8Error>559     pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
560         match self.groupname_bytes() {
561             Some(bytes) => str::from_utf8(bytes).map(Some),
562             None => Ok(None),
563         }
564     }
565 
566     /// Returns the group name of the owner of this file, if present.
567     ///
568     /// A return value of `None` indicates that the group name is not present in
569     /// this header format.
groupname_bytes(&self) -> Option<&[u8]>570     pub fn groupname_bytes(&self) -> Option<&[u8]> {
571         if let Some(ustar) = self.as_ustar() {
572             Some(ustar.groupname_bytes())
573         } else if let Some(gnu) = self.as_gnu() {
574             Some(gnu.groupname_bytes())
575         } else {
576             None
577         }
578     }
579 
580     /// Sets the group name inside this header.
581     ///
582     /// This function will return an error if this header format cannot encode a
583     /// group name or the name is too long.
set_groupname(&mut self, name: &str) -> io::Result<()>584     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
585         if let Some(ustar) = self.as_ustar_mut() {
586             return ustar.set_groupname(name);
587         }
588         if let Some(gnu) = self.as_gnu_mut() {
589             gnu.set_groupname(name)
590         } else {
591             Err(other("not a ustar or gnu archive, cannot set groupname"))
592         }
593     }
594 
595     /// Returns the device major number, if present.
596     ///
597     /// This field may not be present in all archives, and it may not be
598     /// correctly formed in all archives. `Ok(Some(..))` means it was present
599     /// and correctly decoded, `Ok(None)` indicates that this header format does
600     /// not include the device major number, and `Err` indicates that it was
601     /// present and failed to decode.
device_major(&self) -> io::Result<Option<u32>>602     pub fn device_major(&self) -> io::Result<Option<u32>> {
603         if let Some(ustar) = self.as_ustar() {
604             ustar.device_major().map(Some)
605         } else if let Some(gnu) = self.as_gnu() {
606             gnu.device_major().map(Some)
607         } else {
608             Ok(None)
609         }
610     }
611 
612     /// Encodes the value `major` into the dev_major field of this header.
613     ///
614     /// This function will return an error if this header format cannot encode a
615     /// major device number.
set_device_major(&mut self, major: u32) -> io::Result<()>616     pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
617         if let Some(ustar) = self.as_ustar_mut() {
618             return Ok(ustar.set_device_major(major));
619         }
620         if let Some(gnu) = self.as_gnu_mut() {
621             Ok(gnu.set_device_major(major))
622         } else {
623             Err(other("not a ustar or gnu archive, cannot set dev_major"))
624         }
625     }
626 
627     /// Returns the device minor number, if present.
628     ///
629     /// This field may not be present in all archives, and it may not be
630     /// correctly formed in all archives. `Ok(Some(..))` means it was present
631     /// and correctly decoded, `Ok(None)` indicates that this header format does
632     /// not include the device minor number, and `Err` indicates that it was
633     /// present and failed to decode.
device_minor(&self) -> io::Result<Option<u32>>634     pub fn device_minor(&self) -> io::Result<Option<u32>> {
635         if let Some(ustar) = self.as_ustar() {
636             ustar.device_minor().map(Some)
637         } else if let Some(gnu) = self.as_gnu() {
638             gnu.device_minor().map(Some)
639         } else {
640             Ok(None)
641         }
642     }
643 
644     /// Encodes the value `minor` into the dev_minor field of this header.
645     ///
646     /// This function will return an error if this header format cannot encode a
647     /// minor device number.
set_device_minor(&mut self, minor: u32) -> io::Result<()>648     pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
649         if let Some(ustar) = self.as_ustar_mut() {
650             return Ok(ustar.set_device_minor(minor));
651         }
652         if let Some(gnu) = self.as_gnu_mut() {
653             Ok(gnu.set_device_minor(minor))
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             HeaderMode::__Nonexhaustive => panic!(),
759         }
760 
761         // Note that if we are a GNU header we *could* set atime/ctime, except
762         // the `tar` utility doesn't do that by default and it causes problems
763         // with 7-zip [1].
764         //
765         // It's always possible to fill them out manually, so we just don't fill
766         // it out automatically here.
767         //
768         // [1]: https://github.com/alexcrichton/tar-rs/issues/70
769 
770         // TODO: need to bind more file types
771         self.set_entry_type(entry_type(meta.mode()));
772 
773         fn entry_type(mode: u32) -> EntryType {
774             match mode as libc::mode_t & libc::S_IFMT {
775                 libc::S_IFREG => EntryType::file(),
776                 libc::S_IFLNK => EntryType::symlink(),
777                 libc::S_IFCHR => EntryType::character_special(),
778                 libc::S_IFBLK => EntryType::block_special(),
779                 libc::S_IFDIR => EntryType::dir(),
780                 libc::S_IFIFO => EntryType::fifo(),
781                 _ => EntryType::new(b' '),
782             }
783         }
784     }
785 
786     #[cfg(windows)]
fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)787     fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
788         // There's no concept of a file mode on Windows, so do a best approximation here.
789         match mode {
790             HeaderMode::Complete => {
791                 self.set_uid(0);
792                 self.set_gid(0);
793                 // The dates listed in tarballs are always seconds relative to
794                 // January 1, 1970. On Windows, however, the timestamps are returned as
795                 // dates relative to January 1, 1601 (in 100ns intervals), so we need to
796                 // add in some offset for those dates.
797                 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
798                 self.set_mtime(mtime);
799                 let fs_mode = {
800                     const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
801                     let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
802                     match (meta.is_dir(), readonly != 0) {
803                         (true, false) => 0o755,
804                         (true, true) => 0o555,
805                         (false, false) => 0o644,
806                         (false, true) => 0o444,
807                     }
808                 };
809                 self.set_mode(fs_mode);
810             }
811             HeaderMode::Deterministic => {
812                 self.set_uid(0);
813                 self.set_gid(0);
814                 self.set_mtime(123456789); // see above in unix
815                 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
816                 self.set_mode(fs_mode);
817             }
818             HeaderMode::__Nonexhaustive => panic!(),
819         }
820 
821         let ft = meta.file_type();
822         self.set_entry_type(if ft.is_dir() {
823             EntryType::dir()
824         } else if ft.is_file() {
825             EntryType::file()
826         } else if ft.is_symlink() {
827             EntryType::symlink()
828         } else {
829             EntryType::new(b' ')
830         });
831     }
832 
debug_fields(&self, b: &mut fmt::DebugStruct)833     fn debug_fields(&self, b: &mut fmt::DebugStruct) {
834         if let Ok(entry_size) = self.entry_size() {
835             b.field("entry_size", &entry_size);
836         }
837         if let Ok(size) = self.size() {
838             b.field("size", &size);
839         }
840         if let Ok(path) = self.path() {
841             b.field("path", &path);
842         }
843         if let Ok(link_name) = self.link_name() {
844             b.field("link_name", &link_name);
845         }
846         if let Ok(mode) = self.mode() {
847             b.field("mode", &DebugAsOctal(mode));
848         }
849         if let Ok(uid) = self.uid() {
850             b.field("uid", &uid);
851         }
852         if let Ok(gid) = self.gid() {
853             b.field("gid", &gid);
854         }
855         if let Ok(mtime) = self.mtime() {
856             b.field("mtime", &mtime);
857         }
858         if let Ok(username) = self.username() {
859             b.field("username", &username);
860         }
861         if let Ok(groupname) = self.groupname() {
862             b.field("groupname", &groupname);
863         }
864         if let Ok(device_major) = self.device_major() {
865             b.field("device_major", &device_major);
866         }
867         if let Ok(device_minor) = self.device_minor() {
868             b.field("device_minor", &device_minor);
869         }
870         if let Ok(cksum) = self.cksum() {
871             b.field("cksum", &cksum);
872             b.field("cksum_valid", &(cksum == self.calculate_cksum()));
873         }
874     }
875 }
876 
877 struct DebugAsOctal<T>(T);
878 
879 impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result880     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
881         fmt::Octal::fmt(&self.0, f)
882     }
883 }
884 
cast<T, U>(a: &T) -> &U885 unsafe fn cast<T, U>(a: &T) -> &U {
886     assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
887     assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
888     &*(a as *const T as *const U)
889 }
890 
cast_mut<T, U>(a: &mut T) -> &mut U891 unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
892     assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
893     assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
894     &mut *(a as *mut T as *mut U)
895 }
896 
897 impl Clone for Header {
clone(&self) -> Header898     fn clone(&self) -> Header {
899         Header { bytes: self.bytes }
900     }
901 }
902 
903 impl fmt::Debug for Header {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result904     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
905         if let Some(me) = self.as_ustar() {
906             me.fmt(f)
907         } else if let Some(me) = self.as_gnu() {
908             me.fmt(f)
909         } else {
910             self.as_old().fmt(f)
911         }
912     }
913 }
914 
915 impl OldHeader {
916     /// Views this as a normal `Header`
as_header(&self) -> &Header917     pub fn as_header(&self) -> &Header {
918         unsafe { cast(self) }
919     }
920 
921     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header922     pub fn as_header_mut(&mut self) -> &mut Header {
923         unsafe { cast_mut(self) }
924     }
925 }
926 
927 impl fmt::Debug for OldHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result928     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
929         let mut f = f.debug_struct("OldHeader");
930         self.as_header().debug_fields(&mut f);
931         f.finish()
932     }
933 }
934 
935 impl UstarHeader {
936     /// See `Header::path_bytes`
path_bytes(&self) -> Cow<[u8]>937     pub fn path_bytes(&self) -> Cow<[u8]> {
938         if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
939             Cow::Borrowed(truncate(&self.name))
940         } else {
941             let mut bytes = Vec::new();
942             let prefix = truncate(&self.prefix);
943             if prefix.len() > 0 {
944                 bytes.extend_from_slice(prefix);
945                 bytes.push(b'/');
946             }
947             bytes.extend_from_slice(truncate(&self.name));
948             Cow::Owned(bytes)
949         }
950     }
951 
952     /// Gets the path in a "lossy" way, used for error reporting ONLY.
path_lossy(&self) -> String953     fn path_lossy(&self) -> String {
954         String::from_utf8_lossy(&self.path_bytes()).to_string()
955     }
956 
957     /// See `Header::set_path`
set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>958     pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
959         self._set_path(p.as_ref())
960     }
961 
_set_path(&mut self, path: &Path) -> io::Result<()>962     fn _set_path(&mut self, path: &Path) -> io::Result<()> {
963         // This can probably be optimized quite a bit more, but for now just do
964         // something that's relatively easy and readable.
965         //
966         // First up, if the path fits within `self.name` then we just shove it
967         // in there. If not then we try to split it between some existing path
968         // components where it can fit in name/prefix. To do that we peel off
969         // enough until the path fits in `prefix`, then we try to put both
970         // halves into their destination.
971         let bytes = path2bytes(path)?;
972         let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
973         if bytes.len() <= maxnamelen {
974             copy_path_into(&mut self.name, path, false).map_err(|err| {
975                 io::Error::new(
976                     err.kind(),
977                     format!("{} when setting path for {}", err, self.path_lossy()),
978                 )
979             })?;
980         } else {
981             let mut prefix = path;
982             let mut prefixlen;
983             loop {
984                 match prefix.parent() {
985                     Some(parent) => prefix = parent,
986                     None => {
987                         return Err(other(&format!(
988                             "path cannot be split to be inserted into archive: {}",
989                             path.display()
990                         )));
991                     }
992                 }
993                 prefixlen = path2bytes(prefix)?.len();
994                 if prefixlen <= maxprefixlen {
995                     break;
996                 }
997             }
998             copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
999                 io::Error::new(
1000                     err.kind(),
1001                     format!("{} when setting path for {}", err, self.path_lossy()),
1002                 )
1003             })?;
1004             let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1005             copy_path_into(&mut self.name, &path, false).map_err(|err| {
1006                 io::Error::new(
1007                     err.kind(),
1008                     format!("{} when setting path for {}", err, self.path_lossy()),
1009                 )
1010             })?;
1011         }
1012         Ok(())
1013     }
1014 
1015     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1016     pub fn username_bytes(&self) -> &[u8] {
1017         truncate(&self.uname)
1018     }
1019 
1020     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1021     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1022         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1023             io::Error::new(
1024                 err.kind(),
1025                 format!("{} when setting username for {}", err, self.path_lossy()),
1026             )
1027         })
1028     }
1029 
1030     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1031     pub fn groupname_bytes(&self) -> &[u8] {
1032         truncate(&self.gname)
1033     }
1034 
1035     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1036     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1037         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1038             io::Error::new(
1039                 err.kind(),
1040                 format!("{} when setting groupname for {}", err, self.path_lossy()),
1041             )
1042         })
1043     }
1044 
1045     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1046     pub fn device_major(&self) -> io::Result<u32> {
1047         octal_from(&self.dev_major)
1048             .map(|u| u as u32)
1049             .map_err(|err| {
1050                 io::Error::new(
1051                     err.kind(),
1052                     format!(
1053                         "{} when getting device_major for {}",
1054                         err,
1055                         self.path_lossy()
1056                     ),
1057                 )
1058             })
1059     }
1060 
1061     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1062     pub fn set_device_major(&mut self, major: u32) {
1063         octal_into(&mut self.dev_major, major);
1064     }
1065 
1066     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1067     pub fn device_minor(&self) -> io::Result<u32> {
1068         octal_from(&self.dev_minor)
1069             .map(|u| u as u32)
1070             .map_err(|err| {
1071                 io::Error::new(
1072                     err.kind(),
1073                     format!(
1074                         "{} when getting device_minor for {}",
1075                         err,
1076                         self.path_lossy()
1077                     ),
1078                 )
1079             })
1080     }
1081 
1082     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1083     pub fn set_device_minor(&mut self, minor: u32) {
1084         octal_into(&mut self.dev_minor, minor);
1085     }
1086 
1087     /// Views this as a normal `Header`
as_header(&self) -> &Header1088     pub fn as_header(&self) -> &Header {
1089         unsafe { cast(self) }
1090     }
1091 
1092     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1093     pub fn as_header_mut(&mut self) -> &mut Header {
1094         unsafe { cast_mut(self) }
1095     }
1096 }
1097 
1098 impl fmt::Debug for UstarHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1099     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1100         let mut f = f.debug_struct("UstarHeader");
1101         self.as_header().debug_fields(&mut f);
1102         f.finish()
1103     }
1104 }
1105 
1106 impl GnuHeader {
1107     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1108     pub fn username_bytes(&self) -> &[u8] {
1109         truncate(&self.uname)
1110     }
1111 
1112     /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
fullname_lossy(&self) -> String1113     fn fullname_lossy(&self) -> String {
1114         format!(
1115             "{}:{}",
1116             String::from_utf8_lossy(&self.groupname_bytes()),
1117             String::from_utf8_lossy(&self.username_bytes()),
1118         )
1119     }
1120 
1121     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1122     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1123         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1124             io::Error::new(
1125                 err.kind(),
1126                 format!(
1127                     "{} when setting username for {}",
1128                     err,
1129                     self.fullname_lossy()
1130                 ),
1131             )
1132         })
1133     }
1134 
1135     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1136     pub fn groupname_bytes(&self) -> &[u8] {
1137         truncate(&self.gname)
1138     }
1139 
1140     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1141     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1142         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1143             io::Error::new(
1144                 err.kind(),
1145                 format!(
1146                     "{} when setting groupname for {}",
1147                     err,
1148                     self.fullname_lossy()
1149                 ),
1150             )
1151         })
1152     }
1153 
1154     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1155     pub fn device_major(&self) -> io::Result<u32> {
1156         octal_from(&self.dev_major)
1157             .map(|u| u as u32)
1158             .map_err(|err| {
1159                 io::Error::new(
1160                     err.kind(),
1161                     format!(
1162                         "{} when getting device_major for {}",
1163                         err,
1164                         self.fullname_lossy()
1165                     ),
1166                 )
1167             })
1168     }
1169 
1170     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1171     pub fn set_device_major(&mut self, major: u32) {
1172         octal_into(&mut self.dev_major, major);
1173     }
1174 
1175     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1176     pub fn device_minor(&self) -> io::Result<u32> {
1177         octal_from(&self.dev_minor)
1178             .map(|u| u as u32)
1179             .map_err(|err| {
1180                 io::Error::new(
1181                     err.kind(),
1182                     format!(
1183                         "{} when getting device_minor for {}",
1184                         err,
1185                         self.fullname_lossy()
1186                     ),
1187                 )
1188             })
1189     }
1190 
1191     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1192     pub fn set_device_minor(&mut self, minor: u32) {
1193         octal_into(&mut self.dev_minor, minor);
1194     }
1195 
1196     /// Returns the last modification time in Unix time format
atime(&self) -> io::Result<u64>1197     pub fn atime(&self) -> io::Result<u64> {
1198         num_field_wrapper_from(&self.atime).map_err(|err| {
1199             io::Error::new(
1200                 err.kind(),
1201                 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1202             )
1203         })
1204     }
1205 
1206     /// Encodes the `atime` provided into this header.
1207     ///
1208     /// Note that this time is typically a number of seconds passed since
1209     /// January 1, 1970.
set_atime(&mut self, atime: u64)1210     pub fn set_atime(&mut self, atime: u64) {
1211         num_field_wrapper_into(&mut self.atime, atime);
1212     }
1213 
1214     /// Returns the last modification time in Unix time format
ctime(&self) -> io::Result<u64>1215     pub fn ctime(&self) -> io::Result<u64> {
1216         num_field_wrapper_from(&self.ctime).map_err(|err| {
1217             io::Error::new(
1218                 err.kind(),
1219                 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1220             )
1221         })
1222     }
1223 
1224     /// Encodes the `ctime` provided into this header.
1225     ///
1226     /// Note that this time is typically a number of seconds passed since
1227     /// January 1, 1970.
set_ctime(&mut self, ctime: u64)1228     pub fn set_ctime(&mut self, ctime: u64) {
1229         num_field_wrapper_into(&mut self.ctime, ctime);
1230     }
1231 
1232     /// Returns the "real size" of the file this header represents.
1233     ///
1234     /// This is applicable for sparse files where the returned size here is the
1235     /// size of the entire file after the sparse regions have been filled in.
real_size(&self) -> io::Result<u64>1236     pub fn real_size(&self) -> io::Result<u64> {
1237         octal_from(&self.realsize).map_err(|err| {
1238             io::Error::new(
1239                 err.kind(),
1240                 format!(
1241                     "{} when getting real_size for {}",
1242                     err,
1243                     self.fullname_lossy()
1244                 ),
1245             )
1246         })
1247     }
1248 
1249     /// Indicates whether this header will be followed by additional
1250     /// sparse-header records.
1251     ///
1252     /// Note that this is handled internally by this library, and is likely only
1253     /// interesting if a `raw` iterator is being used.
is_extended(&self) -> bool1254     pub fn is_extended(&self) -> bool {
1255         self.isextended[0] == 1
1256     }
1257 
1258     /// Views this as a normal `Header`
as_header(&self) -> &Header1259     pub fn as_header(&self) -> &Header {
1260         unsafe { cast(self) }
1261     }
1262 
1263     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1264     pub fn as_header_mut(&mut self) -> &mut Header {
1265         unsafe { cast_mut(self) }
1266     }
1267 }
1268 
1269 impl fmt::Debug for GnuHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1270     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1271         let mut f = f.debug_struct("GnuHeader");
1272         self.as_header().debug_fields(&mut f);
1273         if let Ok(atime) = self.atime() {
1274             f.field("atime", &atime);
1275         }
1276         if let Ok(ctime) = self.ctime() {
1277             f.field("ctime", &ctime);
1278         }
1279         f.field("is_extended", &self.is_extended())
1280             .field("sparse", &DebugSparseHeaders(&self.sparse))
1281             .finish()
1282     }
1283 }
1284 
1285 struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1286 
1287 impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1288     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1289         let mut f = f.debug_list();
1290         for header in self.0 {
1291             if !header.is_empty() {
1292                 f.entry(header);
1293             }
1294         }
1295         f.finish()
1296     }
1297 }
1298 
1299 impl GnuSparseHeader {
1300     /// Returns true if block is empty
is_empty(&self) -> bool1301     pub fn is_empty(&self) -> bool {
1302         self.offset[0] == 0 || self.numbytes[0] == 0
1303     }
1304 
1305     /// Offset of the block from the start of the file
1306     ///
1307     /// Returns `Err` for a malformed `offset` field.
offset(&self) -> io::Result<u64>1308     pub fn offset(&self) -> io::Result<u64> {
1309         octal_from(&self.offset).map_err(|err| {
1310             io::Error::new(
1311                 err.kind(),
1312                 format!("{} when getting offset from sparse header", err),
1313             )
1314         })
1315     }
1316 
1317     /// Length of the block
1318     ///
1319     /// Returns `Err` for a malformed `numbytes` field.
length(&self) -> io::Result<u64>1320     pub fn length(&self) -> io::Result<u64> {
1321         octal_from(&self.numbytes).map_err(|err| {
1322             io::Error::new(
1323                 err.kind(),
1324                 format!("{} when getting length from sparse header", err),
1325             )
1326         })
1327     }
1328 }
1329 
1330 impl fmt::Debug for GnuSparseHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1331     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1332         let mut f = f.debug_struct("GnuSparseHeader");
1333         if let Ok(offset) = self.offset() {
1334             f.field("offset", &offset);
1335         }
1336         if let Ok(length) = self.length() {
1337             f.field("length", &length);
1338         }
1339         f.finish()
1340     }
1341 }
1342 
1343 impl GnuExtSparseHeader {
1344     /// Crates a new zero'd out sparse header entry.
new() -> GnuExtSparseHeader1345     pub fn new() -> GnuExtSparseHeader {
1346         unsafe { mem::zeroed() }
1347     }
1348 
1349     /// Returns a view into this header as a byte array.
as_bytes(&self) -> &[u8; 512]1350     pub fn as_bytes(&self) -> &[u8; 512] {
1351         debug_assert_eq!(mem::size_of_val(self), 512);
1352         unsafe { mem::transmute(self) }
1353     }
1354 
1355     /// Returns a view into this header as a byte array.
as_mut_bytes(&mut self) -> &mut [u8; 512]1356     pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1357         debug_assert_eq!(mem::size_of_val(self), 512);
1358         unsafe { mem::transmute(self) }
1359     }
1360 
1361     /// Returns a slice of the underlying sparse headers.
1362     ///
1363     /// Some headers may represent empty chunks of both the offset and numbytes
1364     /// fields are 0.
sparse(&self) -> &[GnuSparseHeader; 21]1365     pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1366         &self.sparse
1367     }
1368 
1369     /// Indicates if another sparse header should be following this one.
is_extended(&self) -> bool1370     pub fn is_extended(&self) -> bool {
1371         self.isextended[0] == 1
1372     }
1373 }
1374 
1375 impl Default for GnuExtSparseHeader {
default() -> Self1376     fn default() -> Self {
1377         Self::new()
1378     }
1379 }
1380 
octal_from(slice: &[u8]) -> io::Result<u64>1381 fn octal_from(slice: &[u8]) -> io::Result<u64> {
1382     let trun = truncate(slice);
1383     let num = match str::from_utf8(trun) {
1384         Ok(n) => n,
1385         Err(_) => {
1386             return Err(other(&format!(
1387                 "numeric field did not have utf-8 text: {}",
1388                 String::from_utf8_lossy(trun)
1389             )));
1390         }
1391     };
1392     match u64::from_str_radix(num.trim(), 8) {
1393         Ok(n) => Ok(n),
1394         Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1395     }
1396 }
1397 
octal_into<T: fmt::Octal>(dst: &mut [u8], val: T)1398 fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1399     let o = format!("{:o}", val);
1400     let value = o.bytes().rev().chain(repeat(b'0'));
1401     for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1402         *slot = value;
1403     }
1404 }
1405 
1406 // Wrapper to figure out if we should fill the header field using tar's numeric
1407 // extension (binary) or not (octal).
num_field_wrapper_into(dst: &mut [u8], src: u64)1408 fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1409     if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1410         numeric_extended_into(dst, src);
1411     } else {
1412         octal_into(dst, src);
1413     }
1414 }
1415 
1416 // Wrapper to figure out if we should read the header field in binary (numeric
1417 // extension) or octal (standard encoding).
num_field_wrapper_from(src: &[u8]) -> io::Result<u64>1418 fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1419     if src[0] & 0x80 != 0 {
1420         Ok(numeric_extended_from(src))
1421     } else {
1422         octal_from(src)
1423     }
1424 }
1425 
1426 // When writing numeric fields with is the extended form, the high bit of the
1427 // first byte is set to 1 and the remainder of the field is treated as binary
1428 // instead of octal ascii.
1429 // This handles writing u64 to 8 (uid, gid) or 12 (size, *time) bytes array.
numeric_extended_into(dst: &mut [u8], src: u64)1430 fn numeric_extended_into(dst: &mut [u8], src: u64) {
1431     let len: usize = dst.len();
1432     for (slot, val) in dst.iter_mut().zip(
1433         repeat(0)
1434             .take(len - 8) // to zero init extra bytes
1435             .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1436     ) {
1437         *slot = val;
1438     }
1439     dst[0] |= 0x80;
1440 }
1441 
numeric_extended_from(src: &[u8]) -> u641442 fn numeric_extended_from(src: &[u8]) -> u64 {
1443     let mut dst: u64 = 0;
1444     let mut b_to_skip = 1;
1445     if src.len() == 8 {
1446         // read first byte without extension flag bit
1447         dst = (src[0] ^ 0x80) as u64;
1448     } else {
1449         // only read last 8 bytes
1450         b_to_skip = src.len() - 8;
1451     }
1452     for byte in src.iter().skip(b_to_skip) {
1453         dst <<= 8;
1454         dst |= *byte as u64;
1455     }
1456     dst
1457 }
1458 
truncate(slice: &[u8]) -> &[u8]1459 fn truncate(slice: &[u8]) -> &[u8] {
1460     match slice.iter().position(|i| *i == 0) {
1461         Some(i) => &slice[..i],
1462         None => slice,
1463     }
1464 }
1465 
1466 /// Copies `bytes` into the `slot` provided, returning an error if the `bytes`
1467 /// array is too long or if it contains any nul bytes.
copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()>1468 fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1469     if bytes.len() > slot.len() {
1470         Err(other("provided value is too long"))
1471     } else if bytes.iter().any(|b| *b == 0) {
1472         Err(other("provided value contains a nul byte"))
1473     } else {
1474         for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1475             *slot = *val;
1476         }
1477         Ok(())
1478     }
1479 }
1480 
1481 /// Copies `path` into the `slot` provided
1482 ///
1483 /// Returns an error if:
1484 ///
1485 /// * the path is too long to fit
1486 /// * a nul byte was found
1487 /// * an invalid path component is encountered (e.g. a root path or parent dir)
1488 /// * the path itself is empty
copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()>1489 fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1490     let mut emitted = false;
1491     let mut needs_slash = false;
1492     for component in path.components() {
1493         let bytes = path2bytes(Path::new(component.as_os_str()))?;
1494         match (component, is_link_name) {
1495             (Component::Prefix(..), false) | (Component::RootDir, false) => {
1496                 return Err(other("paths in archives must be relative"));
1497             }
1498             (Component::ParentDir, false) => {
1499                 return Err(other("paths in archives must not have `..`"));
1500             }
1501             // Allow "./" as the path
1502             (Component::CurDir, false) if path.components().count() == 1 => {}
1503             (Component::CurDir, false) => continue,
1504             (Component::Normal(_), _) | (_, true) => {}
1505         };
1506         if needs_slash {
1507             copy(&mut slot, b"/")?;
1508         }
1509         if bytes.contains(&b'/') {
1510             if let Component::Normal(..) = component {
1511                 return Err(other("path component in archive cannot contain `/`"));
1512             }
1513         }
1514         copy(&mut slot, &*bytes)?;
1515         if &*bytes != b"/" {
1516             needs_slash = true;
1517         }
1518         emitted = true;
1519     }
1520     if !emitted {
1521         return Err(other("paths in archives must have at least one component"));
1522     }
1523     if ends_with_slash(path) {
1524         copy(&mut slot, &[b'/'])?;
1525     }
1526     return Ok(());
1527 
1528     fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1529         copy_into(*slot, bytes)?;
1530         let tmp = mem::replace(slot, &mut []);
1531         *slot = &mut tmp[bytes.len()..];
1532         Ok(())
1533     }
1534 }
1535 
1536 #[cfg(target_arch = "wasm32")]
ends_with_slash(p: &Path) -> bool1537 fn ends_with_slash(p: &Path) -> bool {
1538     p.to_string_lossy().ends_with('/')
1539 }
1540 
1541 #[cfg(windows)]
ends_with_slash(p: &Path) -> bool1542 fn ends_with_slash(p: &Path) -> bool {
1543     let last = p.as_os_str().encode_wide().last();
1544     last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1545 }
1546 
1547 #[cfg(unix)]
ends_with_slash(p: &Path) -> bool1548 fn ends_with_slash(p: &Path) -> bool {
1549     p.as_os_str().as_bytes().ends_with(&[b'/'])
1550 }
1551 
1552 #[cfg(any(windows, target_arch = "wasm32"))]
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1553 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1554     p.as_os_str()
1555         .to_str()
1556         .map(|s| s.as_bytes())
1557         .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1558         .map(|bytes| {
1559             if bytes.contains(&b'\\') {
1560                 // Normalize to Unix-style path separators
1561                 let mut bytes = bytes.to_owned();
1562                 for b in &mut bytes {
1563                     if *b == b'\\' {
1564                         *b = b'/';
1565                     }
1566                 }
1567                 Cow::Owned(bytes)
1568             } else {
1569                 Cow::Borrowed(bytes)
1570             }
1571         })
1572 }
1573 
1574 #[cfg(unix)]
1575 /// On unix this will never fail
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1576 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1577     Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1578 }
1579 
1580 #[cfg(windows)]
1581 /// On windows we cannot accept non-Unicode bytes because it
1582 /// is impossible to convert it to UTF-16.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1583 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1584     return match bytes {
1585         Cow::Borrowed(bytes) => {
1586             let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1587             Ok(Cow::Borrowed(Path::new(s)))
1588         }
1589         Cow::Owned(bytes) => {
1590             let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1591             Ok(Cow::Owned(PathBuf::from(s)))
1592         }
1593     };
1594 
1595     fn not_unicode(v: &[u8]) -> io::Error {
1596         other(&format!(
1597             "only Unicode paths are supported on Windows: {}",
1598             String::from_utf8_lossy(v)
1599         ))
1600     }
1601 }
1602 
1603 #[cfg(unix)]
1604 /// On unix this operation can never fail.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1605 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1606     use std::ffi::{OsStr, OsString};
1607 
1608     Ok(match bytes {
1609         Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1610         Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1611     })
1612 }
1613 
1614 #[cfg(target_arch = "wasm32")]
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1615 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1616     Ok(match bytes {
1617         Cow::Borrowed(bytes) => {
1618             Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1619         }
1620         Cow::Owned(bytes) => {
1621             Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1622         }
1623     })
1624 }
1625 
1626 #[cfg(target_arch = "wasm32")]
invalid_utf8<T>(_: T) -> io::Error1627 fn invalid_utf8<T>(_: T) -> io::Error {
1628     io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1629 }
1630