tar/
header.rs

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