1#[cfg(unix)]
2use std::os::unix::prelude::*;
3#[cfg(windows)]
4use std::os::windows::prelude::*;
5
6use std::borrow::Cow;
7use std::fmt;
8use std::fs;
9use std::io;
10use std::iter;
11use std::iter::repeat;
12use std::mem;
13use std::path::{Component, Path, PathBuf};
14use std::str;
15
16use crate::other;
17use crate::EntryType;
18
19#[repr(C)]
21#[allow(missing_docs)]
22pub struct Header {
23 bytes: [u8; 512],
24}
25
26#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29#[non_exhaustive]
30pub enum HeaderMode {
31 Complete,
34
35 Deterministic,
38}
39
40#[repr(C)]
42#[allow(missing_docs)]
43pub struct OldHeader {
44 pub name: [u8; 100],
45 pub mode: [u8; 8],
46 pub uid: [u8; 8],
47 pub gid: [u8; 8],
48 pub size: [u8; 12],
49 pub mtime: [u8; 12],
50 pub cksum: [u8; 8],
51 pub linkflag: [u8; 1],
52 pub linkname: [u8; 100],
53 pub pad: [u8; 255],
54}
55
56#[repr(C)]
58#[allow(missing_docs)]
59pub struct UstarHeader {
60 pub name: [u8; 100],
61 pub mode: [u8; 8],
62 pub uid: [u8; 8],
63 pub gid: [u8; 8],
64 pub size: [u8; 12],
65 pub mtime: [u8; 12],
66 pub cksum: [u8; 8],
67 pub typeflag: [u8; 1],
68 pub linkname: [u8; 100],
69
70 pub magic: [u8; 6],
72 pub version: [u8; 2],
73 pub uname: [u8; 32],
74 pub gname: [u8; 32],
75 pub dev_major: [u8; 8],
76 pub dev_minor: [u8; 8],
77 pub prefix: [u8; 155],
78 pub pad: [u8; 12],
79}
80
81#[repr(C)]
83#[allow(missing_docs)]
84pub struct GnuHeader {
85 pub name: [u8; 100],
86 pub mode: [u8; 8],
87 pub uid: [u8; 8],
88 pub gid: [u8; 8],
89 pub size: [u8; 12],
90 pub mtime: [u8; 12],
91 pub cksum: [u8; 8],
92 pub typeflag: [u8; 1],
93 pub linkname: [u8; 100],
94
95 pub magic: [u8; 6],
97 pub version: [u8; 2],
98 pub uname: [u8; 32],
99 pub gname: [u8; 32],
100 pub dev_major: [u8; 8],
101 pub dev_minor: [u8; 8],
102 pub atime: [u8; 12],
103 pub ctime: [u8; 12],
104 pub offset: [u8; 12],
105 pub longnames: [u8; 4],
106 pub unused: [u8; 1],
107 pub sparse: [GnuSparseHeader; 4],
108 pub isextended: [u8; 1],
109 pub realsize: [u8; 12],
110 pub pad: [u8; 17],
111}
112
113#[repr(C)]
117#[allow(missing_docs)]
118pub struct GnuSparseHeader {
119 pub offset: [u8; 12],
120 pub numbytes: [u8; 12],
121}
122
123#[repr(C)]
128#[allow(missing_docs)]
129pub struct GnuExtSparseHeader {
130 pub sparse: [GnuSparseHeader; 21],
131 pub isextended: [u8; 1],
132 pub padding: [u8; 7],
133}
134
135impl Header {
136 pub fn new_gnu() -> Header {
142 let mut header = Header { bytes: [0; 512] };
143 unsafe {
144 let gnu = cast_mut::<_, GnuHeader>(&mut header);
145 gnu.magic = *b"ustar ";
146 gnu.version = *b" \0";
147 }
148 header.set_mtime(0);
149 header
150 }
151
152 pub fn new_ustar() -> Header {
160 let mut header = Header { bytes: [0; 512] };
161 unsafe {
162 let gnu = cast_mut::<_, UstarHeader>(&mut header);
163 gnu.magic = *b"ustar\0";
164 gnu.version = *b"00";
165 }
166 header.set_mtime(0);
167 header
168 }
169
170 pub fn new_old() -> Header {
177 let mut header = Header { bytes: [0; 512] };
178 header.set_mtime(0);
179 header
180 }
181
182 fn is_ustar(&self) -> bool {
183 let ustar = unsafe { cast::<_, UstarHeader>(self) };
184 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
185 }
186
187 fn is_gnu(&self) -> bool {
188 let ustar = unsafe { cast::<_, UstarHeader>(self) };
189 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
190 }
191
192 pub fn as_old(&self) -> &OldHeader {
197 unsafe { cast(self) }
198 }
199
200 pub fn as_old_mut(&mut self) -> &mut OldHeader {
202 unsafe { cast_mut(self) }
203 }
204
205 pub fn as_ustar(&self) -> Option<&UstarHeader> {
215 if self.is_ustar() {
216 Some(unsafe { cast(self) })
217 } else {
218 None
219 }
220 }
221
222 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
224 if self.is_ustar() {
225 Some(unsafe { cast_mut(self) })
226 } else {
227 None
228 }
229 }
230
231 pub fn as_gnu(&self) -> Option<&GnuHeader> {
241 if self.is_gnu() {
242 Some(unsafe { cast(self) })
243 } else {
244 None
245 }
246 }
247
248 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
250 if self.is_gnu() {
251 Some(unsafe { cast_mut(self) })
252 } else {
253 None
254 }
255 }
256
257 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
261 assert_eq!(bytes.len(), mem::size_of::<Header>());
262 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
263 unsafe { &*(bytes.as_ptr() as *const Header) }
264 }
265
266 pub fn as_bytes(&self) -> &[u8; 512] {
268 &self.bytes
269 }
270
271 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
273 &mut self.bytes
274 }
275
276 pub fn set_metadata(&mut self, meta: &fs::Metadata) {
283 self.fill_from(meta, HeaderMode::Complete);
284 }
285
286 pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
289 self.fill_from(meta, mode);
290 }
291
292 pub fn entry_size(&self) -> io::Result<u64> {
301 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
302 io::Error::new(
303 err.kind(),
304 format!("{} when getting size for {}", err, self.path_lossy()),
305 )
306 })
307 }
308
309 pub fn size(&self) -> io::Result<u64> {
313 if self.entry_type().is_gnu_sparse() {
314 self.as_gnu()
315 .ok_or_else(|| other("sparse header was not a gnu header"))
316 .and_then(|h| h.real_size())
317 } else {
318 self.entry_size()
319 }
320 }
321
322 pub fn set_size(&mut self, size: u64) {
324 num_field_wrapper_into(&mut self.as_old_mut().size, size);
325 }
326
327 pub fn path(&self) -> io::Result<Cow<Path>> {
335 bytes2path(self.path_bytes())
336 }
337
338 pub fn path_bytes(&self) -> Cow<[u8]> {
346 if let Some(ustar) = self.as_ustar() {
347 ustar.path_bytes()
348 } else {
349 let name = truncate(&self.as_old().name);
350 Cow::Borrowed(name)
351 }
352 }
353
354 fn path_lossy(&self) -> String {
356 String::from_utf8_lossy(&self.path_bytes()).to_string()
357 }
358
359 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
371 self._set_path(p.as_ref())
372 }
373
374 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
375 if let Some(ustar) = self.as_ustar_mut() {
376 return ustar.set_path(path);
377 }
378 copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
379 io::Error::new(
380 err.kind(),
381 format!("{} when setting path for {}", err, self.path_lossy()),
382 )
383 })
384 }
385
386 pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
395 match self.link_name_bytes() {
396 Some(bytes) => bytes2path(bytes).map(Some),
397 None => Ok(None),
398 }
399 }
400
401 pub fn link_name_bytes(&self) -> Option<Cow<[u8]>> {
409 let old = self.as_old();
410 if old.linkname[0] != 0 {
411 Some(Cow::Borrowed(truncate(&old.linkname)))
412 } else {
413 None
414 }
415 }
416
417 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
424 self._set_link_name(p.as_ref())
425 }
426
427 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
428 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
429 io::Error::new(
430 err.kind(),
431 format!("{} when setting link name for {}", err, self.path_lossy()),
432 )
433 })
434 }
435
436 pub fn mode(&self) -> io::Result<u32> {
440 octal_from(&self.as_old().mode)
441 .map(|u| u as u32)
442 .map_err(|err| {
443 io::Error::new(
444 err.kind(),
445 format!("{} when getting mode for {}", err, self.path_lossy()),
446 )
447 })
448 }
449
450 pub fn set_mode(&mut self, mode: u32) {
452 octal_into(&mut self.as_old_mut().mode, mode);
453 }
454
455 pub fn uid(&self) -> io::Result<u64> {
459 num_field_wrapper_from(&self.as_old().uid)
460 .map(|u| u as u64)
461 .map_err(|err| {
462 io::Error::new(
463 err.kind(),
464 format!("{} when getting uid for {}", err, self.path_lossy()),
465 )
466 })
467 }
468
469 pub fn set_uid(&mut self, uid: u64) {
471 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
472 }
473
474 pub fn gid(&self) -> io::Result<u64> {
476 num_field_wrapper_from(&self.as_old().gid)
477 .map(|u| u as u64)
478 .map_err(|err| {
479 io::Error::new(
480 err.kind(),
481 format!("{} when getting gid for {}", err, self.path_lossy()),
482 )
483 })
484 }
485
486 pub fn set_gid(&mut self, gid: u64) {
488 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
489 }
490
491 pub fn mtime(&self) -> io::Result<u64> {
493 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
494 io::Error::new(
495 err.kind(),
496 format!("{} when getting mtime for {}", err, self.path_lossy()),
497 )
498 })
499 }
500
501 pub fn set_mtime(&mut self, mtime: u64) {
506 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
507 }
508
509 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
516 match self.username_bytes() {
517 Some(bytes) => str::from_utf8(bytes).map(Some),
518 None => Ok(None),
519 }
520 }
521
522 pub fn username_bytes(&self) -> Option<&[u8]> {
527 if let Some(ustar) = self.as_ustar() {
528 Some(ustar.username_bytes())
529 } else if let Some(gnu) = self.as_gnu() {
530 Some(gnu.username_bytes())
531 } else {
532 None
533 }
534 }
535
536 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
541 if let Some(ustar) = self.as_ustar_mut() {
542 return ustar.set_username(name);
543 }
544 if let Some(gnu) = self.as_gnu_mut() {
545 gnu.set_username(name)
546 } else {
547 Err(other("not a ustar or gnu archive, cannot set username"))
548 }
549 }
550
551 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
558 match self.groupname_bytes() {
559 Some(bytes) => str::from_utf8(bytes).map(Some),
560 None => Ok(None),
561 }
562 }
563
564 pub fn groupname_bytes(&self) -> Option<&[u8]> {
569 if let Some(ustar) = self.as_ustar() {
570 Some(ustar.groupname_bytes())
571 } else if let Some(gnu) = self.as_gnu() {
572 Some(gnu.groupname_bytes())
573 } else {
574 None
575 }
576 }
577
578 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
583 if let Some(ustar) = self.as_ustar_mut() {
584 return ustar.set_groupname(name);
585 }
586 if let Some(gnu) = self.as_gnu_mut() {
587 gnu.set_groupname(name)
588 } else {
589 Err(other("not a ustar or gnu archive, cannot set groupname"))
590 }
591 }
592
593 pub fn device_major(&self) -> io::Result<Option<u32>> {
601 if let Some(ustar) = self.as_ustar() {
602 ustar.device_major().map(Some)
603 } else if let Some(gnu) = self.as_gnu() {
604 gnu.device_major().map(Some)
605 } else {
606 Ok(None)
607 }
608 }
609
610 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
615 if let Some(ustar) = self.as_ustar_mut() {
616 ustar.set_device_major(major);
617 Ok(())
618 } else if let Some(gnu) = self.as_gnu_mut() {
619 gnu.set_device_major(major);
620 Ok(())
621 } else {
622 Err(other("not a ustar or gnu archive, cannot set dev_major"))
623 }
624 }
625
626 pub fn device_minor(&self) -> io::Result<Option<u32>> {
634 if let Some(ustar) = self.as_ustar() {
635 ustar.device_minor().map(Some)
636 } else if let Some(gnu) = self.as_gnu() {
637 gnu.device_minor().map(Some)
638 } else {
639 Ok(None)
640 }
641 }
642
643 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
648 if let Some(ustar) = self.as_ustar_mut() {
649 ustar.set_device_minor(minor);
650 Ok(())
651 } else if let Some(gnu) = self.as_gnu_mut() {
652 gnu.set_device_minor(minor);
653 Ok(())
654 } else {
655 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
656 }
657 }
658
659 pub fn entry_type(&self) -> EntryType {
661 EntryType::new(self.as_old().linkflag[0])
662 }
663
664 pub fn set_entry_type(&mut self, ty: EntryType) {
666 self.as_old_mut().linkflag = [ty.as_byte()];
667 }
668
669 pub fn cksum(&self) -> io::Result<u32> {
673 octal_from(&self.as_old().cksum)
674 .map(|u| u as u32)
675 .map_err(|err| {
676 io::Error::new(
677 err.kind(),
678 format!("{} when getting cksum for {}", err, self.path_lossy()),
679 )
680 })
681 }
682
683 pub fn set_cksum(&mut self) {
686 let cksum = self.calculate_cksum();
687 octal_into(&mut self.as_old_mut().cksum, cksum);
688 }
689
690 fn calculate_cksum(&self) -> u32 {
691 let old = self.as_old();
692 let start = old as *const _ as usize;
693 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
694 let offset = cksum_start - start;
695 let len = old.cksum.len();
696 self.bytes[0..offset]
697 .iter()
698 .chain(iter::repeat(&b' ').take(len))
699 .chain(&self.bytes[offset + len..])
700 .fold(0, |a, b| a + (*b as u32))
701 }
702
703 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
704 self.fill_platform_from(meta, mode);
705 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
707 0
708 } else {
709 meta.len()
710 });
711 if let Some(ustar) = self.as_ustar_mut() {
712 ustar.set_device_major(0);
713 ustar.set_device_minor(0);
714 }
715 if let Some(gnu) = self.as_gnu_mut() {
716 gnu.set_device_major(0);
717 gnu.set_device_minor(0);
718 }
719 }
720
721 #[cfg(target_arch = "wasm32")]
722 #[allow(unused_variables)]
723 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
724 unimplemented!();
725 }
726
727 #[cfg(unix)]
728 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
729 match mode {
730 HeaderMode::Complete => {
731 self.set_mtime(meta.mtime() as u64);
732 self.set_uid(meta.uid() as u64);
733 self.set_gid(meta.gid() as u64);
734 self.set_mode(meta.mode() as u32);
735 }
736 HeaderMode::Deterministic => {
737 self.set_mtime(1153704088);
747
748 self.set_uid(0);
749 self.set_gid(0);
750
751 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
753 0o755
754 } else {
755 0o644
756 };
757 self.set_mode(fs_mode);
758 }
759 }
760
761 self.set_entry_type(entry_type(meta.mode()));
772
773 fn entry_type(mode: u32) -> EntryType {
774 match mode as libc::mode_t & libc::S_IFMT {
775 libc::S_IFREG => EntryType::file(),
776 libc::S_IFLNK => EntryType::symlink(),
777 libc::S_IFCHR => EntryType::character_special(),
778 libc::S_IFBLK => EntryType::block_special(),
779 libc::S_IFDIR => EntryType::dir(),
780 libc::S_IFIFO => EntryType::fifo(),
781 _ => EntryType::new(b' '),
782 }
783 }
784 }
785
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(123456789); 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 { mem::transmute(self) }
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 { mem::transmute(self) }
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 >= 8589934592 || (src >= 2097152 && 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 Ok(numeric_extended_from(src))
1420 } else {
1421 octal_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 = mem::replace(slot, &mut []);
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(unix)]
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(unix)]
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(unix)]
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}
1629
1630pub fn canonicalize(path: &Path) -> PathBuf {
1631 canonicalize_or_err(path).unwrap_or(path.to_path_buf())
1632}
1633
1634pub fn canonicalize_or_err(path: &Path) -> io::Result<PathBuf> {
1635 if cfg!(target_os = "wasi") {
1636 return Ok(path.to_path_buf());
1639 } else {
1640 Ok(path.canonicalize().map_err(|err| {
1641 io::Error::new(
1642 err.kind(),
1643 format!("{} while canonicalizing {}", err, path.display()),
1644 )
1645 })?)
1646 }
1647}