1#[cfg(any(unix, target_os = "redox"))]
2use std::os::unix::prelude::*;
3#[cfg(windows)]
4use std::os::windows::prelude::*;
5
6use std::{borrow::Cow, fmt, iter, iter::repeat, iter::repeat_n, mem, str};
7
8#[cfg(all(windows, feature = "runtime-async-std"))]
9use async_std::fs;
10#[cfg(feature = "runtime-async-std")]
11use async_std::{
12 fs::Metadata,
13 io,
14 path::{Component, Path, PathBuf},
15};
16#[cfg(feature = "runtime-tokio")]
17use std::{
18 fs::Metadata,
19 path::{Component, Path, PathBuf},
20};
21#[cfg(all(windows, feature = "runtime-tokio"))]
22use tokio::fs;
23#[cfg(feature = "runtime-tokio")]
24use tokio::io;
25
26use crate::{EntryType, other};
27
28#[repr(C)]
30#[allow(missing_docs)]
31pub struct Header {
32 bytes: [u8; 512],
33}
34
35#[derive(Clone, Copy, PartialEq, Eq, Debug)]
38#[non_exhaustive]
39pub enum HeaderMode {
40 Complete,
43
44 Deterministic,
47}
48
49#[repr(C)]
51#[allow(missing_docs)]
52pub struct OldHeader {
53 pub name: [u8; 100],
54 pub mode: [u8; 8],
55 pub uid: [u8; 8],
56 pub gid: [u8; 8],
57 pub size: [u8; 12],
58 pub mtime: [u8; 12],
59 pub cksum: [u8; 8],
60 pub linkflag: [u8; 1],
61 pub linkname: [u8; 100],
62 pub pad: [u8; 255],
63}
64
65#[repr(C)]
67#[allow(missing_docs)]
68pub struct UstarHeader {
69 pub name: [u8; 100],
70 pub mode: [u8; 8],
71 pub uid: [u8; 8],
72 pub gid: [u8; 8],
73 pub size: [u8; 12],
74 pub mtime: [u8; 12],
75 pub cksum: [u8; 8],
76 pub typeflag: [u8; 1],
77 pub linkname: [u8; 100],
78
79 pub magic: [u8; 6],
81 pub version: [u8; 2],
82 pub uname: [u8; 32],
83 pub gname: [u8; 32],
84 pub dev_major: [u8; 8],
85 pub dev_minor: [u8; 8],
86 pub prefix: [u8; 155],
87 pub pad: [u8; 12],
88}
89
90#[repr(C)]
92#[allow(missing_docs)]
93pub struct GnuHeader {
94 pub name: [u8; 100],
95 pub mode: [u8; 8],
96 pub uid: [u8; 8],
97 pub gid: [u8; 8],
98 pub size: [u8; 12],
99 pub mtime: [u8; 12],
100 pub cksum: [u8; 8],
101 pub typeflag: [u8; 1],
102 pub linkname: [u8; 100],
103
104 pub magic: [u8; 6],
106 pub version: [u8; 2],
107 pub uname: [u8; 32],
108 pub gname: [u8; 32],
109 pub dev_major: [u8; 8],
110 pub dev_minor: [u8; 8],
111 pub atime: [u8; 12],
112 pub ctime: [u8; 12],
113 pub offset: [u8; 12],
114 pub longnames: [u8; 4],
115 pub unused: [u8; 1],
116 pub sparse: [GnuSparseHeader; 4],
117 pub isextended: [u8; 1],
118 pub realsize: [u8; 12],
119 pub pad: [u8; 17],
120}
121
122#[repr(C)]
126#[allow(missing_docs)]
127pub struct GnuSparseHeader {
128 pub offset: [u8; 12],
129 pub numbytes: [u8; 12],
130}
131
132#[repr(C)]
137#[allow(missing_docs)]
138#[derive(Debug)]
139pub struct GnuExtSparseHeader {
140 pub sparse: [GnuSparseHeader; 21],
141 pub isextended: [u8; 1],
142 pub padding: [u8; 7],
143}
144
145impl Header {
146 pub fn new_gnu() -> Header {
152 let mut header = Header { bytes: [0; 512] };
153 unsafe {
154 let gnu = cast_mut::<_, GnuHeader>(&mut header);
155 gnu.magic = *b"ustar ";
156 gnu.version = *b" \0";
157 }
158 header.set_mtime(0);
159 header
160 }
161
162 pub fn new_ustar() -> Header {
170 let mut header = Header { bytes: [0; 512] };
171 unsafe {
172 let gnu = cast_mut::<_, UstarHeader>(&mut header);
173 gnu.magic = *b"ustar\0";
174 gnu.version = *b"00";
175 }
176 header.set_mtime(0);
177 header
178 }
179
180 pub fn new_old() -> Header {
187 let mut header = Header { bytes: [0; 512] };
188 header.set_mtime(0);
189 header
190 }
191
192 fn is_ustar(&self) -> bool {
193 let ustar = unsafe { cast::<_, UstarHeader>(self) };
194 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
195 }
196
197 fn is_gnu(&self) -> bool {
198 let ustar = unsafe { cast::<_, UstarHeader>(self) };
199 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
200 }
201
202 pub fn as_old(&self) -> &OldHeader {
207 unsafe { cast(self) }
208 }
209
210 pub fn as_old_mut(&mut self) -> &mut OldHeader {
212 unsafe { cast_mut(self) }
213 }
214
215 pub fn as_ustar(&self) -> Option<&UstarHeader> {
225 if self.is_ustar() {
226 Some(unsafe { cast(self) })
227 } else {
228 None
229 }
230 }
231
232 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
234 if self.is_ustar() {
235 Some(unsafe { cast_mut(self) })
236 } else {
237 None
238 }
239 }
240
241 pub fn as_gnu(&self) -> Option<&GnuHeader> {
251 if self.is_gnu() {
252 Some(unsafe { cast(self) })
253 } else {
254 None
255 }
256 }
257
258 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
260 if self.is_gnu() {
261 Some(unsafe { cast_mut(self) })
262 } else {
263 None
264 }
265 }
266
267 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
271 assert_eq!(bytes.len(), mem::size_of::<Header>());
272 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
273 unsafe { &*(bytes.as_ptr() as *const Header) }
274 }
275
276 pub fn as_bytes(&self) -> &[u8; 512] {
278 &self.bytes
279 }
280
281 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
283 &mut self.bytes
284 }
285
286 pub fn set_metadata(&mut self, meta: &Metadata) {
293 self.fill_from(meta, HeaderMode::Complete);
294 }
295
296 pub fn set_metadata_in_mode(&mut self, meta: &Metadata, mode: HeaderMode) {
299 self.fill_from(meta, mode);
300 }
301
302 pub fn entry_size(&self) -> io::Result<u64> {
311 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
312 io::Error::new(
313 err.kind(),
314 format!("{} when getting size for {}", err, self.path_lossy()),
315 )
316 })
317 }
318
319 pub fn size(&self) -> io::Result<u64> {
323 if self.entry_type().is_gnu_sparse() {
324 self.as_gnu()
325 .ok_or_else(|| other("sparse header was not a gnu header"))
326 .and_then(GnuHeader::real_size)
327 } else {
328 self.entry_size()
329 }
330 }
331
332 pub fn set_size(&mut self, size: u64) {
334 num_field_wrapper_into(&mut self.as_old_mut().size, size);
335 }
336
337 pub fn path(&self) -> io::Result<Cow<'_, Path>> {
345 bytes2path(self.path_bytes())
346 }
347
348 pub fn path_bytes(&self) -> Cow<'_, [u8]> {
356 if let Some(ustar) = self.as_ustar() {
357 ustar.path_bytes()
358 } else {
359 let name = truncate(&self.as_old().name);
360 Cow::Borrowed(name)
361 }
362 }
363
364 fn path_lossy(&self) -> String {
366 String::from_utf8_lossy(&self.path_bytes()).to_string()
367 }
368
369 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
375 self.set_path_inner(p.as_ref(), false)
376 }
377
378 pub(crate) fn set_truncated_path_for_gnu_header<P: AsRef<Path>>(
382 &mut self,
383 p: P,
384 ) -> io::Result<()> {
385 self.set_path_inner(p.as_ref(), true)
386 }
387
388 fn set_path_inner(&mut self, path: &Path, is_truncated_gnu_long_path: bool) -> io::Result<()> {
389 if let Some(ustar) = self.as_ustar_mut() {
390 return ustar.set_path(path);
391 }
392 if is_truncated_gnu_long_path {
393 copy_path_into_gnu_long(&mut self.as_old_mut().name, path, false)
394 } else {
395 copy_path_into(&mut self.as_old_mut().name, path, false)
396 }
397 .map_err(|err| {
398 io::Error::new(
399 err.kind(),
400 format!("{} when setting path for {}", err, self.path_lossy()),
401 )
402 })
403 }
404
405 pub fn link_name(&self) -> io::Result<Option<Cow<'_, Path>>> {
414 match self.link_name_bytes() {
415 Some(bytes) => bytes2path(bytes).map(Some),
416 None => Ok(None),
417 }
418 }
419
420 pub fn link_name_bytes(&self) -> Option<Cow<'_, [u8]>> {
428 let old = self.as_old();
429 if old.linkname[0] == 0 {
430 None
431 } else {
432 Some(Cow::Borrowed(truncate(&old.linkname)))
433 }
434 }
435
436 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
442 self._set_link_name(p.as_ref())
443 }
444
445 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
446 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
447 io::Error::new(
448 err.kind(),
449 format!("{} when setting link name for {}", err, self.path_lossy()),
450 )
451 })
452 }
453
454 pub fn mode(&self) -> io::Result<u32> {
458 octal_from(&self.as_old().mode)
459 .map(|u| u as u32)
460 .map_err(|err| {
461 io::Error::new(
462 err.kind(),
463 format!("{} when getting mode for {}", err, self.path_lossy()),
464 )
465 })
466 }
467
468 pub fn set_mode(&mut self, mode: u32) {
470 octal_into(&mut self.as_old_mut().mode, mode);
471 }
472
473 pub fn uid(&self) -> io::Result<u64> {
477 num_field_wrapper_from(&self.as_old().uid).map_err(|err| {
478 io::Error::new(
479 err.kind(),
480 format!("{} when getting uid for {}", err, self.path_lossy()),
481 )
482 })
483 }
484
485 pub fn set_uid(&mut self, uid: u64) {
487 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
488 }
489
490 pub fn gid(&self) -> io::Result<u64> {
492 num_field_wrapper_from(&self.as_old().gid).map_err(|err| {
493 io::Error::new(
494 err.kind(),
495 format!("{} when getting gid for {}", err, self.path_lossy()),
496 )
497 })
498 }
499
500 pub fn set_gid(&mut self, gid: u64) {
502 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
503 }
504
505 pub fn mtime(&self) -> io::Result<u64> {
507 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
508 io::Error::new(
509 err.kind(),
510 format!("{} when getting mtime for {}", err, self.path_lossy()),
511 )
512 })
513 }
514
515 pub fn set_mtime(&mut self, mtime: u64) {
520 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
521 }
522
523 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
530 match self.username_bytes() {
531 Some(bytes) => str::from_utf8(bytes).map(Some),
532 None => Ok(None),
533 }
534 }
535
536 pub fn username_bytes(&self) -> Option<&[u8]> {
541 if let Some(ustar) = self.as_ustar() {
542 Some(ustar.username_bytes())
543 } else if let Some(gnu) = self.as_gnu() {
544 Some(gnu.username_bytes())
545 } else {
546 None
547 }
548 }
549
550 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
555 if let Some(ustar) = self.as_ustar_mut() {
556 return ustar.set_username(name);
557 }
558 if let Some(gnu) = self.as_gnu_mut() {
559 gnu.set_username(name)
560 } else {
561 Err(other("not a ustar or gnu archive, cannot set username"))
562 }
563 }
564
565 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
572 match self.groupname_bytes() {
573 Some(bytes) => str::from_utf8(bytes).map(Some),
574 None => Ok(None),
575 }
576 }
577
578 pub fn groupname_bytes(&self) -> Option<&[u8]> {
583 if let Some(ustar) = self.as_ustar() {
584 Some(ustar.groupname_bytes())
585 } else if let Some(gnu) = self.as_gnu() {
586 Some(gnu.groupname_bytes())
587 } else {
588 None
589 }
590 }
591
592 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
597 if let Some(ustar) = self.as_ustar_mut() {
598 return ustar.set_groupname(name);
599 }
600 if let Some(gnu) = self.as_gnu_mut() {
601 gnu.set_groupname(name)
602 } else {
603 Err(other("not a ustar or gnu archive, cannot set groupname"))
604 }
605 }
606
607 pub fn device_major(&self) -> io::Result<Option<u32>> {
615 if let Some(ustar) = self.as_ustar() {
616 ustar.device_major().map(Some)
617 } else if let Some(gnu) = self.as_gnu() {
618 gnu.device_major().map(Some)
619 } else {
620 Ok(None)
621 }
622 }
623
624 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
629 if let Some(ustar) = self.as_ustar_mut() {
630 ustar.set_device_major(major);
631 Ok(())
632 } else if let Some(gnu) = self.as_gnu_mut() {
633 gnu.set_device_major(major);
634 Ok(())
635 } else {
636 Err(other("not a ustar or gnu archive, cannot set dev_major"))
637 }
638 }
639
640 pub fn device_minor(&self) -> io::Result<Option<u32>> {
648 if let Some(ustar) = self.as_ustar() {
649 ustar.device_minor().map(Some)
650 } else if let Some(gnu) = self.as_gnu() {
651 gnu.device_minor().map(Some)
652 } else {
653 Ok(None)
654 }
655 }
656
657 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
662 if let Some(ustar) = self.as_ustar_mut() {
663 ustar.set_device_minor(minor);
664 Ok(())
665 } else if let Some(gnu) = self.as_gnu_mut() {
666 gnu.set_device_minor(minor);
667 Ok(())
668 } else {
669 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
670 }
671 }
672
673 pub fn entry_type(&self) -> EntryType {
675 EntryType::new(self.as_old().linkflag[0])
676 }
677
678 pub fn set_entry_type(&mut self, ty: EntryType) {
680 self.as_old_mut().linkflag = [ty.as_byte()];
681 }
682
683 pub fn cksum(&self) -> io::Result<u32> {
687 octal_from(&self.as_old().cksum)
688 .map(|u| u as u32)
689 .map_err(|err| {
690 io::Error::new(
691 err.kind(),
692 format!("{} when getting cksum for {}", err, self.path_lossy()),
693 )
694 })
695 }
696
697 pub fn set_cksum(&mut self) {
700 let cksum = self.calculate_cksum();
701 octal_into(&mut self.as_old_mut().cksum, cksum);
702 }
703
704 fn calculate_cksum(&self) -> u32 {
705 let old = self.as_old();
706 let start = old as *const _ as usize;
707 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
708 let offset = cksum_start - start;
709 let len = old.cksum.len();
710 self.bytes[0..offset]
711 .iter()
712 .chain(iter::repeat_n(&b' ', len))
713 .chain(&self.bytes[offset + len..])
714 .fold(0, |a, b| a + (*b as u32))
715 }
716
717 fn fill_from(&mut self, meta: &Metadata, mode: HeaderMode) {
718 self.fill_platform_from(meta, mode);
719 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
721 0
722 } else {
723 meta.len()
724 });
725 if let Some(ustar) = self.as_ustar_mut() {
726 ustar.set_device_major(0);
727 ustar.set_device_minor(0);
728 }
729 if let Some(gnu) = self.as_gnu_mut() {
730 gnu.set_device_major(0);
731 gnu.set_device_minor(0);
732 }
733 }
734
735 #[cfg(target_arch = "wasm32")]
736 #[allow(unused_variables)]
737 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
738 unimplemented!();
739 }
740
741 #[cfg(any(unix, target_os = "redox"))]
742 fn fill_platform_from(&mut self, meta: &Metadata, mode: HeaderMode) {
743 match mode {
744 HeaderMode::Complete => {
745 self.set_mtime(meta.mtime() as u64);
746 self.set_uid(meta.uid() as u64);
747 self.set_gid(meta.gid() as u64);
748 self.set_mode(meta.mode());
749 }
750 HeaderMode::Deterministic => {
751 self.set_mtime(0);
752 self.set_uid(0);
753 self.set_gid(0);
754
755 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
757 0o755
758 } else {
759 0o644
760 };
761 self.set_mode(fs_mode);
762 }
763 }
764
765 self.set_entry_type(entry_type(meta.mode()));
776
777 #[cfg(not(target_os = "redox"))]
778 fn entry_type(mode: u32) -> EntryType {
779 match mode as libc::mode_t & libc::S_IFMT {
780 libc::S_IFREG => EntryType::file(),
781 libc::S_IFLNK => EntryType::symlink(),
782 libc::S_IFCHR => EntryType::character_special(),
783 libc::S_IFBLK => EntryType::block_special(),
784 libc::S_IFDIR => EntryType::dir(),
785 libc::S_IFIFO => EntryType::fifo(),
786 _ => EntryType::new(b' '),
787 }
788 }
789
790 #[cfg(target_os = "redox")]
791 fn entry_type(mode: u32) -> EntryType {
792 use syscall;
793 match mode as u16 & syscall::MODE_TYPE {
794 syscall::MODE_FILE => EntryType::file(),
795 syscall::MODE_SYMLINK => EntryType::symlink(),
796 syscall::MODE_DIR => EntryType::dir(),
797 _ => EntryType::new(b' '),
798 }
799 }
800 }
801
802 #[cfg(windows)]
803 fn fill_platform_from(&mut self, meta: &Metadata, mode: HeaderMode) {
804 match mode {
806 HeaderMode::Complete => {
807 self.set_uid(0);
808 self.set_gid(0);
809 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
814 self.set_mtime(mtime);
815 let fs_mode = {
816 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
817 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
818 match (meta.is_dir(), readonly != 0) {
819 (true, false) => 0o755,
820 (true, true) => 0o555,
821 (false, false) => 0o644,
822 (false, true) => 0o444,
823 }
824 };
825 self.set_mode(fs_mode);
826 }
827 HeaderMode::Deterministic => {
828 self.set_uid(0);
829 self.set_gid(0);
830 self.set_mtime(0);
831 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
832 self.set_mode(fs_mode);
833 }
834 }
835
836 let ft = meta.file_type();
837 self.set_entry_type(if ft.is_dir() {
838 EntryType::dir()
839 } else if ft.is_file() {
840 EntryType::file()
841 } else if ft.is_symlink() {
842 EntryType::symlink()
843 } else {
844 EntryType::new(b' ')
845 });
846 }
847
848 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
849 if let Ok(entry_size) = self.entry_size() {
850 b.field("entry_size", &entry_size);
851 }
852 if let Ok(size) = self.size() {
853 b.field("size", &size);
854 }
855 if let Ok(path) = self.path() {
856 b.field("path", &path);
857 }
858 if let Ok(link_name) = self.link_name() {
859 b.field("link_name", &link_name);
860 }
861 if let Ok(mode) = self.mode() {
862 b.field("mode", &DebugAsOctal(mode));
863 }
864 if let Ok(uid) = self.uid() {
865 b.field("uid", &uid);
866 }
867 if let Ok(gid) = self.gid() {
868 b.field("gid", &gid);
869 }
870 if let Ok(mtime) = self.mtime() {
871 b.field("mtime", &mtime);
872 }
873 if let Ok(username) = self.username() {
874 b.field("username", &username);
875 }
876 if let Ok(groupname) = self.groupname() {
877 b.field("groupname", &groupname);
878 }
879 if let Ok(device_major) = self.device_major() {
880 b.field("device_major", &device_major);
881 }
882 if let Ok(device_minor) = self.device_minor() {
883 b.field("device_minor", &device_minor);
884 }
885 if let Ok(cksum) = self.cksum() {
886 b.field("cksum", &cksum);
887 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
888 }
889 }
890}
891
892struct DebugAsOctal<T>(T);
893
894impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
895 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
896 fmt::Octal::fmt(&self.0, f)
897 }
898}
899
900unsafe fn cast<T, U>(a: &T) -> &U {
901 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
902 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
903 unsafe { &*(a as *const T as *const U) }
904}
905
906unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
907 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
908 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
909 unsafe { &mut *(a as *mut T as *mut U) }
910}
911
912impl Clone for Header {
913 fn clone(&self) -> Header {
914 Header { bytes: self.bytes }
915 }
916}
917
918impl fmt::Debug for Header {
919 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
920 if let Some(me) = self.as_ustar() {
921 me.fmt(f)
922 } else if let Some(me) = self.as_gnu() {
923 me.fmt(f)
924 } else {
925 self.as_old().fmt(f)
926 }
927 }
928}
929
930impl OldHeader {
931 pub fn as_header(&self) -> &Header {
933 unsafe { cast(self) }
934 }
935
936 pub fn as_header_mut(&mut self) -> &mut Header {
938 unsafe { cast_mut(self) }
939 }
940}
941
942impl fmt::Debug for OldHeader {
943 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
944 let mut f = f.debug_struct("OldHeader");
945 self.as_header().debug_fields(&mut f);
946 f.finish()
947 }
948}
949
950impl UstarHeader {
951 pub fn path_bytes(&self) -> Cow<'_, [u8]> {
953 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
954 Cow::Borrowed(truncate(&self.name))
955 } else {
956 let mut bytes = Vec::new();
957 let prefix = truncate(&self.prefix);
958 if !prefix.is_empty() {
959 bytes.extend_from_slice(prefix);
960 bytes.push(b'/');
961 }
962 bytes.extend_from_slice(truncate(&self.name));
963 Cow::Owned(bytes)
964 }
965 }
966
967 fn path_lossy(&self) -> String {
969 String::from_utf8_lossy(&self.path_bytes()).to_string()
970 }
971
972 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
974 self._set_path(p.as_ref())
975 }
976
977 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
978 let bytes = path2bytes(path)?;
987 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
988 if bytes.len() <= maxnamelen {
989 copy_path_into(&mut self.name, path, false).map_err(|err| {
990 io::Error::new(
991 err.kind(),
992 format!("{} when setting path for {}", err, self.path_lossy()),
993 )
994 })?;
995 } else {
996 let mut prefix = path;
997 let mut prefixlen;
998 loop {
999 match prefix.parent() {
1000 Some(parent) => prefix = parent,
1001 None => {
1002 return Err(other(&format!(
1003 "path cannot be split to be inserted into archive: {}",
1004 path.display()
1005 )));
1006 }
1007 }
1008 prefixlen = path2bytes(prefix)?.len();
1009 if prefixlen <= maxprefixlen {
1010 break;
1011 }
1012 }
1013 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
1014 io::Error::new(
1015 err.kind(),
1016 format!("{} when setting path for {}", err, self.path_lossy()),
1017 )
1018 })?;
1019 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1020 copy_path_into(&mut self.name, &path, false).map_err(|err| {
1021 io::Error::new(
1022 err.kind(),
1023 format!("{} when setting path for {}", err, self.path_lossy()),
1024 )
1025 })?;
1026 }
1027 Ok(())
1028 }
1029
1030 pub fn username_bytes(&self) -> &[u8] {
1032 truncate(&self.uname)
1033 }
1034
1035 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1037 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1038 io::Error::new(
1039 err.kind(),
1040 format!("{} when setting username for {}", err, self.path_lossy()),
1041 )
1042 })
1043 }
1044
1045 pub fn groupname_bytes(&self) -> &[u8] {
1047 truncate(&self.gname)
1048 }
1049
1050 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1052 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1053 io::Error::new(
1054 err.kind(),
1055 format!("{} when setting groupname for {}", err, self.path_lossy()),
1056 )
1057 })
1058 }
1059
1060 pub fn device_major(&self) -> io::Result<u32> {
1062 octal_from(&self.dev_major)
1063 .map(|u| u as u32)
1064 .map_err(|err| {
1065 io::Error::new(
1066 err.kind(),
1067 format!(
1068 "{} when getting device_major for {}",
1069 err,
1070 self.path_lossy()
1071 ),
1072 )
1073 })
1074 }
1075
1076 pub fn set_device_major(&mut self, major: u32) {
1078 octal_into(&mut self.dev_major, major);
1079 }
1080
1081 pub fn device_minor(&self) -> io::Result<u32> {
1083 octal_from(&self.dev_minor)
1084 .map(|u| u as u32)
1085 .map_err(|err| {
1086 io::Error::new(
1087 err.kind(),
1088 format!(
1089 "{} when getting device_minor for {}",
1090 err,
1091 self.path_lossy()
1092 ),
1093 )
1094 })
1095 }
1096
1097 pub fn set_device_minor(&mut self, minor: u32) {
1099 octal_into(&mut self.dev_minor, minor);
1100 }
1101
1102 pub fn as_header(&self) -> &Header {
1104 unsafe { cast(self) }
1105 }
1106
1107 pub fn as_header_mut(&mut self) -> &mut Header {
1109 unsafe { cast_mut(self) }
1110 }
1111}
1112
1113impl fmt::Debug for UstarHeader {
1114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1115 let mut f = f.debug_struct("UstarHeader");
1116 self.as_header().debug_fields(&mut f);
1117 f.finish()
1118 }
1119}
1120
1121impl GnuHeader {
1122 pub fn username_bytes(&self) -> &[u8] {
1124 truncate(&self.uname)
1125 }
1126
1127 fn fullname_lossy(&self) -> String {
1129 format!(
1130 "{}:{}",
1131 String::from_utf8_lossy(self.groupname_bytes()),
1132 String::from_utf8_lossy(self.username_bytes()),
1133 )
1134 }
1135
1136 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1138 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1139 io::Error::new(
1140 err.kind(),
1141 format!(
1142 "{} when setting username for {}",
1143 err,
1144 self.fullname_lossy()
1145 ),
1146 )
1147 })
1148 }
1149
1150 pub fn groupname_bytes(&self) -> &[u8] {
1152 truncate(&self.gname)
1153 }
1154
1155 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1157 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1158 io::Error::new(
1159 err.kind(),
1160 format!(
1161 "{} when setting groupname for {}",
1162 err,
1163 self.fullname_lossy()
1164 ),
1165 )
1166 })
1167 }
1168
1169 pub fn device_major(&self) -> io::Result<u32> {
1171 octal_from(&self.dev_major)
1172 .map(|u| u as u32)
1173 .map_err(|err| {
1174 io::Error::new(
1175 err.kind(),
1176 format!(
1177 "{} when getting device_major for {}",
1178 err,
1179 self.fullname_lossy()
1180 ),
1181 )
1182 })
1183 }
1184
1185 pub fn set_device_major(&mut self, major: u32) {
1187 octal_into(&mut self.dev_major, major);
1188 }
1189
1190 pub fn device_minor(&self) -> io::Result<u32> {
1192 octal_from(&self.dev_minor)
1193 .map(|u| u as u32)
1194 .map_err(|err| {
1195 io::Error::new(
1196 err.kind(),
1197 format!(
1198 "{} when getting device_minor for {}",
1199 err,
1200 self.fullname_lossy()
1201 ),
1202 )
1203 })
1204 }
1205
1206 pub fn set_device_minor(&mut self, minor: u32) {
1208 octal_into(&mut self.dev_minor, minor);
1209 }
1210
1211 pub fn atime(&self) -> io::Result<u64> {
1213 num_field_wrapper_from(&self.atime).map_err(|err| {
1214 io::Error::new(
1215 err.kind(),
1216 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1217 )
1218 })
1219 }
1220
1221 pub fn set_atime(&mut self, atime: u64) {
1226 num_field_wrapper_into(&mut self.atime, atime);
1227 }
1228
1229 pub fn ctime(&self) -> io::Result<u64> {
1231 num_field_wrapper_from(&self.ctime).map_err(|err| {
1232 io::Error::new(
1233 err.kind(),
1234 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1235 )
1236 })
1237 }
1238
1239 pub fn set_ctime(&mut self, ctime: u64) {
1244 num_field_wrapper_into(&mut self.ctime, ctime);
1245 }
1246
1247 pub fn real_size(&self) -> io::Result<u64> {
1252 octal_from(&self.realsize).map_err(|err| {
1253 io::Error::new(
1254 err.kind(),
1255 format!(
1256 "{} when getting real_size for {}",
1257 err,
1258 self.fullname_lossy()
1259 ),
1260 )
1261 })
1262 }
1263
1264 pub fn is_extended(&self) -> bool {
1270 self.isextended[0] == 1
1271 }
1272
1273 pub fn as_header(&self) -> &Header {
1275 unsafe { cast(self) }
1276 }
1277
1278 pub fn as_header_mut(&mut self) -> &mut Header {
1280 unsafe { cast_mut(self) }
1281 }
1282}
1283
1284impl fmt::Debug for GnuHeader {
1285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1286 let mut f = f.debug_struct("GnuHeader");
1287 self.as_header().debug_fields(&mut f);
1288 if let Ok(atime) = self.atime() {
1289 f.field("atime", &atime);
1290 }
1291 if let Ok(ctime) = self.ctime() {
1292 f.field("ctime", &ctime);
1293 }
1294 f.field("is_extended", &self.is_extended())
1295 .field("sparse", &DebugSparseHeaders(&self.sparse))
1296 .finish()
1297 }
1298}
1299
1300struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1301
1302impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1304 let mut f = f.debug_list();
1305 for header in self.0 {
1306 if !header.is_empty() {
1307 f.entry(header);
1308 }
1309 }
1310 f.finish()
1311 }
1312}
1313
1314impl GnuSparseHeader {
1315 pub fn is_empty(&self) -> bool {
1317 self.offset[0] == 0 || self.numbytes[0] == 0
1318 }
1319
1320 pub fn offset(&self) -> io::Result<u64> {
1324 octal_from(&self.offset).map_err(|err| {
1325 io::Error::new(
1326 err.kind(),
1327 format!("{} when getting offset from sparse header", err),
1328 )
1329 })
1330 }
1331
1332 pub fn length(&self) -> io::Result<u64> {
1336 octal_from(&self.numbytes).map_err(|err| {
1337 io::Error::new(
1338 err.kind(),
1339 format!("{} when getting length from sparse header", err),
1340 )
1341 })
1342 }
1343}
1344
1345impl fmt::Debug for GnuSparseHeader {
1346 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1347 let mut f = f.debug_struct("GnuSparseHeader");
1348 if let Ok(offset) = self.offset() {
1349 f.field("offset", &offset);
1350 }
1351 if let Ok(length) = self.length() {
1352 f.field("length", &length);
1353 }
1354 f.finish()
1355 }
1356}
1357
1358impl GnuExtSparseHeader {
1359 pub fn new() -> GnuExtSparseHeader {
1361 unsafe { mem::zeroed() }
1362 }
1363
1364 pub fn as_bytes(&self) -> &[u8; 512] {
1366 debug_assert_eq!(mem::size_of_val(self), 512);
1367 unsafe { &*(self as *const GnuExtSparseHeader as *const [u8; 512]) }
1368 }
1369
1370 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1372 debug_assert_eq!(mem::size_of_val(self), 512);
1373 unsafe { &mut *(self as *mut GnuExtSparseHeader as *mut [u8; 512]) }
1374 }
1375
1376 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1381 &self.sparse
1382 }
1383
1384 pub fn is_extended(&self) -> bool {
1386 self.isextended[0] == 1
1387 }
1388}
1389
1390impl Default for GnuExtSparseHeader {
1391 fn default() -> Self {
1392 Self::new()
1393 }
1394}
1395
1396fn octal_from(slice: &[u8]) -> io::Result<u64> {
1397 let trun = truncate(slice);
1398 let num = match str::from_utf8(trun) {
1399 Ok(n) => n,
1400 Err(_) => {
1401 return Err(other(&format!(
1402 "numeric field did not have utf-8 text: {}",
1403 String::from_utf8_lossy(trun)
1404 )));
1405 }
1406 };
1407 match u64::from_str_radix(num.trim(), 8) {
1408 Ok(n) => Ok(n),
1409 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1410 }
1411}
1412
1413fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1414 let o = format!("{:o}", val);
1415 let value = o.bytes().rev().chain(repeat(b'0'));
1416 for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1417 *slot = value;
1418 }
1419}
1420
1421fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1424 if src >= 8_589_934_592 || (src >= 2_097_152 && dst.len() == 8) {
1425 numeric_extended_into(dst, src);
1426 } else {
1427 octal_into(dst, src);
1428 }
1429}
1430
1431fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1434 if src[0] & 0x80 == 0 {
1435 octal_from(src)
1436 } else {
1437 Ok(numeric_extended_from(src))
1438 }
1439}
1440
1441fn numeric_extended_into(dst: &mut [u8], src: u64) {
1446 let len: usize = dst.len();
1447 for (slot, val) in dst.iter_mut().zip(
1448 repeat_n(0, len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1450 ) {
1451 *slot = val;
1452 }
1453 dst[0] |= 0x80;
1454}
1455
1456fn numeric_extended_from(src: &[u8]) -> u64 {
1457 let mut dst: u64 = 0;
1458 let mut b_to_skip = 1;
1459 if src.len() == 8 {
1460 dst = (src[0] ^ 0x80) as u64;
1462 } else {
1463 b_to_skip = src.len() - 8;
1465 }
1466 for byte in src.iter().skip(b_to_skip) {
1467 dst <<= 8;
1468 dst |= *byte as u64;
1469 }
1470 dst
1471}
1472
1473fn truncate(slice: &[u8]) -> &[u8] {
1474 match slice.iter().position(|i| *i == 0) {
1475 Some(i) => &slice[..i],
1476 None => slice,
1477 }
1478}
1479
1480fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1483 if bytes.len() > slot.len() {
1484 Err(other("provided value is too long"))
1485 } else if bytes.contains(&0) {
1486 Err(other("provided value contains a nul byte"))
1487 } else {
1488 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1489 *slot = *val;
1490 }
1491 Ok(())
1492 }
1493}
1494
1495fn copy_path_into_inner(
1496 mut slot: &mut [u8],
1497 path: &Path,
1498 is_link_name: bool,
1499 is_truncated_gnu_long_path: bool,
1500) -> io::Result<()> {
1501 let mut emitted = false;
1502 let mut needs_slash = false;
1503 let mut iter = path.components().peekable();
1504 while let Some(component) = iter.next() {
1505 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1506 match (component, is_link_name) {
1507 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1508 return Err(other("paths in archives must be relative"));
1509 }
1510 (Component::ParentDir, false) => {
1511 if is_truncated_gnu_long_path && iter.peek().is_none() {
1512 {}
1515 } else {
1516 return Err(other("paths in archives must not have `..`"));
1517 }
1518 }
1519 (Component::CurDir, false) if path.components().count() == 1 => {}
1521 (Component::CurDir, false) => continue,
1522 (Component::Normal(_), _) | (_, true) => {}
1523 };
1524 if needs_slash {
1525 copy(&mut slot, b"/")?;
1526 }
1527 if bytes.contains(&b'/') {
1528 if let Component::Normal(..) = component {
1529 return Err(other("path component in archive cannot contain `/`"));
1530 }
1531 }
1532 copy(&mut slot, &bytes)?;
1533 if &*bytes != b"/" {
1534 needs_slash = true;
1535 }
1536 emitted = true;
1537 }
1538 if !emitted {
1539 return Err(other("paths in archives must have at least one component"));
1540 }
1541 if ends_with_slash(path) {
1542 copy(&mut slot, b"/")?;
1543 }
1544 return Ok(());
1545
1546 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1547 copy_into(slot, bytes)?;
1548 let tmp = std::mem::take(slot);
1549 *slot = &mut tmp[bytes.len()..];
1550 Ok(())
1551 }
1552}
1553
1554fn copy_path_into(slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1563 copy_path_into_inner(slot, path, is_link_name, false)
1564}
1565
1566fn copy_path_into_gnu_long(slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1577 copy_path_into_inner(slot, path, is_link_name, true)
1578}
1579
1580#[cfg(target_arch = "wasm32")]
1581fn ends_with_slash(p: &Path) -> bool {
1582 p.to_string_lossy().ends_with('/')
1583}
1584
1585#[cfg(windows)]
1586fn ends_with_slash(p: &Path) -> bool {
1587 let last = p.as_os_str().encode_wide().last();
1588 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1589}
1590
1591#[cfg(any(unix, target_os = "redox"))]
1592fn ends_with_slash(p: &Path) -> bool {
1593 p.as_os_str().as_bytes().ends_with(b"/")
1594}
1595
1596#[cfg(any(windows, target_arch = "wasm32"))]
1597pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1598 p.as_os_str()
1599 .to_str()
1600 .map(|s| s.as_bytes())
1601 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1602 .map(|bytes| {
1603 if bytes.contains(&b'\\') {
1604 let mut bytes = bytes.to_owned();
1606 for b in &mut bytes {
1607 if *b == b'\\' {
1608 *b = b'/';
1609 }
1610 }
1611 Cow::Owned(bytes)
1612 } else {
1613 Cow::Borrowed(bytes)
1614 }
1615 })
1616}
1617
1618#[cfg(any(unix, target_os = "redox"))]
1619pub fn path2bytes(p: &Path) -> io::Result<Cow<'_, [u8]>> {
1621 Ok(Cow::Borrowed(p.as_os_str().as_bytes()))
1622}
1623
1624#[cfg(windows)]
1625pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1628 return match bytes {
1629 Cow::Borrowed(bytes) => {
1630 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1631 Ok(Cow::Borrowed(Path::new(s)))
1632 }
1633 Cow::Owned(bytes) => {
1634 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1635 Ok(Cow::Owned(PathBuf::from(s)))
1636 }
1637 };
1638
1639 fn not_unicode(v: &[u8]) -> io::Error {
1640 other(&format!(
1641 "only Unicode paths are supported on Windows: {}",
1642 String::from_utf8_lossy(v)
1643 ))
1644 }
1645}
1646
1647#[cfg(any(unix, target_os = "redox"))]
1648pub fn bytes2path(bytes: Cow<'_, [u8]>) -> io::Result<Cow<'_, Path>> {
1650 use std::ffi::{OsStr, OsString};
1651
1652 Ok(match bytes {
1653 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1654 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1655 })
1656}
1657
1658#[cfg(target_arch = "wasm32")]
1659pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1660 Ok(match bytes {
1661 Cow::Borrowed(bytes) => {
1662 Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1663 }
1664 Cow::Owned(bytes) => {
1665 Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1666 }
1667 })
1668}
1669
1670#[cfg(target_arch = "wasm32")]
1671fn invalid_utf8<T>(_: T) -> io::Error {
1672 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1673}