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