async_tar/
header.rs

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