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