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
8#[cfg(feature = "fs")]
9use async_std::fs;
10use async_std::{
11 io,
12 path::{Component, Path, PathBuf},
13};
14
15use crate::{other, EntryType};
16
17#[repr(C)]
19#[allow(missing_docs)]
20pub struct Header {
21 bytes: [u8; 512],
22}
23
24#[derive(Clone, Copy, PartialEq, Eq, Debug)]
27#[non_exhaustive]
28pub enum HeaderMode {
29 Complete,
32
33 Deterministic,
36}
37
38#[repr(C)]
40#[allow(missing_docs)]
41pub struct OldHeader {
42 pub name: [u8; 100],
43 pub mode: [u8; 8],
44 pub uid: [u8; 8],
45 pub gid: [u8; 8],
46 pub size: [u8; 12],
47 pub mtime: [u8; 12],
48 pub cksum: [u8; 8],
49 pub linkflag: [u8; 1],
50 pub linkname: [u8; 100],
51 pub pad: [u8; 255],
52}
53
54#[repr(C)]
56#[allow(missing_docs)]
57pub struct UstarHeader {
58 pub name: [u8; 100],
59 pub mode: [u8; 8],
60 pub uid: [u8; 8],
61 pub gid: [u8; 8],
62 pub size: [u8; 12],
63 pub mtime: [u8; 12],
64 pub cksum: [u8; 8],
65 pub typeflag: [u8; 1],
66 pub linkname: [u8; 100],
67
68 pub magic: [u8; 6],
70 pub version: [u8; 2],
71 pub uname: [u8; 32],
72 pub gname: [u8; 32],
73 pub dev_major: [u8; 8],
74 pub dev_minor: [u8; 8],
75 pub prefix: [u8; 155],
76 pub pad: [u8; 12],
77}
78
79#[repr(C)]
81#[allow(missing_docs)]
82pub struct GnuHeader {
83 pub name: [u8; 100],
84 pub mode: [u8; 8],
85 pub uid: [u8; 8],
86 pub gid: [u8; 8],
87 pub size: [u8; 12],
88 pub mtime: [u8; 12],
89 pub cksum: [u8; 8],
90 pub typeflag: [u8; 1],
91 pub linkname: [u8; 100],
92
93 pub magic: [u8; 6],
95 pub version: [u8; 2],
96 pub uname: [u8; 32],
97 pub gname: [u8; 32],
98 pub dev_major: [u8; 8],
99 pub dev_minor: [u8; 8],
100 pub atime: [u8; 12],
101 pub ctime: [u8; 12],
102 pub offset: [u8; 12],
103 pub longnames: [u8; 4],
104 pub unused: [u8; 1],
105 pub sparse: [GnuSparseHeader; 4],
106 pub isextended: [u8; 1],
107 pub realsize: [u8; 12],
108 pub pad: [u8; 17],
109}
110
111#[repr(C)]
115#[allow(missing_docs)]
116pub struct GnuSparseHeader {
117 pub offset: [u8; 12],
118 pub numbytes: [u8; 12],
119}
120
121#[repr(C)]
126#[allow(missing_docs)]
127#[derive(Debug)]
128pub struct GnuExtSparseHeader {
129 pub sparse: [GnuSparseHeader; 21],
130 pub isextended: [u8; 1],
131 pub padding: [u8; 7],
132}
133
134impl Header {
135 pub fn new_gnu() -> Header {
141 let mut header = Header { bytes: [0; 512] };
142 unsafe {
143 let gnu = cast_mut::<_, GnuHeader>(&mut header);
144 gnu.magic = *b"ustar ";
145 gnu.version = *b" \0";
146 }
147 header.set_mtime(0);
148 header
149 }
150
151 pub fn new_ustar() -> Header {
159 let mut header = Header { bytes: [0; 512] };
160 unsafe {
161 let gnu = cast_mut::<_, UstarHeader>(&mut header);
162 gnu.magic = *b"ustar\0";
163 gnu.version = *b"00";
164 }
165 header.set_mtime(0);
166 header
167 }
168
169 pub fn new_old() -> Header {
176 let mut header = Header { bytes: [0; 512] };
177 header.set_mtime(0);
178 header
179 }
180
181 fn is_ustar(&self) -> bool {
182 let ustar = unsafe { cast::<_, UstarHeader>(self) };
183 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
184 }
185
186 fn is_gnu(&self) -> bool {
187 let ustar = unsafe { cast::<_, UstarHeader>(self) };
188 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
189 }
190
191 pub fn as_old(&self) -> &OldHeader {
196 unsafe { cast(self) }
197 }
198
199 pub fn as_old_mut(&mut self) -> &mut OldHeader {
201 unsafe { cast_mut(self) }
202 }
203
204 pub fn as_ustar(&self) -> Option<&UstarHeader> {
214 if self.is_ustar() {
215 Some(unsafe { cast(self) })
216 } else {
217 None
218 }
219 }
220
221 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
223 if self.is_ustar() {
224 Some(unsafe { cast_mut(self) })
225 } else {
226 None
227 }
228 }
229
230 pub fn as_gnu(&self) -> Option<&GnuHeader> {
240 if self.is_gnu() {
241 Some(unsafe { cast(self) })
242 } else {
243 None
244 }
245 }
246
247 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
249 if self.is_gnu() {
250 Some(unsafe { cast_mut(self) })
251 } else {
252 None
253 }
254 }
255
256 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
260 assert_eq!(bytes.len(), mem::size_of::<Header>());
261 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
262 unsafe { &*(bytes.as_ptr() as *const Header) }
263 }
264
265 pub fn as_bytes(&self) -> &[u8; 512] {
267 &self.bytes
268 }
269
270 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
272 &mut self.bytes
273 }
274
275 #[cfg(feature = "fs")]
282 pub fn set_metadata(&mut self, meta: &fs::Metadata) {
283 self.fill_from(meta, HeaderMode::Complete);
284 }
285
286 #[cfg(feature = "fs")]
289 pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
290 self.fill_from(meta, mode);
291 }
292
293 pub fn entry_size(&self) -> io::Result<u64> {
302 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
303 io::Error::new(
304 err.kind(),
305 format!("{} when getting size for {}", err, self.path_lossy()),
306 )
307 })
308 }
309
310 pub fn size(&self) -> io::Result<u64> {
314 if self.entry_type().is_gnu_sparse() {
315 self.as_gnu()
316 .ok_or_else(|| other("sparse header was not a gnu header"))
317 .and_then(GnuHeader::real_size)
318 } else {
319 self.entry_size()
320 }
321 }
322
323 pub fn set_size(&mut self, size: u64) {
325 num_field_wrapper_into(&mut self.as_old_mut().size, size);
326 }
327
328 pub fn path(&self) -> io::Result<Cow<Path>> {
336 bytes2path(self.path_bytes())
337 }
338
339 pub fn path_bytes(&self) -> Cow<[u8]> {
347 if let Some(ustar) = self.as_ustar() {
348 ustar.path_bytes()
349 } else {
350 let name = truncate(&self.as_old().name);
351 Cow::Borrowed(name)
352 }
353 }
354
355 fn path_lossy(&self) -> String {
357 String::from_utf8_lossy(&self.path_bytes()).to_string()
358 }
359
360 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
366 self._set_path(p.as_ref())
367 }
368
369 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
370 if let Some(ustar) = self.as_ustar_mut() {
371 return ustar.set_path(path);
372 }
373 copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
374 io::Error::new(
375 err.kind(),
376 format!("{} when setting path for {}", err, self.path_lossy()),
377 )
378 })
379 }
380
381 pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
390 match self.link_name_bytes() {
391 Some(bytes) => bytes2path(bytes).map(Some),
392 None => Ok(None),
393 }
394 }
395
396 pub fn link_name_bytes(&self) -> Option<Cow<[u8]>> {
404 let old = self.as_old();
405 if old.linkname[0] == 0 {
406 None
407 } else {
408 Some(Cow::Borrowed(truncate(&old.linkname)))
409 }
410 }
411
412 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
418 self._set_link_name(p.as_ref())
419 }
420
421 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
422 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
423 io::Error::new(
424 err.kind(),
425 format!("{} when setting link name for {}", err, self.path_lossy()),
426 )
427 })
428 }
429
430 pub fn mode(&self) -> io::Result<u32> {
434 octal_from(&self.as_old().mode)
435 .map(|u| u as u32)
436 .map_err(|err| {
437 io::Error::new(
438 err.kind(),
439 format!("{} when getting mode for {}", err, self.path_lossy()),
440 )
441 })
442 }
443
444 pub fn set_mode(&mut self, mode: u32) {
446 octal_into(&mut self.as_old_mut().mode, mode);
447 }
448
449 pub fn uid(&self) -> io::Result<u64> {
453 num_field_wrapper_from(&self.as_old().uid)
454 .map(|u| u as u64)
455 .map_err(|err| {
456 io::Error::new(
457 err.kind(),
458 format!("{} when getting uid for {}", err, self.path_lossy()),
459 )
460 })
461 }
462
463 pub fn set_uid(&mut self, uid: u64) {
465 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
466 }
467
468 pub fn gid(&self) -> io::Result<u64> {
470 num_field_wrapper_from(&self.as_old().gid)
471 .map(|u| u as u64)
472 .map_err(|err| {
473 io::Error::new(
474 err.kind(),
475 format!("{} when getting gid for {}", err, self.path_lossy()),
476 )
477 })
478 }
479
480 pub fn set_gid(&mut self, gid: u64) {
482 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
483 }
484
485 pub fn mtime(&self) -> io::Result<u64> {
487 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
488 io::Error::new(
489 err.kind(),
490 format!("{} when getting mtime for {}", err, self.path_lossy()),
491 )
492 })
493 }
494
495 pub fn set_mtime(&mut self, mtime: u64) {
500 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
501 }
502
503 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
510 match self.username_bytes() {
511 Some(bytes) => str::from_utf8(bytes).map(Some),
512 None => Ok(None),
513 }
514 }
515
516 pub fn username_bytes(&self) -> Option<&[u8]> {
521 if let Some(ustar) = self.as_ustar() {
522 Some(ustar.username_bytes())
523 } else if let Some(gnu) = self.as_gnu() {
524 Some(gnu.username_bytes())
525 } else {
526 None
527 }
528 }
529
530 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
535 if let Some(ustar) = self.as_ustar_mut() {
536 return ustar.set_username(name);
537 }
538 if let Some(gnu) = self.as_gnu_mut() {
539 gnu.set_username(name)
540 } else {
541 Err(other("not a ustar or gnu archive, cannot set username"))
542 }
543 }
544
545 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
552 match self.groupname_bytes() {
553 Some(bytes) => str::from_utf8(bytes).map(Some),
554 None => Ok(None),
555 }
556 }
557
558 pub fn groupname_bytes(&self) -> Option<&[u8]> {
563 if let Some(ustar) = self.as_ustar() {
564 Some(ustar.groupname_bytes())
565 } else if let Some(gnu) = self.as_gnu() {
566 Some(gnu.groupname_bytes())
567 } else {
568 None
569 }
570 }
571
572 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
577 if let Some(ustar) = self.as_ustar_mut() {
578 return ustar.set_groupname(name);
579 }
580 if let Some(gnu) = self.as_gnu_mut() {
581 gnu.set_groupname(name)
582 } else {
583 Err(other("not a ustar or gnu archive, cannot set groupname"))
584 }
585 }
586
587 pub fn device_major(&self) -> io::Result<Option<u32>> {
595 if let Some(ustar) = self.as_ustar() {
596 ustar.device_major().map(Some)
597 } else if let Some(gnu) = self.as_gnu() {
598 gnu.device_major().map(Some)
599 } else {
600 Ok(None)
601 }
602 }
603
604 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
609 if let Some(ustar) = self.as_ustar_mut() {
610 ustar.set_device_major(major);
611 Ok(())
612 } else if let Some(gnu) = self.as_gnu_mut() {
613 gnu.set_device_major(major);
614 Ok(())
615 } else {
616 Err(other("not a ustar or gnu archive, cannot set dev_major"))
617 }
618 }
619
620 pub fn device_minor(&self) -> io::Result<Option<u32>> {
628 if let Some(ustar) = self.as_ustar() {
629 ustar.device_minor().map(Some)
630 } else if let Some(gnu) = self.as_gnu() {
631 gnu.device_minor().map(Some)
632 } else {
633 Ok(None)
634 }
635 }
636
637 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
642 if let Some(ustar) = self.as_ustar_mut() {
643 ustar.set_device_minor(minor);
644 Ok(())
645 } else if let Some(gnu) = self.as_gnu_mut() {
646 gnu.set_device_minor(minor);
647 Ok(())
648 } else {
649 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
650 }
651 }
652
653 pub fn entry_type(&self) -> EntryType {
655 EntryType::new(self.as_old().linkflag[0])
656 }
657
658 pub fn set_entry_type(&mut self, ty: EntryType) {
660 self.as_old_mut().linkflag = [ty.as_byte()];
661 }
662
663 pub fn cksum(&self) -> io::Result<u32> {
667 octal_from(&self.as_old().cksum)
668 .map(|u| u as u32)
669 .map_err(|err| {
670 io::Error::new(
671 err.kind(),
672 format!("{} when getting cksum for {}", err, self.path_lossy()),
673 )
674 })
675 }
676
677 pub fn set_cksum(&mut self) {
680 let cksum = self.calculate_cksum();
681 octal_into(&mut self.as_old_mut().cksum, cksum);
682 }
683
684 fn calculate_cksum(&self) -> u32 {
685 let old = self.as_old();
686 let start = old as *const _ as usize;
687 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
688 let offset = cksum_start - start;
689 let len = old.cksum.len();
690 self.bytes[0..offset]
691 .iter()
692 .chain(iter::repeat(&b' ').take(len))
693 .chain(&self.bytes[offset + len..])
694 .fold(0, |a, b| a + (*b as u32))
695 }
696
697 #[cfg(feature = "fs")]
698 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
699 self.fill_platform_from(meta, mode);
700 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
702 0
703 } else {
704 meta.len()
705 });
706 if let Some(ustar) = self.as_ustar_mut() {
707 ustar.set_device_major(0);
708 ustar.set_device_minor(0);
709 }
710 if let Some(gnu) = self.as_gnu_mut() {
711 gnu.set_device_major(0);
712 gnu.set_device_minor(0);
713 }
714 }
715
716 #[cfg(feature = "fs")]
717 #[cfg(target_arch = "wasm32")]
718 #[allow(unused_variables)]
719 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
720 unimplemented!();
721 }
722
723 #[cfg(feature = "fs")]
724 #[cfg(any(unix, target_os = "redox"))]
725 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
726 match mode {
727 HeaderMode::Complete => {
728 self.set_mtime(meta.mtime() as u64);
729 self.set_uid(meta.uid() as u64);
730 self.set_gid(meta.gid() as u64);
731 self.set_mode(meta.mode() as u32);
732 }
733 HeaderMode::Deterministic => {
734 self.set_mtime(0);
735 self.set_uid(0);
736 self.set_gid(0);
737
738 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
740 0o755
741 } else {
742 0o644
743 };
744 self.set_mode(fs_mode);
745 }
746 }
747
748 self.set_entry_type(entry_type(meta.mode()));
759
760 #[cfg(not(target_os = "redox"))]
761 fn entry_type(mode: u32) -> EntryType {
762 match mode as libc::mode_t & libc::S_IFMT {
763 libc::S_IFREG => EntryType::file(),
764 libc::S_IFLNK => EntryType::symlink(),
765 libc::S_IFCHR => EntryType::character_special(),
766 libc::S_IFBLK => EntryType::block_special(),
767 libc::S_IFDIR => EntryType::dir(),
768 libc::S_IFIFO => EntryType::fifo(),
769 _ => EntryType::new(b' '),
770 }
771 }
772
773 #[cfg(target_os = "redox")]
774 fn entry_type(mode: u32) -> EntryType {
775 use syscall;
776 match mode as u16 & syscall::MODE_TYPE {
777 syscall::MODE_FILE => EntryType::file(),
778 syscall::MODE_SYMLINK => EntryType::symlink(),
779 syscall::MODE_DIR => EntryType::dir(),
780 _ => EntryType::new(b' '),
781 }
782 }
783 }
784
785 #[cfg(feature = "fs")]
786 #[cfg(windows)]
787 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
788 match mode {
790 HeaderMode::Complete => {
791 self.set_uid(0);
792 self.set_gid(0);
793 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
798 self.set_mtime(mtime);
799 let fs_mode = {
800 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
801 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
802 match (meta.is_dir(), readonly != 0) {
803 (true, false) => 0o755,
804 (true, true) => 0o555,
805 (false, false) => 0o644,
806 (false, true) => 0o444,
807 }
808 };
809 self.set_mode(fs_mode);
810 }
811 HeaderMode::Deterministic => {
812 self.set_uid(0);
813 self.set_gid(0);
814 self.set_mtime(0);
815 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
816 self.set_mode(fs_mode);
817 }
818 }
819
820 let ft = meta.file_type();
821 self.set_entry_type(if ft.is_dir() {
822 EntryType::dir()
823 } else if ft.is_file() {
824 EntryType::file()
825 } else if ft.is_symlink() {
826 EntryType::symlink()
827 } else {
828 EntryType::new(b' ')
829 });
830 }
831
832 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
833 if let Ok(entry_size) = self.entry_size() {
834 b.field("entry_size", &entry_size);
835 }
836 if let Ok(size) = self.size() {
837 b.field("size", &size);
838 }
839 if let Ok(path) = self.path() {
840 b.field("path", &path);
841 }
842 if let Ok(link_name) = self.link_name() {
843 b.field("link_name", &link_name);
844 }
845 if let Ok(mode) = self.mode() {
846 b.field("mode", &DebugAsOctal(mode));
847 }
848 if let Ok(uid) = self.uid() {
849 b.field("uid", &uid);
850 }
851 if let Ok(gid) = self.gid() {
852 b.field("gid", &gid);
853 }
854 if let Ok(mtime) = self.mtime() {
855 b.field("mtime", &mtime);
856 }
857 if let Ok(username) = self.username() {
858 b.field("username", &username);
859 }
860 if let Ok(groupname) = self.groupname() {
861 b.field("groupname", &groupname);
862 }
863 if let Ok(device_major) = self.device_major() {
864 b.field("device_major", &device_major);
865 }
866 if let Ok(device_minor) = self.device_minor() {
867 b.field("device_minor", &device_minor);
868 }
869 if let Ok(cksum) = self.cksum() {
870 b.field("cksum", &cksum);
871 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
872 }
873 }
874}
875
876struct DebugAsOctal<T>(T);
877
878impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
879 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
880 fmt::Octal::fmt(&self.0, f)
881 }
882}
883
884unsafe fn cast<T, U>(a: &T) -> &U {
885 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
886 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
887 &*(a as *const T as *const U)
888}
889
890unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
891 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
892 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
893 &mut *(a as *mut T as *mut U)
894}
895
896impl Clone for Header {
897 fn clone(&self) -> Header {
898 Header { bytes: self.bytes }
899 }
900}
901
902impl fmt::Debug for Header {
903 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
904 if let Some(me) = self.as_ustar() {
905 me.fmt(f)
906 } else if let Some(me) = self.as_gnu() {
907 me.fmt(f)
908 } else {
909 self.as_old().fmt(f)
910 }
911 }
912}
913
914impl OldHeader {
915 pub fn as_header(&self) -> &Header {
917 unsafe { cast(self) }
918 }
919
920 pub fn as_header_mut(&mut self) -> &mut Header {
922 unsafe { cast_mut(self) }
923 }
924}
925
926impl fmt::Debug for OldHeader {
927 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
928 let mut f = f.debug_struct("OldHeader");
929 self.as_header().debug_fields(&mut f);
930 f.finish()
931 }
932}
933
934impl UstarHeader {
935 pub fn path_bytes(&self) -> Cow<[u8]> {
937 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
938 Cow::Borrowed(truncate(&self.name))
939 } else {
940 let mut bytes = Vec::new();
941 let prefix = truncate(&self.prefix);
942 if !prefix.is_empty() {
943 bytes.extend_from_slice(prefix);
944 bytes.push(b'/');
945 }
946 bytes.extend_from_slice(truncate(&self.name));
947 Cow::Owned(bytes)
948 }
949 }
950
951 fn path_lossy(&self) -> String {
953 String::from_utf8_lossy(&self.path_bytes()).to_string()
954 }
955
956 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
958 self._set_path(p.as_ref())
959 }
960
961 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
962 let bytes = path2bytes(path)?;
971 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
972 if bytes.len() <= maxnamelen {
973 copy_path_into(&mut self.name, path, false).map_err(|err| {
974 io::Error::new(
975 err.kind(),
976 format!("{} when setting path for {}", err, self.path_lossy()),
977 )
978 })?;
979 } else {
980 let mut prefix = path;
981 let mut prefixlen;
982 loop {
983 match prefix.parent() {
984 Some(parent) => prefix = parent,
985 None => {
986 return Err(other(&format!(
987 "path cannot be split to be inserted into archive: {}",
988 path.display()
989 )));
990 }
991 }
992 prefixlen = path2bytes(prefix)?.len();
993 if prefixlen <= maxprefixlen {
994 break;
995 }
996 }
997 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
998 io::Error::new(
999 err.kind(),
1000 format!("{} when setting path for {}", err, self.path_lossy()),
1001 )
1002 })?;
1003 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1004 copy_path_into(&mut self.name, &path, false).map_err(|err| {
1005 io::Error::new(
1006 err.kind(),
1007 format!("{} when setting path for {}", err, self.path_lossy()),
1008 )
1009 })?;
1010 }
1011 Ok(())
1012 }
1013
1014 pub fn username_bytes(&self) -> &[u8] {
1016 truncate(&self.uname)
1017 }
1018
1019 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1021 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1022 io::Error::new(
1023 err.kind(),
1024 format!("{} when setting username for {}", err, self.path_lossy()),
1025 )
1026 })
1027 }
1028
1029 pub fn groupname_bytes(&self) -> &[u8] {
1031 truncate(&self.gname)
1032 }
1033
1034 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1036 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1037 io::Error::new(
1038 err.kind(),
1039 format!("{} when setting groupname for {}", err, self.path_lossy()),
1040 )
1041 })
1042 }
1043
1044 pub fn device_major(&self) -> io::Result<u32> {
1046 octal_from(&self.dev_major)
1047 .map(|u| u as u32)
1048 .map_err(|err| {
1049 io::Error::new(
1050 err.kind(),
1051 format!(
1052 "{} when getting device_major for {}",
1053 err,
1054 self.path_lossy()
1055 ),
1056 )
1057 })
1058 }
1059
1060 pub fn set_device_major(&mut self, major: u32) {
1062 octal_into(&mut self.dev_major, major);
1063 }
1064
1065 pub fn device_minor(&self) -> io::Result<u32> {
1067 octal_from(&self.dev_minor)
1068 .map(|u| u as u32)
1069 .map_err(|err| {
1070 io::Error::new(
1071 err.kind(),
1072 format!(
1073 "{} when getting device_minor for {}",
1074 err,
1075 self.path_lossy()
1076 ),
1077 )
1078 })
1079 }
1080
1081 pub fn set_device_minor(&mut self, minor: u32) {
1083 octal_into(&mut self.dev_minor, minor);
1084 }
1085
1086 pub fn as_header(&self) -> &Header {
1088 unsafe { cast(self) }
1089 }
1090
1091 pub fn as_header_mut(&mut self) -> &mut Header {
1093 unsafe { cast_mut(self) }
1094 }
1095}
1096
1097impl fmt::Debug for UstarHeader {
1098 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1099 let mut f = f.debug_struct("UstarHeader");
1100 self.as_header().debug_fields(&mut f);
1101 f.finish()
1102 }
1103}
1104
1105impl GnuHeader {
1106 pub fn username_bytes(&self) -> &[u8] {
1108 truncate(&self.uname)
1109 }
1110
1111 fn fullname_lossy(&self) -> String {
1113 format!(
1114 "{}:{}",
1115 String::from_utf8_lossy(self.groupname_bytes()),
1116 String::from_utf8_lossy(self.username_bytes()),
1117 )
1118 }
1119
1120 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1122 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1123 io::Error::new(
1124 err.kind(),
1125 format!(
1126 "{} when setting username for {}",
1127 err,
1128 self.fullname_lossy()
1129 ),
1130 )
1131 })
1132 }
1133
1134 pub fn groupname_bytes(&self) -> &[u8] {
1136 truncate(&self.gname)
1137 }
1138
1139 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1141 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1142 io::Error::new(
1143 err.kind(),
1144 format!(
1145 "{} when setting groupname for {}",
1146 err,
1147 self.fullname_lossy()
1148 ),
1149 )
1150 })
1151 }
1152
1153 pub fn device_major(&self) -> io::Result<u32> {
1155 octal_from(&self.dev_major)
1156 .map(|u| u as u32)
1157 .map_err(|err| {
1158 io::Error::new(
1159 err.kind(),
1160 format!(
1161 "{} when getting device_major for {}",
1162 err,
1163 self.fullname_lossy()
1164 ),
1165 )
1166 })
1167 }
1168
1169 pub fn set_device_major(&mut self, major: u32) {
1171 octal_into(&mut self.dev_major, major);
1172 }
1173
1174 pub fn device_minor(&self) -> io::Result<u32> {
1176 octal_from(&self.dev_minor)
1177 .map(|u| u as u32)
1178 .map_err(|err| {
1179 io::Error::new(
1180 err.kind(),
1181 format!(
1182 "{} when getting device_minor for {}",
1183 err,
1184 self.fullname_lossy()
1185 ),
1186 )
1187 })
1188 }
1189
1190 pub fn set_device_minor(&mut self, minor: u32) {
1192 octal_into(&mut self.dev_minor, minor);
1193 }
1194
1195 pub fn atime(&self) -> io::Result<u64> {
1197 num_field_wrapper_from(&self.atime).map_err(|err| {
1198 io::Error::new(
1199 err.kind(),
1200 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1201 )
1202 })
1203 }
1204
1205 pub fn set_atime(&mut self, atime: u64) {
1210 num_field_wrapper_into(&mut self.atime, atime);
1211 }
1212
1213 pub fn ctime(&self) -> io::Result<u64> {
1215 num_field_wrapper_from(&self.ctime).map_err(|err| {
1216 io::Error::new(
1217 err.kind(),
1218 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1219 )
1220 })
1221 }
1222
1223 pub fn set_ctime(&mut self, ctime: u64) {
1228 num_field_wrapper_into(&mut self.ctime, ctime);
1229 }
1230
1231 pub fn real_size(&self) -> io::Result<u64> {
1236 octal_from(&self.realsize).map_err(|err| {
1237 io::Error::new(
1238 err.kind(),
1239 format!(
1240 "{} when getting real_size for {}",
1241 err,
1242 self.fullname_lossy()
1243 ),
1244 )
1245 })
1246 }
1247
1248 pub fn is_extended(&self) -> bool {
1254 self.isextended[0] == 1
1255 }
1256
1257 pub fn as_header(&self) -> &Header {
1259 unsafe { cast(self) }
1260 }
1261
1262 pub fn as_header_mut(&mut self) -> &mut Header {
1264 unsafe { cast_mut(self) }
1265 }
1266}
1267
1268impl fmt::Debug for GnuHeader {
1269 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1270 let mut f = f.debug_struct("GnuHeader");
1271 self.as_header().debug_fields(&mut f);
1272 if let Ok(atime) = self.atime() {
1273 f.field("atime", &atime);
1274 }
1275 if let Ok(ctime) = self.ctime() {
1276 f.field("ctime", &ctime);
1277 }
1278 f.field("is_extended", &self.is_extended())
1279 .field("sparse", &DebugSparseHeaders(&self.sparse))
1280 .finish()
1281 }
1282}
1283
1284struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1285
1286impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1287 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1288 let mut f = f.debug_list();
1289 for header in self.0 {
1290 if !header.is_empty() {
1291 f.entry(header);
1292 }
1293 }
1294 f.finish()
1295 }
1296}
1297
1298impl GnuSparseHeader {
1299 pub fn is_empty(&self) -> bool {
1301 self.offset[0] == 0 || self.numbytes[0] == 0
1302 }
1303
1304 pub fn offset(&self) -> io::Result<u64> {
1308 octal_from(&self.offset).map_err(|err| {
1309 io::Error::new(
1310 err.kind(),
1311 format!("{} when getting offset from sparse header", err),
1312 )
1313 })
1314 }
1315
1316 pub fn length(&self) -> io::Result<u64> {
1320 octal_from(&self.numbytes).map_err(|err| {
1321 io::Error::new(
1322 err.kind(),
1323 format!("{} when getting length from sparse header", err),
1324 )
1325 })
1326 }
1327}
1328
1329impl fmt::Debug for GnuSparseHeader {
1330 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1331 let mut f = f.debug_struct("GnuSparseHeader");
1332 if let Ok(offset) = self.offset() {
1333 f.field("offset", &offset);
1334 }
1335 if let Ok(length) = self.length() {
1336 f.field("length", &length);
1337 }
1338 f.finish()
1339 }
1340}
1341
1342impl GnuExtSparseHeader {
1343 pub fn new() -> GnuExtSparseHeader {
1345 unsafe { mem::zeroed() }
1346 }
1347
1348 pub fn as_bytes(&self) -> &[u8; 512] {
1350 debug_assert_eq!(mem::size_of_val(self), 512);
1351 unsafe { &*(self as *const GnuExtSparseHeader as *const [u8; 512]) }
1352 }
1353
1354 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1356 debug_assert_eq!(mem::size_of_val(self), 512);
1357 unsafe { &mut *(self as *mut GnuExtSparseHeader as *mut [u8; 512]) }
1358 }
1359
1360 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1365 &self.sparse
1366 }
1367
1368 pub fn is_extended(&self) -> bool {
1370 self.isextended[0] == 1
1371 }
1372}
1373
1374impl Default for GnuExtSparseHeader {
1375 fn default() -> Self {
1376 Self::new()
1377 }
1378}
1379
1380fn octal_from(slice: &[u8]) -> io::Result<u64> {
1381 let trun = truncate(slice);
1382 let num = match str::from_utf8(trun) {
1383 Ok(n) => n,
1384 Err(_) => {
1385 return Err(other(&format!(
1386 "numeric field did not have utf-8 text: {}",
1387 String::from_utf8_lossy(trun)
1388 )));
1389 }
1390 };
1391 match u64::from_str_radix(num.trim(), 8) {
1392 Ok(n) => Ok(n),
1393 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1394 }
1395}
1396
1397fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1398 let o = format!("{:o}", val);
1399 let value = o.bytes().rev().chain(repeat(b'0'));
1400 for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1401 *slot = value;
1402 }
1403}
1404
1405fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1408 if src >= 8_589_934_592 || (src >= 2_097_152 && dst.len() == 8) {
1409 numeric_extended_into(dst, src);
1410 } else {
1411 octal_into(dst, src);
1412 }
1413}
1414
1415fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1418 if src[0] & 0x80 == 0 {
1419 octal_from(src)
1420 } else {
1421 Ok(numeric_extended_from(src))
1422 }
1423}
1424
1425fn numeric_extended_into(dst: &mut [u8], src: u64) {
1430 let len: usize = dst.len();
1431 for (slot, val) in dst.iter_mut().zip(
1432 repeat(0)
1433 .take(len - 8) .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1435 ) {
1436 *slot = val;
1437 }
1438 dst[0] |= 0x80;
1439}
1440
1441fn numeric_extended_from(src: &[u8]) -> u64 {
1442 let mut dst: u64 = 0;
1443 let mut b_to_skip = 1;
1444 if src.len() == 8 {
1445 dst = (src[0] ^ 0x80) as u64;
1447 } else {
1448 b_to_skip = src.len() - 8;
1450 }
1451 for byte in src.iter().skip(b_to_skip) {
1452 dst <<= 8;
1453 dst |= *byte as u64;
1454 }
1455 dst
1456}
1457
1458fn truncate(slice: &[u8]) -> &[u8] {
1459 match slice.iter().position(|i| *i == 0) {
1460 Some(i) => &slice[..i],
1461 None => slice,
1462 }
1463}
1464
1465fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1468 if bytes.len() > slot.len() {
1469 Err(other("provided value is too long"))
1470 } else if bytes.iter().any(|b| *b == 0) {
1471 Err(other("provided value contains a nul byte"))
1472 } else {
1473 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1474 *slot = *val;
1475 }
1476 Ok(())
1477 }
1478}
1479
1480fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1489 let mut emitted = false;
1490 let mut needs_slash = false;
1491 for component in path.components() {
1492 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1493 match (component, is_link_name) {
1494 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1495 return Err(other("paths in archives must be relative"));
1496 }
1497 (Component::ParentDir, false) => {
1498 return Err(other("paths in archives must not have `..`"));
1499 }
1500 (Component::CurDir, false) if path.components().count() == 1 => {}
1502 (Component::CurDir, false) => continue,
1503 (Component::Normal(_), _) | (_, true) => {}
1504 };
1505 if needs_slash {
1506 copy(&mut slot, b"/")?;
1507 }
1508 if bytes.contains(&b'/') {
1509 if let Component::Normal(..) = component {
1510 return Err(other("path component in archive cannot contain `/`"));
1511 }
1512 }
1513 copy(&mut slot, &*bytes)?;
1514 if &*bytes != b"/" {
1515 needs_slash = true;
1516 }
1517 emitted = true;
1518 }
1519 if !emitted {
1520 return Err(other("paths in archives must have at least one component"));
1521 }
1522 if ends_with_slash(path) {
1523 copy(&mut slot, &[b'/'])?;
1524 }
1525 return Ok(());
1526
1527 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1528 copy_into(*slot, bytes)?;
1529 let tmp = std::mem::take(slot);
1530 *slot = &mut tmp[bytes.len()..];
1531 Ok(())
1532 }
1533}
1534
1535#[cfg(target_arch = "wasm32")]
1536fn ends_with_slash(p: &Path) -> bool {
1537 p.to_string_lossy().ends_with('/')
1538}
1539
1540#[cfg(windows)]
1541fn ends_with_slash(p: &Path) -> bool {
1542 let last = p.as_os_str().encode_wide().last();
1543 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1544}
1545
1546#[cfg(any(unix, target_os = "redox"))]
1547fn ends_with_slash(p: &Path) -> bool {
1548 p.as_os_str().as_bytes().ends_with(&[b'/'])
1549}
1550
1551#[cfg(any(windows, target_arch = "wasm32"))]
1552pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1553 p.as_os_str()
1554 .to_str()
1555 .map(|s| s.as_bytes())
1556 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1557 .map(|bytes| {
1558 if bytes.contains(&b'\\') {
1559 let mut bytes = bytes.to_owned();
1561 for b in &mut bytes {
1562 if *b == b'\\' {
1563 *b = b'/';
1564 }
1565 }
1566 Cow::Owned(bytes)
1567 } else {
1568 Cow::Borrowed(bytes)
1569 }
1570 })
1571}
1572
1573#[cfg(any(unix, target_os = "redox"))]
1574pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1576 Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1577}
1578
1579#[cfg(windows)]
1580pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1583 return match bytes {
1584 Cow::Borrowed(bytes) => {
1585 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1586 Ok(Cow::Borrowed(Path::new(s)))
1587 }
1588 Cow::Owned(bytes) => {
1589 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1590 Ok(Cow::Owned(PathBuf::from(s)))
1591 }
1592 };
1593
1594 fn not_unicode(v: &[u8]) -> io::Error {
1595 other(&format!(
1596 "only Unicode paths are supported on Windows: {}",
1597 String::from_utf8_lossy(v)
1598 ))
1599 }
1600}
1601
1602#[cfg(any(unix, target_os = "redox"))]
1603pub fn bytes2path(bytes: Cow<'_, [u8]>) -> io::Result<Cow<'_, Path>> {
1605 use std::ffi::{OsStr, OsString};
1606
1607 Ok(match bytes {
1608 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1609 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1610 })
1611}
1612
1613#[cfg(target_arch = "wasm32")]
1614pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1615 Ok(match bytes {
1616 Cow::Borrowed(bytes) => {
1617 Cow::Borrowed(Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?))
1618 }
1619 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(
1620 String::from_utf8(bytes).map_err(invalid_utf8)?,
1621 )),
1622 })
1623}
1624
1625#[cfg(target_arch = "wasm32")]
1626fn invalid_utf8<T>(_: T) -> io::Error {
1627 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1628}