Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

tar/
header.rs

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