1#[cfg(any(unix, target_os = "redox"))]
2use std::os::unix::prelude::*;
3#[cfg(windows)]
4use std::os::windows::prelude::*;
5
6use std::{borrow::Cow, fmt, iter, iter::repeat, mem, str};
7
8use async_std::{
9 fs, io,
10 path::{Component, Path, PathBuf},
11};
12
13use crate::{other, EntryType};
14
15#[repr(C)]
17#[allow(missing_docs)]
18pub struct Header {
19 bytes: [u8; 512],
20}
21
22#[derive(Clone, Copy, PartialEq, Eq, Debug)]
25#[non_exhaustive]
26pub enum HeaderMode {
27 Complete,
30
31 Deterministic,
34}
35
36#[repr(C)]
38#[allow(missing_docs)]
39pub struct OldHeader {
40 pub name: [u8; 100],
41 pub mode: [u8; 8],
42 pub uid: [u8; 8],
43 pub gid: [u8; 8],
44 pub size: [u8; 12],
45 pub mtime: [u8; 12],
46 pub cksum: [u8; 8],
47 pub linkflag: [u8; 1],
48 pub linkname: [u8; 100],
49 pub pad: [u8; 255],
50}
51
52#[repr(C)]
54#[allow(missing_docs)]
55pub struct UstarHeader {
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 typeflag: [u8; 1],
64 pub linkname: [u8; 100],
65
66 pub magic: [u8; 6],
68 pub version: [u8; 2],
69 pub uname: [u8; 32],
70 pub gname: [u8; 32],
71 pub dev_major: [u8; 8],
72 pub dev_minor: [u8; 8],
73 pub prefix: [u8; 155],
74 pub pad: [u8; 12],
75}
76
77#[repr(C)]
79#[allow(missing_docs)]
80pub struct GnuHeader {
81 pub name: [u8; 100],
82 pub mode: [u8; 8],
83 pub uid: [u8; 8],
84 pub gid: [u8; 8],
85 pub size: [u8; 12],
86 pub mtime: [u8; 12],
87 pub cksum: [u8; 8],
88 pub typeflag: [u8; 1],
89 pub linkname: [u8; 100],
90
91 pub magic: [u8; 6],
93 pub version: [u8; 2],
94 pub uname: [u8; 32],
95 pub gname: [u8; 32],
96 pub dev_major: [u8; 8],
97 pub dev_minor: [u8; 8],
98 pub atime: [u8; 12],
99 pub ctime: [u8; 12],
100 pub offset: [u8; 12],
101 pub longnames: [u8; 4],
102 pub unused: [u8; 1],
103 pub sparse: [GnuSparseHeader; 4],
104 pub isextended: [u8; 1],
105 pub realsize: [u8; 12],
106 pub pad: [u8; 17],
107}
108
109#[repr(C)]
113#[allow(missing_docs)]
114pub struct GnuSparseHeader {
115 pub offset: [u8; 12],
116 pub numbytes: [u8; 12],
117}
118
119#[repr(C)]
124#[allow(missing_docs)]
125#[derive(Debug)]
126pub struct GnuExtSparseHeader {
127 pub sparse: [GnuSparseHeader; 21],
128 pub isextended: [u8; 1],
129 pub padding: [u8; 7],
130}
131
132impl Header {
133 pub fn new_gnu() -> Header {
139 let mut header = Header { bytes: [0; 512] };
140 unsafe {
141 let gnu = cast_mut::<_, GnuHeader>(&mut header);
142 gnu.magic = *b"ustar ";
143 gnu.version = *b" \0";
144 }
145 header.set_mtime(0);
146 header
147 }
148
149 pub fn new_ustar() -> Header {
157 let mut header = Header { bytes: [0; 512] };
158 unsafe {
159 let gnu = cast_mut::<_, UstarHeader>(&mut header);
160 gnu.magic = *b"ustar\0";
161 gnu.version = *b"00";
162 }
163 header.set_mtime(0);
164 header
165 }
166
167 pub fn new_old() -> Header {
174 let mut header = Header { bytes: [0; 512] };
175 header.set_mtime(0);
176 header
177 }
178
179 fn is_ustar(&self) -> bool {
180 let ustar = unsafe { cast::<_, UstarHeader>(self) };
181 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
182 }
183
184 fn is_gnu(&self) -> bool {
185 let ustar = unsafe { cast::<_, UstarHeader>(self) };
186 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
187 }
188
189 pub fn as_old(&self) -> &OldHeader {
194 unsafe { cast(self) }
195 }
196
197 pub fn as_old_mut(&mut self) -> &mut OldHeader {
199 unsafe { cast_mut(self) }
200 }
201
202 pub fn as_ustar(&self) -> Option<&UstarHeader> {
212 if self.is_ustar() {
213 Some(unsafe { cast(self) })
214 } else {
215 None
216 }
217 }
218
219 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
221 if self.is_ustar() {
222 Some(unsafe { cast_mut(self) })
223 } else {
224 None
225 }
226 }
227
228 pub fn as_gnu(&self) -> Option<&GnuHeader> {
238 if self.is_gnu() {
239 Some(unsafe { cast(self) })
240 } else {
241 None
242 }
243 }
244
245 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
247 if self.is_gnu() {
248 Some(unsafe { cast_mut(self) })
249 } else {
250 None
251 }
252 }
253
254 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
258 assert_eq!(bytes.len(), mem::size_of::<Header>());
259 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
260 unsafe { &*(bytes.as_ptr() as *const Header) }
261 }
262
263 pub fn as_bytes(&self) -> &[u8; 512] {
265 &self.bytes
266 }
267
268 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
270 &mut self.bytes
271 }
272
273 pub fn set_metadata(&mut self, meta: &fs::Metadata) {
280 self.fill_from(meta, HeaderMode::Complete);
281 }
282
283 pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
286 self.fill_from(meta, mode);
287 }
288
289 pub fn entry_size(&self) -> io::Result<u64> {
298 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
299 io::Error::new(
300 err.kind(),
301 format!("{} when getting size for {}", err, self.path_lossy()),
302 )
303 })
304 }
305
306 pub fn size(&self) -> io::Result<u64> {
310 if self.entry_type().is_gnu_sparse() {
311 self.as_gnu()
312 .ok_or_else(|| other("sparse header was not a gnu header"))
313 .and_then(GnuHeader::real_size)
314 } else {
315 self.entry_size()
316 }
317 }
318
319 pub fn set_size(&mut self, size: u64) {
321 num_field_wrapper_into(&mut self.as_old_mut().size, size);
322 }
323
324 pub fn path(&self) -> io::Result<Cow<Path>> {
332 bytes2path(self.path_bytes())
333 }
334
335 pub fn path_bytes(&self) -> Cow<[u8]> {
343 if let Some(ustar) = self.as_ustar() {
344 ustar.path_bytes()
345 } else {
346 let name = truncate(&self.as_old().name);
347 Cow::Borrowed(name)
348 }
349 }
350
351 fn path_lossy(&self) -> String {
353 String::from_utf8_lossy(&self.path_bytes()).to_string()
354 }
355
356 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
362 self._set_path(p.as_ref())
363 }
364
365 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
366 if let Some(ustar) = self.as_ustar_mut() {
367 return ustar.set_path(path);
368 }
369 copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
370 io::Error::new(
371 err.kind(),
372 format!("{} when setting path for {}", err, self.path_lossy()),
373 )
374 })
375 }
376
377 pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
386 match self.link_name_bytes() {
387 Some(bytes) => bytes2path(bytes).map(Some),
388 None => Ok(None),
389 }
390 }
391
392 pub fn link_name_bytes(&self) -> Option<Cow<[u8]>> {
400 let old = self.as_old();
401 if old.linkname[0] == 0 {
402 None
403 } else {
404 Some(Cow::Borrowed(truncate(&old.linkname)))
405 }
406 }
407
408 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
414 self._set_link_name(p.as_ref())
415 }
416
417 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
418 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
419 io::Error::new(
420 err.kind(),
421 format!("{} when setting link name for {}", err, self.path_lossy()),
422 )
423 })
424 }
425
426 pub fn mode(&self) -> io::Result<u32> {
430 octal_from(&self.as_old().mode)
431 .map(|u| u as u32)
432 .map_err(|err| {
433 io::Error::new(
434 err.kind(),
435 format!("{} when getting mode for {}", err, self.path_lossy()),
436 )
437 })
438 }
439
440 pub fn set_mode(&mut self, mode: u32) {
442 octal_into(&mut self.as_old_mut().mode, mode);
443 }
444
445 pub fn uid(&self) -> io::Result<u64> {
449 num_field_wrapper_from(&self.as_old().uid).map_err(|err| {
450 io::Error::new(
451 err.kind(),
452 format!("{} when getting uid for {}", err, self.path_lossy()),
453 )
454 })
455 }
456
457 pub fn set_uid(&mut self, uid: u64) {
459 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
460 }
461
462 pub fn gid(&self) -> io::Result<u64> {
464 num_field_wrapper_from(&self.as_old().gid).map_err(|err| {
465 io::Error::new(
466 err.kind(),
467 format!("{} when getting gid for {}", err, self.path_lossy()),
468 )
469 })
470 }
471
472 pub fn set_gid(&mut self, gid: u64) {
474 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
475 }
476
477 pub fn mtime(&self) -> io::Result<u64> {
479 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
480 io::Error::new(
481 err.kind(),
482 format!("{} when getting mtime for {}", err, self.path_lossy()),
483 )
484 })
485 }
486
487 pub fn set_mtime(&mut self, mtime: u64) {
492 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
493 }
494
495 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
502 match self.username_bytes() {
503 Some(bytes) => str::from_utf8(bytes).map(Some),
504 None => Ok(None),
505 }
506 }
507
508 pub fn username_bytes(&self) -> Option<&[u8]> {
513 if let Some(ustar) = self.as_ustar() {
514 Some(ustar.username_bytes())
515 } else if let Some(gnu) = self.as_gnu() {
516 Some(gnu.username_bytes())
517 } else {
518 None
519 }
520 }
521
522 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
527 if let Some(ustar) = self.as_ustar_mut() {
528 return ustar.set_username(name);
529 }
530 if let Some(gnu) = self.as_gnu_mut() {
531 gnu.set_username(name)
532 } else {
533 Err(other("not a ustar or gnu archive, cannot set username"))
534 }
535 }
536
537 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
544 match self.groupname_bytes() {
545 Some(bytes) => str::from_utf8(bytes).map(Some),
546 None => Ok(None),
547 }
548 }
549
550 pub fn groupname_bytes(&self) -> Option<&[u8]> {
555 if let Some(ustar) = self.as_ustar() {
556 Some(ustar.groupname_bytes())
557 } else if let Some(gnu) = self.as_gnu() {
558 Some(gnu.groupname_bytes())
559 } else {
560 None
561 }
562 }
563
564 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
569 if let Some(ustar) = self.as_ustar_mut() {
570 return ustar.set_groupname(name);
571 }
572 if let Some(gnu) = self.as_gnu_mut() {
573 gnu.set_groupname(name)
574 } else {
575 Err(other("not a ustar or gnu archive, cannot set groupname"))
576 }
577 }
578
579 pub fn device_major(&self) -> io::Result<Option<u32>> {
587 if let Some(ustar) = self.as_ustar() {
588 ustar.device_major().map(Some)
589 } else if let Some(gnu) = self.as_gnu() {
590 gnu.device_major().map(Some)
591 } else {
592 Ok(None)
593 }
594 }
595
596 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
601 if let Some(ustar) = self.as_ustar_mut() {
602 ustar.set_device_major(major);
603 Ok(())
604 } else if let Some(gnu) = self.as_gnu_mut() {
605 gnu.set_device_major(major);
606 Ok(())
607 } else {
608 Err(other("not a ustar or gnu archive, cannot set dev_major"))
609 }
610 }
611
612 pub fn device_minor(&self) -> io::Result<Option<u32>> {
620 if let Some(ustar) = self.as_ustar() {
621 ustar.device_minor().map(Some)
622 } else if let Some(gnu) = self.as_gnu() {
623 gnu.device_minor().map(Some)
624 } else {
625 Ok(None)
626 }
627 }
628
629 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
634 if let Some(ustar) = self.as_ustar_mut() {
635 ustar.set_device_minor(minor);
636 Ok(())
637 } else if let Some(gnu) = self.as_gnu_mut() {
638 gnu.set_device_minor(minor);
639 Ok(())
640 } else {
641 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
642 }
643 }
644
645 pub fn entry_type(&self) -> EntryType {
647 EntryType::new(self.as_old().linkflag[0])
648 }
649
650 pub fn set_entry_type(&mut self, ty: EntryType) {
652 self.as_old_mut().linkflag = [ty.as_byte()];
653 }
654
655 pub fn cksum(&self) -> io::Result<u32> {
659 octal_from(&self.as_old().cksum)
660 .map(|u| u as u32)
661 .map_err(|err| {
662 io::Error::new(
663 err.kind(),
664 format!("{} when getting cksum for {}", err, self.path_lossy()),
665 )
666 })
667 }
668
669 pub fn set_cksum(&mut self) {
672 let cksum = self.calculate_cksum();
673 octal_into(&mut self.as_old_mut().cksum, cksum);
674 }
675
676 fn calculate_cksum(&self) -> u32 {
677 let old = self.as_old();
678 let start = old as *const _ as usize;
679 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
680 let offset = cksum_start - start;
681 let len = old.cksum.len();
682 self.bytes[0..offset]
683 .iter()
684 .chain(iter::repeat(&b' ').take(len))
685 .chain(&self.bytes[offset + len..])
686 .fold(0, |a, b| a + (*b as u32))
687 }
688
689 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
690 self.fill_platform_from(meta, mode);
691 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
693 0
694 } else {
695 meta.len()
696 });
697 if let Some(ustar) = self.as_ustar_mut() {
698 ustar.set_device_major(0);
699 ustar.set_device_minor(0);
700 }
701 if let Some(gnu) = self.as_gnu_mut() {
702 gnu.set_device_major(0);
703 gnu.set_device_minor(0);
704 }
705 }
706
707 #[cfg(target_arch = "wasm32")]
708 #[allow(unused_variables)]
709 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
710 unimplemented!();
711 }
712
713 #[cfg(any(unix, target_os = "redox"))]
714 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
715 match mode {
716 HeaderMode::Complete => {
717 self.set_mtime(meta.mtime() as u64);
718 self.set_uid(meta.uid() as u64);
719 self.set_gid(meta.gid() as u64);
720 self.set_mode(meta.mode());
721 }
722 HeaderMode::Deterministic => {
723 self.set_mtime(0);
724 self.set_uid(0);
725 self.set_gid(0);
726
727 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
729 0o755
730 } else {
731 0o644
732 };
733 self.set_mode(fs_mode);
734 }
735 }
736
737 self.set_entry_type(entry_type(meta.mode()));
748
749 #[cfg(not(target_os = "redox"))]
750 fn entry_type(mode: u32) -> EntryType {
751 match mode as libc::mode_t & libc::S_IFMT {
752 libc::S_IFREG => EntryType::file(),
753 libc::S_IFLNK => EntryType::symlink(),
754 libc::S_IFCHR => EntryType::character_special(),
755 libc::S_IFBLK => EntryType::block_special(),
756 libc::S_IFDIR => EntryType::dir(),
757 libc::S_IFIFO => EntryType::fifo(),
758 _ => EntryType::new(b' '),
759 }
760 }
761
762 #[cfg(target_os = "redox")]
763 fn entry_type(mode: u32) -> EntryType {
764 use syscall;
765 match mode as u16 & syscall::MODE_TYPE {
766 syscall::MODE_FILE => EntryType::file(),
767 syscall::MODE_SYMLINK => EntryType::symlink(),
768 syscall::MODE_DIR => EntryType::dir(),
769 _ => EntryType::new(b' '),
770 }
771 }
772 }
773
774 #[cfg(windows)]
775 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
776 match mode {
778 HeaderMode::Complete => {
779 self.set_uid(0);
780 self.set_gid(0);
781 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
786 self.set_mtime(mtime);
787 let fs_mode = {
788 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
789 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
790 match (meta.is_dir(), readonly != 0) {
791 (true, false) => 0o755,
792 (true, true) => 0o555,
793 (false, false) => 0o644,
794 (false, true) => 0o444,
795 }
796 };
797 self.set_mode(fs_mode);
798 }
799 HeaderMode::Deterministic => {
800 self.set_uid(0);
801 self.set_gid(0);
802 self.set_mtime(0);
803 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
804 self.set_mode(fs_mode);
805 }
806 }
807
808 let ft = meta.file_type();
809 self.set_entry_type(if ft.is_dir() {
810 EntryType::dir()
811 } else if ft.is_file() {
812 EntryType::file()
813 } else if ft.is_symlink() {
814 EntryType::symlink()
815 } else {
816 EntryType::new(b' ')
817 });
818 }
819
820 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
821 if let Ok(entry_size) = self.entry_size() {
822 b.field("entry_size", &entry_size);
823 }
824 if let Ok(size) = self.size() {
825 b.field("size", &size);
826 }
827 if let Ok(path) = self.path() {
828 b.field("path", &path);
829 }
830 if let Ok(link_name) = self.link_name() {
831 b.field("link_name", &link_name);
832 }
833 if let Ok(mode) = self.mode() {
834 b.field("mode", &DebugAsOctal(mode));
835 }
836 if let Ok(uid) = self.uid() {
837 b.field("uid", &uid);
838 }
839 if let Ok(gid) = self.gid() {
840 b.field("gid", &gid);
841 }
842 if let Ok(mtime) = self.mtime() {
843 b.field("mtime", &mtime);
844 }
845 if let Ok(username) = self.username() {
846 b.field("username", &username);
847 }
848 if let Ok(groupname) = self.groupname() {
849 b.field("groupname", &groupname);
850 }
851 if let Ok(device_major) = self.device_major() {
852 b.field("device_major", &device_major);
853 }
854 if let Ok(device_minor) = self.device_minor() {
855 b.field("device_minor", &device_minor);
856 }
857 if let Ok(cksum) = self.cksum() {
858 b.field("cksum", &cksum);
859 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
860 }
861 }
862}
863
864struct DebugAsOctal<T>(T);
865
866impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
867 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
868 fmt::Octal::fmt(&self.0, f)
869 }
870}
871
872unsafe fn cast<T, U>(a: &T) -> &U {
873 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
874 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
875 &*(a as *const T as *const U)
876}
877
878unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
879 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
880 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
881 &mut *(a as *mut T as *mut U)
882}
883
884impl Clone for Header {
885 fn clone(&self) -> Header {
886 Header { bytes: self.bytes }
887 }
888}
889
890impl fmt::Debug for Header {
891 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
892 if let Some(me) = self.as_ustar() {
893 me.fmt(f)
894 } else if let Some(me) = self.as_gnu() {
895 me.fmt(f)
896 } else {
897 self.as_old().fmt(f)
898 }
899 }
900}
901
902impl OldHeader {
903 pub fn as_header(&self) -> &Header {
905 unsafe { cast(self) }
906 }
907
908 pub fn as_header_mut(&mut self) -> &mut Header {
910 unsafe { cast_mut(self) }
911 }
912}
913
914impl fmt::Debug for OldHeader {
915 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
916 let mut f = f.debug_struct("OldHeader");
917 self.as_header().debug_fields(&mut f);
918 f.finish()
919 }
920}
921
922impl UstarHeader {
923 pub fn path_bytes(&self) -> Cow<[u8]> {
925 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
926 Cow::Borrowed(truncate(&self.name))
927 } else {
928 let mut bytes = Vec::new();
929 let prefix = truncate(&self.prefix);
930 if !prefix.is_empty() {
931 bytes.extend_from_slice(prefix);
932 bytes.push(b'/');
933 }
934 bytes.extend_from_slice(truncate(&self.name));
935 Cow::Owned(bytes)
936 }
937 }
938
939 fn path_lossy(&self) -> String {
941 String::from_utf8_lossy(&self.path_bytes()).to_string()
942 }
943
944 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
946 self._set_path(p.as_ref())
947 }
948
949 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
950 let bytes = path2bytes(path)?;
959 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
960 if bytes.len() <= maxnamelen {
961 copy_path_into(&mut self.name, path, false).map_err(|err| {
962 io::Error::new(
963 err.kind(),
964 format!("{} when setting path for {}", err, self.path_lossy()),
965 )
966 })?;
967 } else {
968 let mut prefix = path;
969 let mut prefixlen;
970 loop {
971 match prefix.parent() {
972 Some(parent) => prefix = parent,
973 None => {
974 return Err(other(&format!(
975 "path cannot be split to be inserted into archive: {}",
976 path.display()
977 )));
978 }
979 }
980 prefixlen = path2bytes(prefix)?.len();
981 if prefixlen <= maxprefixlen {
982 break;
983 }
984 }
985 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
986 io::Error::new(
987 err.kind(),
988 format!("{} when setting path for {}", err, self.path_lossy()),
989 )
990 })?;
991 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
992 copy_path_into(&mut self.name, &path, false).map_err(|err| {
993 io::Error::new(
994 err.kind(),
995 format!("{} when setting path for {}", err, self.path_lossy()),
996 )
997 })?;
998 }
999 Ok(())
1000 }
1001
1002 pub fn username_bytes(&self) -> &[u8] {
1004 truncate(&self.uname)
1005 }
1006
1007 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1009 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1010 io::Error::new(
1011 err.kind(),
1012 format!("{} when setting username for {}", err, self.path_lossy()),
1013 )
1014 })
1015 }
1016
1017 pub fn groupname_bytes(&self) -> &[u8] {
1019 truncate(&self.gname)
1020 }
1021
1022 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1024 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1025 io::Error::new(
1026 err.kind(),
1027 format!("{} when setting groupname for {}", err, self.path_lossy()),
1028 )
1029 })
1030 }
1031
1032 pub fn device_major(&self) -> io::Result<u32> {
1034 octal_from(&self.dev_major)
1035 .map(|u| u as u32)
1036 .map_err(|err| {
1037 io::Error::new(
1038 err.kind(),
1039 format!(
1040 "{} when getting device_major for {}",
1041 err,
1042 self.path_lossy()
1043 ),
1044 )
1045 })
1046 }
1047
1048 pub fn set_device_major(&mut self, major: u32) {
1050 octal_into(&mut self.dev_major, major);
1051 }
1052
1053 pub fn device_minor(&self) -> io::Result<u32> {
1055 octal_from(&self.dev_minor)
1056 .map(|u| u as u32)
1057 .map_err(|err| {
1058 io::Error::new(
1059 err.kind(),
1060 format!(
1061 "{} when getting device_minor for {}",
1062 err,
1063 self.path_lossy()
1064 ),
1065 )
1066 })
1067 }
1068
1069 pub fn set_device_minor(&mut self, minor: u32) {
1071 octal_into(&mut self.dev_minor, minor);
1072 }
1073
1074 pub fn as_header(&self) -> &Header {
1076 unsafe { cast(self) }
1077 }
1078
1079 pub fn as_header_mut(&mut self) -> &mut Header {
1081 unsafe { cast_mut(self) }
1082 }
1083}
1084
1085impl fmt::Debug for UstarHeader {
1086 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1087 let mut f = f.debug_struct("UstarHeader");
1088 self.as_header().debug_fields(&mut f);
1089 f.finish()
1090 }
1091}
1092
1093impl GnuHeader {
1094 pub fn username_bytes(&self) -> &[u8] {
1096 truncate(&self.uname)
1097 }
1098
1099 fn fullname_lossy(&self) -> String {
1101 format!(
1102 "{}:{}",
1103 String::from_utf8_lossy(self.groupname_bytes()),
1104 String::from_utf8_lossy(self.username_bytes()),
1105 )
1106 }
1107
1108 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1110 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1111 io::Error::new(
1112 err.kind(),
1113 format!(
1114 "{} when setting username for {}",
1115 err,
1116 self.fullname_lossy()
1117 ),
1118 )
1119 })
1120 }
1121
1122 pub fn groupname_bytes(&self) -> &[u8] {
1124 truncate(&self.gname)
1125 }
1126
1127 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1129 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1130 io::Error::new(
1131 err.kind(),
1132 format!(
1133 "{} when setting groupname for {}",
1134 err,
1135 self.fullname_lossy()
1136 ),
1137 )
1138 })
1139 }
1140
1141 pub fn device_major(&self) -> io::Result<u32> {
1143 octal_from(&self.dev_major)
1144 .map(|u| u as u32)
1145 .map_err(|err| {
1146 io::Error::new(
1147 err.kind(),
1148 format!(
1149 "{} when getting device_major for {}",
1150 err,
1151 self.fullname_lossy()
1152 ),
1153 )
1154 })
1155 }
1156
1157 pub fn set_device_major(&mut self, major: u32) {
1159 octal_into(&mut self.dev_major, major);
1160 }
1161
1162 pub fn device_minor(&self) -> io::Result<u32> {
1164 octal_from(&self.dev_minor)
1165 .map(|u| u as u32)
1166 .map_err(|err| {
1167 io::Error::new(
1168 err.kind(),
1169 format!(
1170 "{} when getting device_minor for {}",
1171 err,
1172 self.fullname_lossy()
1173 ),
1174 )
1175 })
1176 }
1177
1178 pub fn set_device_minor(&mut self, minor: u32) {
1180 octal_into(&mut self.dev_minor, minor);
1181 }
1182
1183 pub fn atime(&self) -> io::Result<u64> {
1185 num_field_wrapper_from(&self.atime).map_err(|err| {
1186 io::Error::new(
1187 err.kind(),
1188 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1189 )
1190 })
1191 }
1192
1193 pub fn set_atime(&mut self, atime: u64) {
1198 num_field_wrapper_into(&mut self.atime, atime);
1199 }
1200
1201 pub fn ctime(&self) -> io::Result<u64> {
1203 num_field_wrapper_from(&self.ctime).map_err(|err| {
1204 io::Error::new(
1205 err.kind(),
1206 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1207 )
1208 })
1209 }
1210
1211 pub fn set_ctime(&mut self, ctime: u64) {
1216 num_field_wrapper_into(&mut self.ctime, ctime);
1217 }
1218
1219 pub fn real_size(&self) -> io::Result<u64> {
1224 octal_from(&self.realsize).map_err(|err| {
1225 io::Error::new(
1226 err.kind(),
1227 format!(
1228 "{} when getting real_size for {}",
1229 err,
1230 self.fullname_lossy()
1231 ),
1232 )
1233 })
1234 }
1235
1236 pub fn is_extended(&self) -> bool {
1242 self.isextended[0] == 1
1243 }
1244
1245 pub fn as_header(&self) -> &Header {
1247 unsafe { cast(self) }
1248 }
1249
1250 pub fn as_header_mut(&mut self) -> &mut Header {
1252 unsafe { cast_mut(self) }
1253 }
1254}
1255
1256impl fmt::Debug for GnuHeader {
1257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1258 let mut f = f.debug_struct("GnuHeader");
1259 self.as_header().debug_fields(&mut f);
1260 if let Ok(atime) = self.atime() {
1261 f.field("atime", &atime);
1262 }
1263 if let Ok(ctime) = self.ctime() {
1264 f.field("ctime", &ctime);
1265 }
1266 f.field("is_extended", &self.is_extended())
1267 .field("sparse", &DebugSparseHeaders(&self.sparse))
1268 .finish()
1269 }
1270}
1271
1272struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1273
1274impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1276 let mut f = f.debug_list();
1277 for header in self.0 {
1278 if !header.is_empty() {
1279 f.entry(header);
1280 }
1281 }
1282 f.finish()
1283 }
1284}
1285
1286impl GnuSparseHeader {
1287 pub fn is_empty(&self) -> bool {
1289 self.offset[0] == 0 || self.numbytes[0] == 0
1290 }
1291
1292 pub fn offset(&self) -> io::Result<u64> {
1296 octal_from(&self.offset).map_err(|err| {
1297 io::Error::new(
1298 err.kind(),
1299 format!("{} when getting offset from sparse header", err),
1300 )
1301 })
1302 }
1303
1304 pub fn length(&self) -> io::Result<u64> {
1308 octal_from(&self.numbytes).map_err(|err| {
1309 io::Error::new(
1310 err.kind(),
1311 format!("{} when getting length from sparse header", err),
1312 )
1313 })
1314 }
1315}
1316
1317impl fmt::Debug for GnuSparseHeader {
1318 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1319 let mut f = f.debug_struct("GnuSparseHeader");
1320 if let Ok(offset) = self.offset() {
1321 f.field("offset", &offset);
1322 }
1323 if let Ok(length) = self.length() {
1324 f.field("length", &length);
1325 }
1326 f.finish()
1327 }
1328}
1329
1330impl GnuExtSparseHeader {
1331 pub fn new() -> GnuExtSparseHeader {
1333 unsafe { mem::zeroed() }
1334 }
1335
1336 pub fn as_bytes(&self) -> &[u8; 512] {
1338 debug_assert_eq!(mem::size_of_val(self), 512);
1339 unsafe { &*(self as *const GnuExtSparseHeader as *const [u8; 512]) }
1340 }
1341
1342 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1344 debug_assert_eq!(mem::size_of_val(self), 512);
1345 unsafe { &mut *(self as *mut GnuExtSparseHeader as *mut [u8; 512]) }
1346 }
1347
1348 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1353 &self.sparse
1354 }
1355
1356 pub fn is_extended(&self) -> bool {
1358 self.isextended[0] == 1
1359 }
1360}
1361
1362impl Default for GnuExtSparseHeader {
1363 fn default() -> Self {
1364 Self::new()
1365 }
1366}
1367
1368fn octal_from(slice: &[u8]) -> io::Result<u64> {
1369 let trun = truncate(slice);
1370 let num = match str::from_utf8(trun) {
1371 Ok(n) => n,
1372 Err(_) => {
1373 return Err(other(&format!(
1374 "numeric field did not have utf-8 text: {}",
1375 String::from_utf8_lossy(trun)
1376 )));
1377 }
1378 };
1379 match u64::from_str_radix(num.trim(), 8) {
1380 Ok(n) => Ok(n),
1381 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1382 }
1383}
1384
1385fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1386 let o = format!("{:o}", val);
1387 let value = o.bytes().rev().chain(repeat(b'0'));
1388 for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1389 *slot = value;
1390 }
1391}
1392
1393fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1396 if src >= 8_589_934_592 || (src >= 2_097_152 && dst.len() == 8) {
1397 numeric_extended_into(dst, src);
1398 } else {
1399 octal_into(dst, src);
1400 }
1401}
1402
1403fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1406 if src[0] & 0x80 == 0 {
1407 octal_from(src)
1408 } else {
1409 Ok(numeric_extended_from(src))
1410 }
1411}
1412
1413fn numeric_extended_into(dst: &mut [u8], src: u64) {
1418 let len: usize = dst.len();
1419 for (slot, val) in dst.iter_mut().zip(
1420 repeat(0)
1421 .take(len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1423 ) {
1424 *slot = val;
1425 }
1426 dst[0] |= 0x80;
1427}
1428
1429fn numeric_extended_from(src: &[u8]) -> u64 {
1430 let mut dst: u64 = 0;
1431 let mut b_to_skip = 1;
1432 if src.len() == 8 {
1433 dst = (src[0] ^ 0x80) as u64;
1435 } else {
1436 b_to_skip = src.len() - 8;
1438 }
1439 for byte in src.iter().skip(b_to_skip) {
1440 dst <<= 8;
1441 dst |= *byte as u64;
1442 }
1443 dst
1444}
1445
1446fn truncate(slice: &[u8]) -> &[u8] {
1447 match slice.iter().position(|i| *i == 0) {
1448 Some(i) => &slice[..i],
1449 None => slice,
1450 }
1451}
1452
1453fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1456 if bytes.len() > slot.len() {
1457 Err(other("provided value is too long"))
1458 } else if bytes.iter().any(|b| *b == 0) {
1459 Err(other("provided value contains a nul byte"))
1460 } else {
1461 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1462 *slot = *val;
1463 }
1464 Ok(())
1465 }
1466}
1467
1468fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1477 let mut emitted = false;
1478 let mut needs_slash = false;
1479 for component in path.components() {
1480 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1481 match (component, is_link_name) {
1482 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1483 return Err(other("paths in archives must be relative"));
1484 }
1485 (Component::ParentDir, false) => {
1486 return Err(other("paths in archives must not have `..`"));
1487 }
1488 (Component::CurDir, false) if path.components().count() == 1 => {}
1490 (Component::CurDir, false) => continue,
1491 (Component::Normal(_), _) | (_, true) => {}
1492 };
1493 if needs_slash {
1494 copy(&mut slot, b"/")?;
1495 }
1496 if bytes.contains(&b'/') {
1497 if let Component::Normal(..) = component {
1498 return Err(other("path component in archive cannot contain `/`"));
1499 }
1500 }
1501 copy(&mut slot, &bytes)?;
1502 if &*bytes != b"/" {
1503 needs_slash = true;
1504 }
1505 emitted = true;
1506 }
1507 if !emitted {
1508 return Err(other("paths in archives must have at least one component"));
1509 }
1510 if ends_with_slash(path) {
1511 copy(&mut slot, &[b'/'])?;
1512 }
1513 return Ok(());
1514
1515 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1516 copy_into(slot, bytes)?;
1517 let tmp = std::mem::take(slot);
1518 *slot = &mut tmp[bytes.len()..];
1519 Ok(())
1520 }
1521}
1522
1523#[cfg(target_arch = "wasm32")]
1524fn ends_with_slash(p: &Path) -> bool {
1525 p.to_string_lossy().ends_with('/')
1526}
1527
1528#[cfg(windows)]
1529fn ends_with_slash(p: &Path) -> bool {
1530 let last = p.as_os_str().encode_wide().last();
1531 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1532}
1533
1534#[cfg(any(unix, target_os = "redox"))]
1535fn ends_with_slash(p: &Path) -> bool {
1536 p.as_os_str().as_bytes().ends_with(&[b'/'])
1537}
1538
1539#[cfg(any(windows, target_arch = "wasm32"))]
1540pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1541 p.as_os_str()
1542 .to_str()
1543 .map(|s| s.as_bytes())
1544 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1545 .map(|bytes| {
1546 if bytes.contains(&b'\\') {
1547 let mut bytes = bytes.to_owned();
1549 for b in &mut bytes {
1550 if *b == b'\\' {
1551 *b = b'/';
1552 }
1553 }
1554 Cow::Owned(bytes)
1555 } else {
1556 Cow::Borrowed(bytes)
1557 }
1558 })
1559}
1560
1561#[cfg(any(unix, target_os = "redox"))]
1562pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1564 Ok(Cow::Borrowed(p.as_os_str().as_bytes()))
1565}
1566
1567#[cfg(windows)]
1568pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1571 return match bytes {
1572 Cow::Borrowed(bytes) => {
1573 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1574 Ok(Cow::Borrowed(Path::new(s)))
1575 }
1576 Cow::Owned(bytes) => {
1577 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1578 Ok(Cow::Owned(PathBuf::from(s)))
1579 }
1580 };
1581
1582 fn not_unicode(v: &[u8]) -> io::Error {
1583 other(&format!(
1584 "only Unicode paths are supported on Windows: {}",
1585 String::from_utf8_lossy(v)
1586 ))
1587 }
1588}
1589
1590#[cfg(any(unix, target_os = "redox"))]
1591pub fn bytes2path(bytes: Cow<'_, [u8]>) -> io::Result<Cow<'_, Path>> {
1593 use std::ffi::{OsStr, OsString};
1594
1595 Ok(match bytes {
1596 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1597 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1598 })
1599}
1600
1601#[cfg(target_arch = "wasm32")]
1602pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1603 Ok(match bytes {
1604 Cow::Borrowed(bytes) => {
1605 Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1606 }
1607 Cow::Owned(bytes) => {
1608 Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1609 }
1610 })
1611}
1612
1613#[cfg(target_arch = "wasm32")]
1614fn invalid_utf8<T>(_: T) -> io::Error {
1615 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1616}