tar_rsl/
header.rs

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