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                 self.set_mtime(0);
738                 self.set_uid(0);
739                 self.set_gid(0);
740 
741                 // Use a default umask value, but propagate the (user) execute bit.
742                 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
743                     0o755
744                 } else {
745                     0o644
746                 };
747                 self.set_mode(fs_mode);
748             }
749             HeaderMode::__Nonexhaustive => panic!(),
750         }
751 
752         // Note that if we are a GNU header we *could* set atime/ctime, except
753         // the `tar` utility doesn't do that by default and it causes problems
754         // with 7-zip [1].
755         //
756         // It's always possible to fill them out manually, so we just don't fill
757         // it out automatically here.
758         //
759         // [1]: https://github.com/alexcrichton/tar-rs/issues/70
760 
761         // TODO: need to bind more file types
762         self.set_entry_type(entry_type(meta.mode()));
763 
764         fn entry_type(mode: u32) -> EntryType {
765             match mode as libc::mode_t & libc::S_IFMT {
766                 libc::S_IFREG => EntryType::file(),
767                 libc::S_IFLNK => EntryType::symlink(),
768                 libc::S_IFCHR => EntryType::character_special(),
769                 libc::S_IFBLK => EntryType::block_special(),
770                 libc::S_IFDIR => EntryType::dir(),
771                 libc::S_IFIFO => EntryType::fifo(),
772                 _ => EntryType::new(b' '),
773             }
774         }
775     }
776 
777     #[cfg(windows)]
fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode)778     fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
779         // There's no concept of a file mode on Windows, so do a best approximation here.
780         match mode {
781             HeaderMode::Complete => {
782                 self.set_uid(0);
783                 self.set_gid(0);
784                 // The dates listed in tarballs are always seconds relative to
785                 // January 1, 1970. On Windows, however, the timestamps are returned as
786                 // dates relative to January 1, 1601 (in 100ns intervals), so we need to
787                 // add in some offset for those dates.
788                 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
789                 self.set_mtime(mtime);
790                 let fs_mode = {
791                     const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
792                     let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
793                     match (meta.is_dir(), readonly != 0) {
794                         (true, false) => 0o755,
795                         (true, true) => 0o555,
796                         (false, false) => 0o644,
797                         (false, true) => 0o444,
798                     }
799                 };
800                 self.set_mode(fs_mode);
801             }
802             HeaderMode::Deterministic => {
803                 self.set_uid(0);
804                 self.set_gid(0);
805                 self.set_mtime(0);
806                 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
807                 self.set_mode(fs_mode);
808             }
809             HeaderMode::__Nonexhaustive => panic!(),
810         }
811 
812         let ft = meta.file_type();
813         self.set_entry_type(if ft.is_dir() {
814             EntryType::dir()
815         } else if ft.is_file() {
816             EntryType::file()
817         } else if ft.is_symlink() {
818             EntryType::symlink()
819         } else {
820             EntryType::new(b' ')
821         });
822     }
823 
debug_fields(&self, b: &mut fmt::DebugStruct)824     fn debug_fields(&self, b: &mut fmt::DebugStruct) {
825         if let Ok(entry_size) = self.entry_size() {
826             b.field("entry_size", &entry_size);
827         }
828         if let Ok(size) = self.size() {
829             b.field("size", &size);
830         }
831         if let Ok(path) = self.path() {
832             b.field("path", &path);
833         }
834         if let Ok(link_name) = self.link_name() {
835             b.field("link_name", &link_name);
836         }
837         if let Ok(mode) = self.mode() {
838             b.field("mode", &DebugAsOctal(mode));
839         }
840         if let Ok(uid) = self.uid() {
841             b.field("uid", &uid);
842         }
843         if let Ok(gid) = self.gid() {
844             b.field("gid", &gid);
845         }
846         if let Ok(mtime) = self.mtime() {
847             b.field("mtime", &mtime);
848         }
849         if let Ok(username) = self.username() {
850             b.field("username", &username);
851         }
852         if let Ok(groupname) = self.groupname() {
853             b.field("groupname", &groupname);
854         }
855         if let Ok(device_major) = self.device_major() {
856             b.field("device_major", &device_major);
857         }
858         if let Ok(device_minor) = self.device_minor() {
859             b.field("device_minor", &device_minor);
860         }
861         if let Ok(cksum) = self.cksum() {
862             b.field("cksum", &cksum);
863             b.field("cksum_valid", &(cksum == self.calculate_cksum()));
864         }
865     }
866 }
867 
868 struct DebugAsOctal<T>(T);
869 
870 impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result871     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
872         fmt::Octal::fmt(&self.0, f)
873     }
874 }
875 
cast<T, U>(a: &T) -> &U876 unsafe fn cast<T, U>(a: &T) -> &U {
877     assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
878     assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
879     &*(a as *const T as *const U)
880 }
881 
cast_mut<T, U>(a: &mut T) -> &mut U882 unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
883     assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
884     assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
885     &mut *(a as *mut T as *mut U)
886 }
887 
888 impl Clone for Header {
clone(&self) -> Header889     fn clone(&self) -> Header {
890         Header { bytes: self.bytes }
891     }
892 }
893 
894 impl fmt::Debug for Header {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result895     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
896         if let Some(me) = self.as_ustar() {
897             me.fmt(f)
898         } else if let Some(me) = self.as_gnu() {
899             me.fmt(f)
900         } else {
901             self.as_old().fmt(f)
902         }
903     }
904 }
905 
906 impl OldHeader {
907     /// Views this as a normal `Header`
as_header(&self) -> &Header908     pub fn as_header(&self) -> &Header {
909         unsafe { cast(self) }
910     }
911 
912     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header913     pub fn as_header_mut(&mut self) -> &mut Header {
914         unsafe { cast_mut(self) }
915     }
916 }
917 
918 impl fmt::Debug for OldHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result919     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
920         let mut f = f.debug_struct("OldHeader");
921         self.as_header().debug_fields(&mut f);
922         f.finish()
923     }
924 }
925 
926 impl UstarHeader {
927     /// See `Header::path_bytes`
path_bytes(&self) -> Cow<[u8]>928     pub fn path_bytes(&self) -> Cow<[u8]> {
929         if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
930             Cow::Borrowed(truncate(&self.name))
931         } else {
932             let mut bytes = Vec::new();
933             let prefix = truncate(&self.prefix);
934             if prefix.len() > 0 {
935                 bytes.extend_from_slice(prefix);
936                 bytes.push(b'/');
937             }
938             bytes.extend_from_slice(truncate(&self.name));
939             Cow::Owned(bytes)
940         }
941     }
942 
943     /// Gets the path in a "lossy" way, used for error reporting ONLY.
path_lossy(&self) -> String944     fn path_lossy(&self) -> String {
945         String::from_utf8_lossy(&self.path_bytes()).to_string()
946     }
947 
948     /// See `Header::set_path`
set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()>949     pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
950         self._set_path(p.as_ref())
951     }
952 
_set_path(&mut self, path: &Path) -> io::Result<()>953     fn _set_path(&mut self, path: &Path) -> io::Result<()> {
954         // This can probably be optimized quite a bit more, but for now just do
955         // something that's relatively easy and readable.
956         //
957         // First up, if the path fits within `self.name` then we just shove it
958         // in there. If not then we try to split it between some existing path
959         // components where it can fit in name/prefix. To do that we peel off
960         // enough until the path fits in `prefix`, then we try to put both
961         // halves into their destination.
962         let bytes = path2bytes(path)?;
963         let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
964         if bytes.len() <= maxnamelen {
965             copy_path_into(&mut self.name, path, false).map_err(|err| {
966                 io::Error::new(
967                     err.kind(),
968                     format!("{} when setting path for {}", err, self.path_lossy()),
969                 )
970             })?;
971         } else {
972             let mut prefix = path;
973             let mut prefixlen;
974             loop {
975                 match prefix.parent() {
976                     Some(parent) => prefix = parent,
977                     None => {
978                         return Err(other(&format!(
979                             "path cannot be split to be inserted into archive: {}",
980                             path.display()
981                         )));
982                     }
983                 }
984                 prefixlen = path2bytes(prefix)?.len();
985                 if prefixlen <= maxprefixlen {
986                     break;
987                 }
988             }
989             copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
990                 io::Error::new(
991                     err.kind(),
992                     format!("{} when setting path for {}", err, self.path_lossy()),
993                 )
994             })?;
995             let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
996             copy_path_into(&mut self.name, &path, false).map_err(|err| {
997                 io::Error::new(
998                     err.kind(),
999                     format!("{} when setting path for {}", err, self.path_lossy()),
1000                 )
1001             })?;
1002         }
1003         Ok(())
1004     }
1005 
1006     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1007     pub fn username_bytes(&self) -> &[u8] {
1008         truncate(&self.uname)
1009     }
1010 
1011     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1012     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1013         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1014             io::Error::new(
1015                 err.kind(),
1016                 format!("{} when setting username for {}", err, self.path_lossy()),
1017             )
1018         })
1019     }
1020 
1021     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1022     pub fn groupname_bytes(&self) -> &[u8] {
1023         truncate(&self.gname)
1024     }
1025 
1026     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1027     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1028         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1029             io::Error::new(
1030                 err.kind(),
1031                 format!("{} when setting groupname for {}", err, self.path_lossy()),
1032             )
1033         })
1034     }
1035 
1036     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1037     pub fn device_major(&self) -> io::Result<u32> {
1038         octal_from(&self.dev_major)
1039             .map(|u| u as u32)
1040             .map_err(|err| {
1041                 io::Error::new(
1042                     err.kind(),
1043                     format!(
1044                         "{} when getting device_major for {}",
1045                         err,
1046                         self.path_lossy()
1047                     ),
1048                 )
1049             })
1050     }
1051 
1052     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1053     pub fn set_device_major(&mut self, major: u32) {
1054         octal_into(&mut self.dev_major, major);
1055     }
1056 
1057     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1058     pub fn device_minor(&self) -> io::Result<u32> {
1059         octal_from(&self.dev_minor)
1060             .map(|u| u as u32)
1061             .map_err(|err| {
1062                 io::Error::new(
1063                     err.kind(),
1064                     format!(
1065                         "{} when getting device_minor for {}",
1066                         err,
1067                         self.path_lossy()
1068                     ),
1069                 )
1070             })
1071     }
1072 
1073     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1074     pub fn set_device_minor(&mut self, minor: u32) {
1075         octal_into(&mut self.dev_minor, minor);
1076     }
1077 
1078     /// Views this as a normal `Header`
as_header(&self) -> &Header1079     pub fn as_header(&self) -> &Header {
1080         unsafe { cast(self) }
1081     }
1082 
1083     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1084     pub fn as_header_mut(&mut self) -> &mut Header {
1085         unsafe { cast_mut(self) }
1086     }
1087 }
1088 
1089 impl fmt::Debug for UstarHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1090     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1091         let mut f = f.debug_struct("UstarHeader");
1092         self.as_header().debug_fields(&mut f);
1093         f.finish()
1094     }
1095 }
1096 
1097 impl GnuHeader {
1098     /// See `Header::username_bytes`
username_bytes(&self) -> &[u8]1099     pub fn username_bytes(&self) -> &[u8] {
1100         truncate(&self.uname)
1101     }
1102 
1103     /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
fullname_lossy(&self) -> String1104     fn fullname_lossy(&self) -> String {
1105         format!(
1106             "{}:{}",
1107             String::from_utf8_lossy(&self.groupname_bytes()),
1108             String::from_utf8_lossy(&self.username_bytes()),
1109         )
1110     }
1111 
1112     /// See `Header::set_username`
set_username(&mut self, name: &str) -> io::Result<()>1113     pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1114         copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1115             io::Error::new(
1116                 err.kind(),
1117                 format!(
1118                     "{} when setting username for {}",
1119                     err,
1120                     self.fullname_lossy()
1121                 ),
1122             )
1123         })
1124     }
1125 
1126     /// See `Header::groupname_bytes`
groupname_bytes(&self) -> &[u8]1127     pub fn groupname_bytes(&self) -> &[u8] {
1128         truncate(&self.gname)
1129     }
1130 
1131     /// See `Header::set_groupname`
set_groupname(&mut self, name: &str) -> io::Result<()>1132     pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1133         copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1134             io::Error::new(
1135                 err.kind(),
1136                 format!(
1137                     "{} when setting groupname for {}",
1138                     err,
1139                     self.fullname_lossy()
1140                 ),
1141             )
1142         })
1143     }
1144 
1145     /// See `Header::device_major`
device_major(&self) -> io::Result<u32>1146     pub fn device_major(&self) -> io::Result<u32> {
1147         octal_from(&self.dev_major)
1148             .map(|u| u as u32)
1149             .map_err(|err| {
1150                 io::Error::new(
1151                     err.kind(),
1152                     format!(
1153                         "{} when getting device_major for {}",
1154                         err,
1155                         self.fullname_lossy()
1156                     ),
1157                 )
1158             })
1159     }
1160 
1161     /// See `Header::set_device_major`
set_device_major(&mut self, major: u32)1162     pub fn set_device_major(&mut self, major: u32) {
1163         octal_into(&mut self.dev_major, major);
1164     }
1165 
1166     /// See `Header::device_minor`
device_minor(&self) -> io::Result<u32>1167     pub fn device_minor(&self) -> io::Result<u32> {
1168         octal_from(&self.dev_minor)
1169             .map(|u| u as u32)
1170             .map_err(|err| {
1171                 io::Error::new(
1172                     err.kind(),
1173                     format!(
1174                         "{} when getting device_minor for {}",
1175                         err,
1176                         self.fullname_lossy()
1177                     ),
1178                 )
1179             })
1180     }
1181 
1182     /// See `Header::set_device_minor`
set_device_minor(&mut self, minor: u32)1183     pub fn set_device_minor(&mut self, minor: u32) {
1184         octal_into(&mut self.dev_minor, minor);
1185     }
1186 
1187     /// Returns the last modification time in Unix time format
atime(&self) -> io::Result<u64>1188     pub fn atime(&self) -> io::Result<u64> {
1189         num_field_wrapper_from(&self.atime).map_err(|err| {
1190             io::Error::new(
1191                 err.kind(),
1192                 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1193             )
1194         })
1195     }
1196 
1197     /// Encodes the `atime` provided into this header.
1198     ///
1199     /// Note that this time is typically a number of seconds passed since
1200     /// January 1, 1970.
set_atime(&mut self, atime: u64)1201     pub fn set_atime(&mut self, atime: u64) {
1202         num_field_wrapper_into(&mut self.atime, atime);
1203     }
1204 
1205     /// Returns the last modification time in Unix time format
ctime(&self) -> io::Result<u64>1206     pub fn ctime(&self) -> io::Result<u64> {
1207         num_field_wrapper_from(&self.ctime).map_err(|err| {
1208             io::Error::new(
1209                 err.kind(),
1210                 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1211             )
1212         })
1213     }
1214 
1215     /// Encodes the `ctime` provided into this header.
1216     ///
1217     /// Note that this time is typically a number of seconds passed since
1218     /// January 1, 1970.
set_ctime(&mut self, ctime: u64)1219     pub fn set_ctime(&mut self, ctime: u64) {
1220         num_field_wrapper_into(&mut self.ctime, ctime);
1221     }
1222 
1223     /// Returns the "real size" of the file this header represents.
1224     ///
1225     /// This is applicable for sparse files where the returned size here is the
1226     /// size of the entire file after the sparse regions have been filled in.
real_size(&self) -> io::Result<u64>1227     pub fn real_size(&self) -> io::Result<u64> {
1228         octal_from(&self.realsize).map_err(|err| {
1229             io::Error::new(
1230                 err.kind(),
1231                 format!(
1232                     "{} when getting real_size for {}",
1233                     err,
1234                     self.fullname_lossy()
1235                 ),
1236             )
1237         })
1238     }
1239 
1240     /// Indicates whether this header will be followed by additional
1241     /// sparse-header records.
1242     ///
1243     /// Note that this is handled internally by this library, and is likely only
1244     /// interesting if a `raw` iterator is being used.
is_extended(&self) -> bool1245     pub fn is_extended(&self) -> bool {
1246         self.isextended[0] == 1
1247     }
1248 
1249     /// Views this as a normal `Header`
as_header(&self) -> &Header1250     pub fn as_header(&self) -> &Header {
1251         unsafe { cast(self) }
1252     }
1253 
1254     /// Views this as a normal `Header`
as_header_mut(&mut self) -> &mut Header1255     pub fn as_header_mut(&mut self) -> &mut Header {
1256         unsafe { cast_mut(self) }
1257     }
1258 }
1259 
1260 impl fmt::Debug for GnuHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1261     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1262         let mut f = f.debug_struct("GnuHeader");
1263         self.as_header().debug_fields(&mut f);
1264         if let Ok(atime) = self.atime() {
1265             f.field("atime", &atime);
1266         }
1267         if let Ok(ctime) = self.ctime() {
1268             f.field("ctime", &ctime);
1269         }
1270         f.field("is_extended", &self.is_extended())
1271             .field("sparse", &DebugSparseHeaders(&self.sparse))
1272             .finish()
1273     }
1274 }
1275 
1276 struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1277 
1278 impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1279     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1280         let mut f = f.debug_list();
1281         for header in self.0 {
1282             if !header.is_empty() {
1283                 f.entry(header);
1284             }
1285         }
1286         f.finish()
1287     }
1288 }
1289 
1290 impl GnuSparseHeader {
1291     /// Returns true if block is empty
is_empty(&self) -> bool1292     pub fn is_empty(&self) -> bool {
1293         self.offset[0] == 0 || self.numbytes[0] == 0
1294     }
1295 
1296     /// Offset of the block from the start of the file
1297     ///
1298     /// Returns `Err` for a malformed `offset` field.
offset(&self) -> io::Result<u64>1299     pub fn offset(&self) -> io::Result<u64> {
1300         octal_from(&self.offset).map_err(|err| {
1301             io::Error::new(
1302                 err.kind(),
1303                 format!("{} when getting offset from sparse header", err),
1304             )
1305         })
1306     }
1307 
1308     /// Length of the block
1309     ///
1310     /// Returns `Err` for a malformed `numbytes` field.
length(&self) -> io::Result<u64>1311     pub fn length(&self) -> io::Result<u64> {
1312         octal_from(&self.numbytes).map_err(|err| {
1313             io::Error::new(
1314                 err.kind(),
1315                 format!("{} when getting length from sparse header", err),
1316             )
1317         })
1318     }
1319 }
1320 
1321 impl fmt::Debug for GnuSparseHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1322     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1323         let mut f = f.debug_struct("GnuSparseHeader");
1324         if let Ok(offset) = self.offset() {
1325             f.field("offset", &offset);
1326         }
1327         if let Ok(length) = self.length() {
1328             f.field("length", &length);
1329         }
1330         f.finish()
1331     }
1332 }
1333 
1334 impl GnuExtSparseHeader {
1335     /// Crates a new zero'd out sparse header entry.
new() -> GnuExtSparseHeader1336     pub fn new() -> GnuExtSparseHeader {
1337         unsafe { mem::zeroed() }
1338     }
1339 
1340     /// Returns a view into this header as a byte array.
as_bytes(&self) -> &[u8; 512]1341     pub fn as_bytes(&self) -> &[u8; 512] {
1342         debug_assert_eq!(mem::size_of_val(self), 512);
1343         unsafe { mem::transmute(self) }
1344     }
1345 
1346     /// Returns a view into this header as a byte array.
as_mut_bytes(&mut self) -> &mut [u8; 512]1347     pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1348         debug_assert_eq!(mem::size_of_val(self), 512);
1349         unsafe { mem::transmute(self) }
1350     }
1351 
1352     /// Returns a slice of the underlying sparse headers.
1353     ///
1354     /// Some headers may represent empty chunks of both the offset and numbytes
1355     /// fields are 0.
sparse(&self) -> &[GnuSparseHeader; 21]1356     pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1357         &self.sparse
1358     }
1359 
1360     /// Indicates if another sparse header should be following this one.
is_extended(&self) -> bool1361     pub fn is_extended(&self) -> bool {
1362         self.isextended[0] == 1
1363     }
1364 }
1365 
1366 impl Default for GnuExtSparseHeader {
default() -> Self1367     fn default() -> Self {
1368         Self::new()
1369     }
1370 }
1371 
octal_from(slice: &[u8]) -> io::Result<u64>1372 fn octal_from(slice: &[u8]) -> io::Result<u64> {
1373     let trun = truncate(slice);
1374     let num = match str::from_utf8(trun) {
1375         Ok(n) => n,
1376         Err(_) => {
1377             return Err(other(&format!(
1378                 "numeric field did not have utf-8 text: {}",
1379                 String::from_utf8_lossy(trun)
1380             )));
1381         }
1382     };
1383     match u64::from_str_radix(num.trim(), 8) {
1384         Ok(n) => Ok(n),
1385         Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1386     }
1387 }
1388 
octal_into<T: fmt::Octal>(dst: &mut [u8], val: T)1389 fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1390     let o = format!("{:o}", val);
1391     let value = o.bytes().rev().chain(repeat(b'0'));
1392     for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1393         *slot = value;
1394     }
1395 }
1396 
1397 // Wrapper to figure out if we should fill the header field using tar's numeric
1398 // extension (binary) or not (octal).
num_field_wrapper_into(dst: &mut [u8], src: u64)1399 fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1400     if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1401         numeric_extended_into(dst, src);
1402     } else {
1403         octal_into(dst, src);
1404     }
1405 }
1406 
1407 // Wrapper to figure out if we should read the header field in binary (numeric
1408 // extension) or octal (standard encoding).
num_field_wrapper_from(src: &[u8]) -> io::Result<u64>1409 fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1410     if src[0] & 0x80 != 0 {
1411         Ok(numeric_extended_from(src))
1412     } else {
1413         octal_from(src)
1414     }
1415 }
1416 
1417 // When writing numeric fields with is the extended form, the high bit of the
1418 // first byte is set to 1 and the remainder of the field is treated as binary
1419 // instead of octal ascii.
1420 // This handles writing u64 to 8 (uid, gid) or 12 (size, *time) bytes array.
numeric_extended_into(dst: &mut [u8], src: u64)1421 fn numeric_extended_into(dst: &mut [u8], src: u64) {
1422     let len: usize = dst.len();
1423     for (slot, val) in dst.iter_mut().zip(
1424         repeat(0)
1425             .take(len - 8) // to zero init extra bytes
1426             .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1427     ) {
1428         *slot = val;
1429     }
1430     dst[0] |= 0x80;
1431 }
1432 
numeric_extended_from(src: &[u8]) -> u641433 fn numeric_extended_from(src: &[u8]) -> u64 {
1434     let mut dst: u64 = 0;
1435     let mut b_to_skip = 1;
1436     if src.len() == 8 {
1437         // read first byte without extension flag bit
1438         dst = (src[0] ^ 0x80) as u64;
1439     } else {
1440         // only read last 8 bytes
1441         b_to_skip = src.len() - 8;
1442     }
1443     for byte in src.iter().skip(b_to_skip) {
1444         dst <<= 8;
1445         dst |= *byte as u64;
1446     }
1447     dst
1448 }
1449 
truncate(slice: &[u8]) -> &[u8]1450 fn truncate(slice: &[u8]) -> &[u8] {
1451     match slice.iter().position(|i| *i == 0) {
1452         Some(i) => &slice[..i],
1453         None => slice,
1454     }
1455 }
1456 
1457 /// Copies `bytes` into the `slot` provided, returning an error if the `bytes`
1458 /// array is too long or if it contains any nul bytes.
copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()>1459 fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1460     if bytes.len() > slot.len() {
1461         Err(other("provided value is too long"))
1462     } else if bytes.iter().any(|b| *b == 0) {
1463         Err(other("provided value contains a nul byte"))
1464     } else {
1465         for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1466             *slot = *val;
1467         }
1468         Ok(())
1469     }
1470 }
1471 
1472 /// Copies `path` into the `slot` provided
1473 ///
1474 /// Returns an error if:
1475 ///
1476 /// * the path is too long to fit
1477 /// * a nul byte was found
1478 /// * an invalid path component is encountered (e.g. a root path or parent dir)
1479 /// * the path itself is empty
copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()>1480 fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1481     let mut emitted = false;
1482     let mut needs_slash = false;
1483     for component in path.components() {
1484         let bytes = path2bytes(Path::new(component.as_os_str()))?;
1485         match (component, is_link_name) {
1486             (Component::Prefix(..), false) | (Component::RootDir, false) => {
1487                 return Err(other("paths in archives must be relative"));
1488             }
1489             (Component::ParentDir, false) => {
1490                 return Err(other("paths in archives must not have `..`"));
1491             }
1492             // Allow "./" as the path
1493             (Component::CurDir, false) if path.components().count() == 1 => {}
1494             (Component::CurDir, false) => continue,
1495             (Component::Normal(_), _) | (_, true) => {}
1496         };
1497         if needs_slash {
1498             copy(&mut slot, b"/")?;
1499         }
1500         if bytes.contains(&b'/') {
1501             if let Component::Normal(..) = component {
1502                 return Err(other("path component in archive cannot contain `/`"));
1503             }
1504         }
1505         copy(&mut slot, &*bytes)?;
1506         if &*bytes != b"/" {
1507             needs_slash = true;
1508         }
1509         emitted = true;
1510     }
1511     if !emitted {
1512         return Err(other("paths in archives must have at least one component"));
1513     }
1514     if ends_with_slash(path) {
1515         copy(&mut slot, &[b'/'])?;
1516     }
1517     return Ok(());
1518 
1519     fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1520         copy_into(*slot, bytes)?;
1521         let tmp = mem::replace(slot, &mut []);
1522         *slot = &mut tmp[bytes.len()..];
1523         Ok(())
1524     }
1525 }
1526 
1527 #[cfg(target_arch = "wasm32")]
ends_with_slash(p: &Path) -> bool1528 fn ends_with_slash(p: &Path) -> bool {
1529     p.to_string_lossy().ends_with('/')
1530 }
1531 
1532 #[cfg(windows)]
ends_with_slash(p: &Path) -> bool1533 fn ends_with_slash(p: &Path) -> bool {
1534     let last = p.as_os_str().encode_wide().last();
1535     last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1536 }
1537 
1538 #[cfg(unix)]
ends_with_slash(p: &Path) -> bool1539 fn ends_with_slash(p: &Path) -> bool {
1540     p.as_os_str().as_bytes().ends_with(&[b'/'])
1541 }
1542 
1543 #[cfg(any(windows, target_arch = "wasm32"))]
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1544 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1545     p.as_os_str()
1546         .to_str()
1547         .map(|s| s.as_bytes())
1548         .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1549         .map(|bytes| {
1550             if bytes.contains(&b'\\') {
1551                 // Normalize to Unix-style path separators
1552                 let mut bytes = bytes.to_owned();
1553                 for b in &mut bytes {
1554                     if *b == b'\\' {
1555                         *b = b'/';
1556                     }
1557                 }
1558                 Cow::Owned(bytes)
1559             } else {
1560                 Cow::Borrowed(bytes)
1561             }
1562         })
1563 }
1564 
1565 #[cfg(unix)]
1566 /// On unix this will never fail
path2bytes(p: &Path) -> io::Result<Cow<[u8]>>1567 pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1568     Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1569 }
1570 
1571 #[cfg(windows)]
1572 /// On windows we cannot accept non-Unicode bytes because it
1573 /// is impossible to convert it to UTF-16.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1574 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1575     return match bytes {
1576         Cow::Borrowed(bytes) => {
1577             let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1578             Ok(Cow::Borrowed(Path::new(s)))
1579         }
1580         Cow::Owned(bytes) => {
1581             let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1582             Ok(Cow::Owned(PathBuf::from(s)))
1583         }
1584     };
1585 
1586     fn not_unicode(v: &[u8]) -> io::Error {
1587         other(&format!(
1588             "only Unicode paths are supported on Windows: {}",
1589             String::from_utf8_lossy(v)
1590         ))
1591     }
1592 }
1593 
1594 #[cfg(unix)]
1595 /// On unix this operation can never fail.
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1596 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1597     use std::ffi::{OsStr, OsString};
1598 
1599     Ok(match bytes {
1600         Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1601         Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1602     })
1603 }
1604 
1605 #[cfg(target_arch = "wasm32")]
bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>>1606 pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1607     Ok(match bytes {
1608         Cow::Borrowed(bytes) => {
1609             Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1610         }
1611         Cow::Owned(bytes) => {
1612             Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1613         }
1614     })
1615 }
1616 
1617 #[cfg(target_arch = "wasm32")]
invalid_utf8<T>(_: T) -> io::Error1618 fn invalid_utf8<T>(_: T) -> io::Error {
1619     io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1620 }
1621