binstall_tar/
header.rs

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