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: &UstarHeader) = self.as_ustar() {
949 me.fmt(f)
950 } else if let Some(me: &GnuHeader) = 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: DebugStruct<'_, '_> = f.debug_struct(name:"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: DebugStruct<'_, '_> = f.debug_struct(name:"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: DebugStruct<'_, '_> = f.debug_struct(name:"GnuHeader");
1329 self.as_header().debug_fields(&mut f);
1330 if let Ok(atime: u64) = self.atime() {
1331 f.field(name:"atime", &atime);
1332 }
1333 if let Ok(ctime: u64) = self.ctime() {
1334 f.field(name:"ctime", &ctime);
1335 }
1336 f&mut DebugStruct<'_, '_>.field("is_extended", &self.is_extended())
1337 .field(name:"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: DebugList<'_, '_> = f.debug_list();
1347 for header: &'a GnuSparseHeader 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: DebugStruct<'_, '_> = f.debug_struct(name:"GnuSparseHeader");
1400 if let Ok(offset: u64) = self.offset() {
1401 f.field(name:"offset", &offset);
1402 }
1403 if let Ok(length: u64) = self.length() {
1404 f.field(name:"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: &[u8] = truncate(slice);
1460 let num: &str = match str::from_utf8(trun) {
1461 Ok(n: &str) => 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(src:num.trim(), radix:8) {
1470 Ok(n: u64) => 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: String = format!("{:o}", val);
1477 let value: impl Iterator = once(b'\0').chain(o.bytes().rev().chain(repeat(elt:b'0')));
1478 for (slot: &mut u8, value: u8) 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, val: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(slice: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: &mut u8, val: u8) in dst.iter_mut().zip(
1510 repeatimpl Iterator(elt:0)
1511 .take(len - 8) // to zero init extra bytes
1512 .chain((0..8).rev().map(|x: i32| ((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: usize = 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: &u8 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: &u8| *i == 0) {
1538 Some(i: usize) => &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(msg:"provided value is too long"))
1548 } else if bytes.iter().any(|b: &u8| *b == 0) {
1549 Err(other(msg:"provided value contains a nul byte"))
1550 } else {
1551 for (slot: &mut u8, val: &u8) 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, is_truncated_gnu_long_path: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, is_truncated_gnu_long_path: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(op: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: &[u8]) => Cow::Borrowed(Path::new(OsStr::from_bytes(slice:bytes))),
1716 Cow::Owned(bytes: Vec) => 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}
1736