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 std::{
9 fs::Metadata,
10 path::{Component, Path, PathBuf},
11};
12use tokio::io;
13
14use crate::{other, EntryType};
15
16#[repr(C)]
18#[allow(missing_docs)]
19pub struct Header {
20 bytes: [u8; 512],
21}
22
23#[derive(Clone, Copy, PartialEq, Eq, Debug)]
26#[non_exhaustive]
27pub enum HeaderMode {
28 Complete,
31
32 Deterministic,
35}
36
37#[repr(C)]
39#[allow(missing_docs)]
40pub struct OldHeader {
41 pub name: [u8; 100],
42 pub mode: [u8; 8],
43 pub uid: [u8; 8],
44 pub gid: [u8; 8],
45 pub size: [u8; 12],
46 pub mtime: [u8; 12],
47 pub cksum: [u8; 8],
48 pub linkflag: [u8; 1],
49 pub linkname: [u8; 100],
50 pub pad: [u8; 255],
51}
52
53#[repr(C)]
55#[allow(missing_docs)]
56pub struct UstarHeader {
57 pub name: [u8; 100],
58 pub mode: [u8; 8],
59 pub uid: [u8; 8],
60 pub gid: [u8; 8],
61 pub size: [u8; 12],
62 pub mtime: [u8; 12],
63 pub cksum: [u8; 8],
64 pub typeflag: [u8; 1],
65 pub linkname: [u8; 100],
66
67 pub magic: [u8; 6],
69 pub version: [u8; 2],
70 pub uname: [u8; 32],
71 pub gname: [u8; 32],
72 pub dev_major: [u8; 8],
73 pub dev_minor: [u8; 8],
74 pub prefix: [u8; 155],
75 pub pad: [u8; 12],
76}
77
78#[repr(C)]
80#[allow(missing_docs)]
81pub struct GnuHeader {
82 pub name: [u8; 100],
83 pub mode: [u8; 8],
84 pub uid: [u8; 8],
85 pub gid: [u8; 8],
86 pub size: [u8; 12],
87 pub mtime: [u8; 12],
88 pub cksum: [u8; 8],
89 pub typeflag: [u8; 1],
90 pub linkname: [u8; 100],
91
92 pub magic: [u8; 6],
94 pub version: [u8; 2],
95 pub uname: [u8; 32],
96 pub gname: [u8; 32],
97 pub dev_major: [u8; 8],
98 pub dev_minor: [u8; 8],
99 pub atime: [u8; 12],
100 pub ctime: [u8; 12],
101 pub offset: [u8; 12],
102 pub longnames: [u8; 4],
103 pub unused: [u8; 1],
104 pub sparse: [GnuSparseHeader; 4],
105 pub isextended: [u8; 1],
106 pub realsize: [u8; 12],
107 pub pad: [u8; 17],
108}
109
110#[repr(C)]
114#[allow(missing_docs)]
115pub struct GnuSparseHeader {
116 pub offset: [u8; 12],
117 pub numbytes: [u8; 12],
118}
119
120#[repr(C)]
125#[allow(missing_docs)]
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: &Metadata) {
280 self.fill_from(meta, HeaderMode::Complete);
281 }
282
283 pub fn set_metadata_in_mode(&mut self, meta: &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(|h| h.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 Some(Cow::Borrowed(truncate(&old.linkname)))
403 } else {
404 None
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 #[allow(clippy::manual_map)]
513 pub fn username_bytes(&self) -> Option<&[u8]> {
514 if let Some(ustar) = self.as_ustar() {
515 Some(ustar.username_bytes())
516 } else if let Some(gnu) = self.as_gnu() {
517 Some(gnu.username_bytes())
518 } else {
519 None
520 }
521 }
522
523 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
528 if let Some(ustar) = self.as_ustar_mut() {
529 return ustar.set_username(name);
530 }
531 if let Some(gnu) = self.as_gnu_mut() {
532 gnu.set_username(name)
533 } else {
534 Err(other("not a ustar or gnu archive, cannot set username"))
535 }
536 }
537
538 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
545 match self.groupname_bytes() {
546 Some(bytes) => str::from_utf8(bytes).map(Some),
547 None => Ok(None),
548 }
549 }
550
551 #[allow(clippy::manual_map)]
556 pub fn groupname_bytes(&self) -> Option<&[u8]> {
557 if let Some(ustar) = self.as_ustar() {
558 Some(ustar.groupname_bytes())
559 } else if let Some(gnu) = self.as_gnu() {
560 Some(gnu.groupname_bytes())
561 } else {
562 None
563 }
564 }
565
566 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
571 if let Some(ustar) = self.as_ustar_mut() {
572 return ustar.set_groupname(name);
573 }
574 if let Some(gnu) = self.as_gnu_mut() {
575 gnu.set_groupname(name)
576 } else {
577 Err(other("not a ustar or gnu archive, cannot set groupname"))
578 }
579 }
580
581 pub fn device_major(&self) -> io::Result<Option<u32>> {
589 if let Some(ustar) = self.as_ustar() {
590 ustar.device_major().map(Some)
591 } else if let Some(gnu) = self.as_gnu() {
592 gnu.device_major().map(Some)
593 } else {
594 Ok(None)
595 }
596 }
597
598 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
603 if let Some(ustar) = self.as_ustar_mut() {
604 ustar.set_device_major(major);
605 Ok(())
606 } else if let Some(gnu) = self.as_gnu_mut() {
607 gnu.set_device_major(major);
608 Ok(())
609 } else {
610 Err(other("not a ustar or gnu archive, cannot set dev_major"))
611 }
612 }
613
614 pub fn device_minor(&self) -> io::Result<Option<u32>> {
622 if let Some(ustar) = self.as_ustar() {
623 ustar.device_minor().map(Some)
624 } else if let Some(gnu) = self.as_gnu() {
625 gnu.device_minor().map(Some)
626 } else {
627 Ok(None)
628 }
629 }
630
631 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
636 if let Some(ustar) = self.as_ustar_mut() {
637 ustar.set_device_minor(minor);
638 Ok(())
639 } else if let Some(gnu) = self.as_gnu_mut() {
640 gnu.set_device_minor(minor);
641 Ok(())
642 } else {
643 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
644 }
645 }
646
647 pub fn entry_type(&self) -> EntryType {
649 EntryType::new(self.as_old().linkflag[0])
650 }
651
652 pub fn set_entry_type(&mut self, ty: EntryType) {
654 self.as_old_mut().linkflag = [ty.as_byte()];
655 }
656
657 pub fn cksum(&self) -> io::Result<u32> {
661 octal_from(&self.as_old().cksum)
662 .map(|u| u as u32)
663 .map_err(|err| {
664 io::Error::new(
665 err.kind(),
666 format!("{} when getting cksum for {}", err, self.path_lossy()),
667 )
668 })
669 }
670
671 pub fn set_cksum(&mut self) {
674 let cksum = self.calculate_cksum();
675 octal_into(&mut self.as_old_mut().cksum, cksum);
676 }
677
678 fn calculate_cksum(&self) -> u32 {
679 let old = self.as_old();
680 let start = old as *const _ as usize;
681 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
682 let offset = cksum_start - start;
683 let len = old.cksum.len();
684 self.bytes[0..offset]
685 .iter()
686 .chain(iter::repeat(&b' ').take(len))
687 .chain(&self.bytes[offset + len..])
688 .fold(0, |a, b| a + (*b as u32))
689 }
690
691 fn fill_from(&mut self, meta: &Metadata, mode: HeaderMode) {
692 self.fill_platform_from(meta, mode);
693 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
695 0
696 } else {
697 meta.len()
698 });
699 if let Some(ustar) = self.as_ustar_mut() {
700 ustar.set_device_major(0);
701 ustar.set_device_minor(0);
702 }
703 if let Some(gnu) = self.as_gnu_mut() {
704 gnu.set_device_major(0);
705 gnu.set_device_minor(0);
706 }
707 }
708
709 #[cfg(target_arch = "wasm32")]
710 #[allow(unused_variables)]
711 fn fill_platform_from(&mut self, meta: &Metadata, mode: HeaderMode) {
712 unimplemented!();
713 }
714
715 #[cfg(any(unix, target_os = "redox"))]
716 fn fill_platform_from(&mut self, meta: &Metadata, mode: HeaderMode) {
717 match mode {
718 HeaderMode::Complete => {
719 self.set_mtime(meta.mtime() as u64);
720 self.set_uid(meta.uid() as u64);
721 self.set_gid(meta.gid() as u64);
722 self.set_mode(meta.mode());
723 }
724 HeaderMode::Deterministic => {
725 self.set_mtime(0);
726 self.set_uid(0);
727 self.set_gid(0);
728
729 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
731 0o755
732 } else {
733 0o644
734 };
735 self.set_mode(fs_mode);
736 }
737 }
738
739 self.set_entry_type(entry_type(meta.mode()));
750
751 #[cfg(not(target_os = "redox"))]
752 fn entry_type(mode: u32) -> EntryType {
753 match mode as libc::mode_t & libc::S_IFMT {
754 libc::S_IFREG => EntryType::file(),
755 libc::S_IFLNK => EntryType::symlink(),
756 libc::S_IFCHR => EntryType::character_special(),
757 libc::S_IFBLK => EntryType::block_special(),
758 libc::S_IFDIR => EntryType::dir(),
759 libc::S_IFIFO => EntryType::fifo(),
760 _ => EntryType::new(b' '),
761 }
762 }
763
764 #[cfg(target_os = "redox")]
765 fn entry_type(mode: u32) -> EntryType {
766 use syscall;
767 match mode as u16 & syscall::MODE_TYPE {
768 syscall::MODE_FILE => EntryType::file(),
769 syscall::MODE_SYMLINK => EntryType::symlink(),
770 syscall::MODE_DIR => EntryType::dir(),
771 _ => EntryType::new(b' '),
772 }
773 }
774 }
775
776 #[cfg(windows)]
777 fn fill_platform_from(&mut self, meta: &Metadata, mode: HeaderMode) {
778 match mode {
780 HeaderMode::Complete => {
781 self.set_uid(0);
782 self.set_gid(0);
783 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
788 self.set_mtime(mtime);
789 let fs_mode = {
790 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
791 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
792 match (meta.is_dir(), readonly != 0) {
793 (true, false) => 0o755,
794 (true, true) => 0o555,
795 (false, false) => 0o644,
796 (false, true) => 0o444,
797 }
798 };
799 self.set_mode(fs_mode);
800 }
801 HeaderMode::Deterministic => {
802 self.set_uid(0);
803 self.set_gid(0);
804 self.set_mtime(0);
805 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
806 self.set_mode(fs_mode);
807 }
808 }
809
810 let ft = meta.file_type();
811 self.set_entry_type(if ft.is_dir() {
812 EntryType::dir()
813 } else if ft.is_file() {
814 EntryType::file()
815 } else if ft.is_symlink() {
816 EntryType::symlink()
817 } else {
818 EntryType::new(b' ')
819 });
820 }
821
822 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
823 if let Ok(entry_size) = self.entry_size() {
824 b.field("entry_size", &entry_size);
825 }
826 if let Ok(size) = self.size() {
827 b.field("size", &size);
828 }
829 if let Ok(path) = self.path() {
830 b.field("path", &path);
831 }
832 if let Ok(link_name) = self.link_name() {
833 b.field("link_name", &link_name);
834 }
835 if let Ok(mode) = self.mode() {
836 b.field("mode", &DebugAsOctal(mode));
837 }
838 if let Ok(uid) = self.uid() {
839 b.field("uid", &uid);
840 }
841 if let Ok(gid) = self.gid() {
842 b.field("gid", &gid);
843 }
844 if let Ok(mtime) = self.mtime() {
845 b.field("mtime", &mtime);
846 }
847 if let Ok(username) = self.username() {
848 b.field("username", &username);
849 }
850 if let Ok(groupname) = self.groupname() {
851 b.field("groupname", &groupname);
852 }
853 if let Ok(device_major) = self.device_major() {
854 b.field("device_major", &device_major);
855 }
856 if let Ok(device_minor) = self.device_minor() {
857 b.field("device_minor", &device_minor);
858 }
859 if let Ok(cksum) = self.cksum() {
860 b.field("cksum", &cksum);
861 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
862 }
863 }
864}
865
866struct DebugAsOctal<T>(T);
867
868impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
869 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
870 fmt::Octal::fmt(&self.0, f)
871 }
872}
873
874unsafe fn cast<T, U>(a: &T) -> &U {
875 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
876 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
877 &*(a as *const T as *const U)
878}
879
880unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
881 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
882 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
883 &mut *(a as *mut T as *mut U)
884}
885
886impl Clone for Header {
887 fn clone(&self) -> Header {
888 Header { bytes: self.bytes }
889 }
890}
891
892impl fmt::Debug for Header {
893 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
894 if let Some(me) = self.as_ustar() {
895 me.fmt(f)
896 } else if let Some(me) = self.as_gnu() {
897 me.fmt(f)
898 } else {
899 self.as_old().fmt(f)
900 }
901 }
902}
903
904impl OldHeader {
905 pub fn as_header(&self) -> &Header {
907 unsafe { cast(self) }
908 }
909
910 pub fn as_header_mut(&mut self) -> &mut Header {
912 unsafe { cast_mut(self) }
913 }
914}
915
916impl fmt::Debug for OldHeader {
917 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
918 let mut f = f.debug_struct("OldHeader");
919 self.as_header().debug_fields(&mut f);
920 f.finish()
921 }
922}
923
924impl UstarHeader {
925 pub fn path_bytes(&self) -> Cow<[u8]> {
927 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
928 Cow::Borrowed(truncate(&self.name))
929 } else {
930 let mut bytes = Vec::new();
931 let prefix = truncate(&self.prefix);
932 if !prefix.is_empty() {
933 bytes.extend_from_slice(prefix);
934 bytes.push(b'/');
935 }
936 bytes.extend_from_slice(truncate(&self.name));
937 Cow::Owned(bytes)
938 }
939 }
940
941 fn path_lossy(&self) -> String {
943 String::from_utf8_lossy(&self.path_bytes()).to_string()
944 }
945
946 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
948 self._set_path(p.as_ref())
949 }
950
951 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
952 let bytes = path2bytes(path)?;
961 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
962 if bytes.len() <= maxnamelen {
963 copy_path_into(&mut self.name, path, false).map_err(|err| {
964 io::Error::new(
965 err.kind(),
966 format!("{} when setting path for {}", err, self.path_lossy()),
967 )
968 })?;
969 } else {
970 let mut prefix = path;
971 let mut prefixlen;
972 loop {
973 match prefix.parent() {
974 Some(parent) => prefix = parent,
975 None => {
976 return Err(other(&format!(
977 "path cannot be split to be inserted into archive: {}",
978 path.display()
979 )));
980 }
981 }
982 prefixlen = path2bytes(prefix)?.len();
983 if prefixlen <= maxprefixlen {
984 break;
985 }
986 }
987 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
988 io::Error::new(
989 err.kind(),
990 format!("{} when setting path for {}", err, self.path_lossy()),
991 )
992 })?;
993 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
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 }
1001 Ok(())
1002 }
1003
1004 pub fn username_bytes(&self) -> &[u8] {
1006 truncate(&self.uname)
1007 }
1008
1009 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1011 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1012 io::Error::new(
1013 err.kind(),
1014 format!("{} when setting username for {}", err, self.path_lossy()),
1015 )
1016 })
1017 }
1018
1019 pub fn groupname_bytes(&self) -> &[u8] {
1021 truncate(&self.gname)
1022 }
1023
1024 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1026 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1027 io::Error::new(
1028 err.kind(),
1029 format!("{} when setting groupname for {}", err, self.path_lossy()),
1030 )
1031 })
1032 }
1033
1034 pub fn device_major(&self) -> io::Result<u32> {
1036 octal_from(&self.dev_major)
1037 .map(|u| u as u32)
1038 .map_err(|err| {
1039 io::Error::new(
1040 err.kind(),
1041 format!(
1042 "{} when getting device_major for {}",
1043 err,
1044 self.path_lossy()
1045 ),
1046 )
1047 })
1048 }
1049
1050 pub fn set_device_major(&mut self, major: u32) {
1052 octal_into(&mut self.dev_major, major);
1053 }
1054
1055 pub fn device_minor(&self) -> io::Result<u32> {
1057 octal_from(&self.dev_minor)
1058 .map(|u| u as u32)
1059 .map_err(|err| {
1060 io::Error::new(
1061 err.kind(),
1062 format!(
1063 "{} when getting device_minor for {}",
1064 err,
1065 self.path_lossy()
1066 ),
1067 )
1068 })
1069 }
1070
1071 pub fn set_device_minor(&mut self, minor: u32) {
1073 octal_into(&mut self.dev_minor, minor);
1074 }
1075
1076 pub fn as_header(&self) -> &Header {
1078 unsafe { cast(self) }
1079 }
1080
1081 pub fn as_header_mut(&mut self) -> &mut Header {
1083 unsafe { cast_mut(self) }
1084 }
1085}
1086
1087impl fmt::Debug for UstarHeader {
1088 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1089 let mut f = f.debug_struct("UstarHeader");
1090 self.as_header().debug_fields(&mut f);
1091 f.finish()
1092 }
1093}
1094
1095impl GnuHeader {
1096 pub fn username_bytes(&self) -> &[u8] {
1098 truncate(&self.uname)
1099 }
1100
1101 fn fullname_lossy(&self) -> String {
1103 format!(
1104 "{}:{}",
1105 String::from_utf8_lossy(self.groupname_bytes()),
1106 String::from_utf8_lossy(self.username_bytes()),
1107 )
1108 }
1109
1110 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1112 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1113 io::Error::new(
1114 err.kind(),
1115 format!(
1116 "{} when setting username for {}",
1117 err,
1118 self.fullname_lossy()
1119 ),
1120 )
1121 })
1122 }
1123
1124 pub fn groupname_bytes(&self) -> &[u8] {
1126 truncate(&self.gname)
1127 }
1128
1129 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1131 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1132 io::Error::new(
1133 err.kind(),
1134 format!(
1135 "{} when setting groupname for {}",
1136 err,
1137 self.fullname_lossy()
1138 ),
1139 )
1140 })
1141 }
1142
1143 pub fn device_major(&self) -> io::Result<u32> {
1145 octal_from(&self.dev_major)
1146 .map(|u| u as u32)
1147 .map_err(|err| {
1148 io::Error::new(
1149 err.kind(),
1150 format!(
1151 "{} when getting device_major for {}",
1152 err,
1153 self.fullname_lossy()
1154 ),
1155 )
1156 })
1157 }
1158
1159 pub fn set_device_major(&mut self, major: u32) {
1161 octal_into(&mut self.dev_major, major);
1162 }
1163
1164 pub fn device_minor(&self) -> io::Result<u32> {
1166 octal_from(&self.dev_minor)
1167 .map(|u| u as u32)
1168 .map_err(|err| {
1169 io::Error::new(
1170 err.kind(),
1171 format!(
1172 "{} when getting device_minor for {}",
1173 err,
1174 self.fullname_lossy()
1175 ),
1176 )
1177 })
1178 }
1179
1180 pub fn set_device_minor(&mut self, minor: u32) {
1182 octal_into(&mut self.dev_minor, minor);
1183 }
1184
1185 pub fn atime(&self) -> io::Result<u64> {
1187 num_field_wrapper_from(&self.atime).map_err(|err| {
1188 io::Error::new(
1189 err.kind(),
1190 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1191 )
1192 })
1193 }
1194
1195 pub fn set_atime(&mut self, atime: u64) {
1200 num_field_wrapper_into(&mut self.atime, atime);
1201 }
1202
1203 pub fn ctime(&self) -> io::Result<u64> {
1205 num_field_wrapper_from(&self.ctime).map_err(|err| {
1206 io::Error::new(
1207 err.kind(),
1208 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1209 )
1210 })
1211 }
1212
1213 pub fn set_ctime(&mut self, ctime: u64) {
1218 num_field_wrapper_into(&mut self.ctime, ctime);
1219 }
1220
1221 pub fn real_size(&self) -> io::Result<u64> {
1226 octal_from(&self.realsize).map_err(|err| {
1227 io::Error::new(
1228 err.kind(),
1229 format!(
1230 "{} when getting real_size for {}",
1231 err,
1232 self.fullname_lossy()
1233 ),
1234 )
1235 })
1236 }
1237
1238 pub fn is_extended(&self) -> bool {
1244 self.isextended[0] == 1
1245 }
1246
1247 pub fn as_header(&self) -> &Header {
1249 unsafe { cast(self) }
1250 }
1251
1252 pub fn as_header_mut(&mut self) -> &mut Header {
1254 unsafe { cast_mut(self) }
1255 }
1256}
1257
1258impl fmt::Debug for GnuHeader {
1259 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1260 let mut f = f.debug_struct("GnuHeader");
1261 self.as_header().debug_fields(&mut f);
1262 if let Ok(atime) = self.atime() {
1263 f.field("atime", &atime);
1264 }
1265 if let Ok(ctime) = self.ctime() {
1266 f.field("ctime", &ctime);
1267 }
1268 f.field("is_extended", &self.is_extended())
1269 .field("sparse", &DebugSparseHeaders(&self.sparse))
1270 .finish()
1271 }
1272}
1273
1274struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1275
1276impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1277 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1278 let mut f = f.debug_list();
1279 for header in self.0 {
1280 if !header.is_empty() {
1281 f.entry(header);
1282 }
1283 }
1284 f.finish()
1285 }
1286}
1287
1288impl GnuSparseHeader {
1289 pub fn is_empty(&self) -> bool {
1291 self.offset[0] == 0 || self.numbytes[0] == 0
1292 }
1293
1294 pub fn offset(&self) -> io::Result<u64> {
1298 octal_from(&self.offset).map_err(|err| {
1299 io::Error::new(
1300 err.kind(),
1301 format!("{} when getting offset from sparse header", err),
1302 )
1303 })
1304 }
1305
1306 pub fn length(&self) -> io::Result<u64> {
1310 octal_from(&self.numbytes).map_err(|err| {
1311 io::Error::new(
1312 err.kind(),
1313 format!("{} when getting length from sparse header", err),
1314 )
1315 })
1316 }
1317}
1318
1319impl fmt::Debug for GnuSparseHeader {
1320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1321 let mut f = f.debug_struct("GnuSparseHeader");
1322 if let Ok(offset) = self.offset() {
1323 f.field("offset", &offset);
1324 }
1325 if let Ok(length) = self.length() {
1326 f.field("length", &length);
1327 }
1328 f.finish()
1329 }
1330}
1331
1332impl GnuExtSparseHeader {
1333 pub fn new() -> GnuExtSparseHeader {
1335 unsafe { mem::zeroed() }
1336 }
1337
1338 pub fn as_bytes(&self) -> &[u8; 512] {
1340 debug_assert_eq!(mem::size_of_val(self), 512);
1341 unsafe { &*(self as *const GnuExtSparseHeader as *const [u8; 512]) }
1342 }
1343
1344 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1346 debug_assert_eq!(mem::size_of_val(self), 512);
1347 unsafe { &mut *(self as *mut GnuExtSparseHeader as *mut [u8; 512]) }
1348 }
1349
1350 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1355 &self.sparse
1356 }
1357
1358 pub fn is_extended(&self) -> bool {
1360 self.isextended[0] == 1
1361 }
1362}
1363
1364impl Default for GnuExtSparseHeader {
1365 fn default() -> Self {
1366 Self::new()
1367 }
1368}
1369
1370fn octal_from(slice: &[u8]) -> io::Result<u64> {
1371 let trun = truncate(slice);
1372 let num = match str::from_utf8(trun) {
1373 Ok(n) => n,
1374 Err(_) => {
1375 return Err(other(&format!(
1376 "numeric field did not have utf-8 text: {}",
1377 String::from_utf8_lossy(trun)
1378 )));
1379 }
1380 };
1381 match u64::from_str_radix(num.trim(), 8) {
1382 Ok(n) => Ok(n),
1383 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1384 }
1385}
1386
1387fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1388 let o = format!("{:o}", val);
1389 let value = o.bytes().rev().chain(repeat(b'0'));
1390 for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1391 *slot = value;
1392 }
1393}
1394
1395fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1398 if src >= 8_589_934_592 || (src >= 2_097_152 && dst.len() == 8) {
1399 numeric_extended_into(dst, src);
1400 } else {
1401 octal_into(dst, src);
1402 }
1403}
1404
1405fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1408 if src[0] & 0x80 != 0 {
1409 Ok(numeric_extended_from(src))
1410 } else {
1411 octal_from(src)
1412 }
1413}
1414
1415fn numeric_extended_into(dst: &mut [u8], src: u64) {
1420 let len: usize = dst.len();
1421 for (slot, val) in dst.iter_mut().zip(
1422 repeat(0)
1423 .take(len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1425 ) {
1426 *slot = val;
1427 }
1428 dst[0] |= 0x80;
1429}
1430
1431fn numeric_extended_from(src: &[u8]) -> u64 {
1432 let mut dst: u64 = 0;
1433 let mut b_to_skip = 1;
1434 if src.len() == 8 {
1435 dst = (src[0] ^ 0x80) as u64;
1437 } else {
1438 b_to_skip = src.len() - 8;
1440 }
1441 for byte in src.iter().skip(b_to_skip) {
1442 dst <<= 8;
1443 dst |= *byte as u64;
1444 }
1445 dst
1446}
1447
1448fn truncate(slice: &[u8]) -> &[u8] {
1449 match slice.iter().position(|i| *i == 0) {
1450 Some(i) => &slice[..i],
1451 None => slice,
1452 }
1453}
1454
1455fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1458 if bytes.len() > slot.len() {
1459 Err(other("provided value is too long"))
1460 } else if bytes.iter().any(|b| *b == 0) {
1461 Err(other("provided value contains a nul byte"))
1462 } else {
1463 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1464 *slot = *val;
1465 }
1466 Ok(())
1467 }
1468}
1469
1470fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1479 let mut emitted = false;
1480 let mut needs_slash = false;
1481 for component in path.components() {
1482 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1483 match (component, is_link_name) {
1484 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1485 return Err(other("paths in archives must be relative"));
1486 }
1487 (Component::ParentDir, false) => {
1488 return Err(other("paths in archives must not have `..`"));
1489 }
1490 (Component::CurDir, false) if path.components().count() == 1 => {}
1492 (Component::CurDir, false) => continue,
1493 (Component::Normal(_), _) | (_, true) => {}
1494 };
1495 if needs_slash {
1496 copy(&mut slot, b"/")?;
1497 }
1498 if bytes.contains(&b'/') {
1499 if let Component::Normal(..) = component {
1500 return Err(other("path component in archive cannot contain `/`"));
1501 }
1502 }
1503 copy(&mut slot, &bytes)?;
1504 if &*bytes != b"/" {
1505 needs_slash = true;
1506 }
1507 emitted = true;
1508 }
1509 if !emitted {
1510 return Err(other("paths in archives must have at least one component"));
1511 }
1512 if ends_with_slash(path) {
1513 copy(&mut slot, &[b'/'])?;
1514 }
1515 return Ok(());
1516
1517 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1518 copy_into(slot, bytes)?;
1519 let tmp = mem::take(slot);
1520 *slot = &mut tmp[bytes.len()..];
1521 Ok(())
1522 }
1523}
1524
1525#[cfg(target_arch = "wasm32")]
1526fn ends_with_slash(p: &Path) -> bool {
1527 p.to_string_lossy().ends_with('/')
1528}
1529
1530#[cfg(windows)]
1531fn ends_with_slash(p: &Path) -> bool {
1532 let last = p.as_os_str().encode_wide().last();
1533 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1534}
1535
1536#[cfg(any(unix, target_os = "redox"))]
1537fn ends_with_slash(p: &Path) -> bool {
1538 p.as_os_str().as_bytes().ends_with(&[b'/'])
1539}
1540
1541#[cfg(any(windows, target_arch = "wasm32"))]
1542pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1543 p.as_os_str()
1544 .to_str()
1545 .map(|s| s.as_bytes())
1546 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1547 .map(|bytes| {
1548 if bytes.contains(&b'\\') {
1549 let mut bytes = bytes.to_owned();
1551 for b in &mut bytes {
1552 if *b == b'\\' {
1553 *b = b'/';
1554 }
1555 }
1556 Cow::Owned(bytes)
1557 } else {
1558 Cow::Borrowed(bytes)
1559 }
1560 })
1561}
1562
1563#[cfg(any(unix, target_os = "redox"))]
1564pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1566 Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1567}
1568
1569#[cfg(windows)]
1570pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1573 return match bytes {
1574 Cow::Borrowed(bytes) => {
1575 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1576 Ok(Cow::Borrowed(Path::new(s)))
1577 }
1578 Cow::Owned(bytes) => {
1579 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1580 Ok(Cow::Owned(PathBuf::from(s)))
1581 }
1582 };
1583
1584 fn not_unicode(v: &[u8]) -> io::Error {
1585 other(&format!(
1586 "only Unicode paths are supported on Windows: {}",
1587 String::from_utf8_lossy(v)
1588 ))
1589 }
1590}
1591
1592#[cfg(any(unix, target_os = "redox"))]
1593pub fn bytes2path(bytes: Cow<'_, [u8]>) -> io::Result<Cow<'_, Path>> {
1595 use std::ffi::{OsStr, OsString};
1596
1597 Ok(match bytes {
1598 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1599 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1600 })
1601}
1602
1603#[cfg(target_arch = "wasm32")]
1604pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1605 Ok(match bytes {
1606 Cow::Borrowed(bytes) => {
1607 Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1608 }
1609 Cow::Owned(bytes) => {
1610 Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1611 }
1612 })
1613}
1614
1615#[cfg(target_arch = "wasm32")]
1616fn invalid_utf8<T>(_: T) -> io::Error {
1617 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1618}