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