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